Subclip Logo

API Reference

AI Video Generation API

Generate and edit video from text prompts and optional image, video, or audio references.

API generations are separate from dashboard history. An awaiting_upload draft is usable for 15 minutes, until uploadExpiresAt. A started job remains accessible until resultExpiresAt, normally one hour after it completes, fails, or finishes cancellation. Access stops at that deadline, and a central cleanup sweep deletes the record and media shortly afterward.

For current credit costs, see API credit costs.

OpenAPI-style reference

API endpoints

Generate or edit video from text prompts and optional image, video, or audio references.

API v1 storage handling: files uploaded or generated through /api/v1 are auto-cleaned and do not count toward the user's storage quota.
GET/api/v1/ai-video-generation/models

List AI video models

Returns the public model IDs, supported inputs, aspect ratios, exact model-specific output tiers, and duration policies. Resolution aliases are not accepted.

Bearer auth

Parameters

No parameters.

Examples

Response

{
  "models": [
    {
      "id": "kling-v3",
      "name": "Kling 3",
      "acceptedAssets": [
        "image"
      ],
      "supportsPublicUrlReferences": true,
      "publicUrlRequirements": {
        "protocol": "https",
        "publiclyDownloadable": true,
        "redirectsValidated": true
      },
      "aspectRatios": [
        "16:9",
        "9:16",
        "1:1"
      ],
      "resolutions": [
        "720p",
        "1080p"
      ],
      "resolutionPolicy": {
        "semantics": "resolution_tier",
        "values": [
          "720p",
          "1080p"
        ],
        "exactValueRequired": true,
        "aliasesAccepted": false
      },
      "durationMode": "fixed",
      "durationPolicy": {
        "mode": "fixed",
        "valuesSeconds": [
          5,
          10,
          15
        ],
        "acceptsThreeSecondRequest": false
      },
      "durations": [
        5,
        10,
        15
      ],
      "supportsGeneratedAudio": true,
      "creditsPerSecond": 6
    }
  ]
}

Responses

StatusDescription
200Model catalog returned
401Missing, invalid, revoked, or unauthorized API key
429Rate limit exceeded; wait for the Retry-After interval before retrying
500Unexpected request-processing error
POST/api/v1/ai-video-generation/uploads

Create reference upload URLs

Creates an awaiting_upload draft and signed upload URLs for supported multimodal references. PUT every file to its returned URL before starting the job. The draft and its upload URLs are usable until uploadExpiresAt, normally 15 minutes after creation. After that deadline, attempts to replay or start the retained draft return 410 until the periodic central cleanup sweep removes it shortly afterward; generationId lookups then return 404. Skip this endpoint for text-only generation.

Bearer auth

Parameters

FieldTypeRequiredDetails
Idempotency-KeystringYes
Required stable key for safely retrying upload-draft creation; reuse the same key when starting this draft
header
modelIdstringYes
Public model ID returned by GET /models
body
promptstringYes
Text instructions for the video; generation settings are saved with the upload draft
body
assetsarrayYes
One or more supported reference assets for the selected model
body
assets[].idstringYes
Client-defined stable asset ID used to reference the upload
body
assets[].kindimage | video | audioYes
Reference media type
body
assets[].fileNamestringYes
Original file name
body
assets[].contentTypestringYes
Allowed MIME type for the selected asset kind
body
assets[].fileSizenumberYes
Exact file size in bytes
body
assets[].tagstringNo
Optional prompt tag such as @reference_1; a stable tag is generated when omitted
body
assets[].rolereference | start_frame | end_frame | audio_reference | source_video | styleNo
Optional semantic role. Use a role supported by the selected model
body
assets[].durationSecondsnumberNo
Required for source video and for model inputs with duration limits
body
assets[].widthnumberNo
Optional declared media width in pixels
body
assets[].heightnumberNo
Optional declared media height in pixels
body

Examples

Request

{
  "modelId": "kling-v3",
  "prompt": "Use the reference as the opening frame, then move the camera forward slowly",
  "aspectRatio": "16:9",
  "resolution": "720p",
  "durationSeconds": 5,
  "generateAudio": false,
  "assets": [
    {
      "id": "reference-1",
      "kind": "image",
      "fileName": "reference.jpg",
      "contentType": "image/jpeg",
      "fileSize": 2483921
    }
  ]
}

Response

{
  "generationId": "aiapi_...",
  "status": "awaiting_upload",
  "estimatedCredits": 30,
  "uploadExpiresIn": 900,
  "uploadExpiresAt": "2026-07-21T11:45:00.000Z",
  "uploads": [
    {
      "id": "reference-1",
      "kind": "image",
      "method": "PUT",
      "contentType": "image/jpeg",
      "declaredFileSize": 2483921,
      "uploadUrl": "https://signed-upload-url..."
    }
  ],
  "startUrl": "/api/v1/ai-video-generation/jobs"
}

Responses

StatusDescription
200Existing upload draft returned for an idempotent replay
201Upload draft and signed upload URLs created
400Invalid request body, asset declaration, or unsupported model setting
401Missing, invalid, revoked, or unauthorized API key
402Insufficient credits; no draft is created
409Idempotency-Key was already used with a different upload request, or the matching draft has already started
410The matching upload draft passed uploadExpiresAt but has not yet been removed by the central cleanup sweep; after removal the same generationId returns 404
413A declared reference asset exceeds the media-type size limit
429Rate limit exceeded; wait for the Retry-After interval before retrying
500Unexpected request-processing or upload-URL creation error
POST/api/v1/ai-video-generation/jobs

Start an AI video job

Starts a text-only or multimodal job. Send direct public HTTPS references in assets, or send the generationId returned by POST /uploads after every signed upload finishes successfully. The upload-to-start flow is the one idempotency exception: POST /jobs must reuse the upload draft's Idempotency-Key even though its body is only { generationId }; all other reuse with a different body returns 409. No artificial wait is required after the final successful PUT. Subclip validates each signed upload and saves an immutable job input before generation starts. A retryable upload_missing (400), upload_verification_failed (502), or upload_lock_failed (502) response includes Retry-After; retry the identical generationId request with exponential backoff and jitter, capped at 15 seconds, until uploadExpiresAt. Create a new draft only after 410 upload_expired. Public HTTPS references are fetched and validated asynchronously after a job is accepted, so an unreachable, private, changed, oversized, or invalid media URL can later produce status failed with error.code INPUT_INVALID. Credits are verified before the job is queued. durationSeconds is the native generation duration, not a trim target. The API does not trim outputs; use the selected model's live durationPolicy.

Bearer auth

Parameters

FieldTypeRequiredDetails
Idempotency-KeystringYes
Required. Reusing it with the same body returns the original job; a different body returns 409. Exception: when starting a POST /uploads draft, reuse that draft's key with the { generationId } body
header
generationIdstringNo
For a multimodal job, send this as the only body field after every PUT succeeds; retry the same value and Idempotency-Key when a retryable finalization error is returned
body
modelIdstringNo
Required unless generationId is provided
body
promptstringNo
Required unless generationId is provided
body
assetsarrayNo
Optional direct public HTTPS reference assets. Do not combine this with generationId
body
assets[].urlstringNo
Direct publicly downloadable HTTPS media URL. Fetching and media validation are asynchronous; a URL that is unreachable, private, changed, oversized, or invalid can later end the accepted job as failed with error.code INPUT_INVALID
body
assets[].kindimage | video | audioNo
Reference media type supported by the selected model
body
assets[].rolereference | start_frame | end_frame | audio_reference | source_video | styleNo
Optional semantic role supported by the selected model
body
assets[].tagstringNo
Optional prompt tag such as @image_1; generated when omitted
body
assets[].fileNamestringNo
Optional display file name; inferred from the URL path when omitted
body
assets[].contentTypestringNo
Optional expected MIME type; actual downloaded media is independently verified
body
assets[].durationSecondsnumberNo
Optional declared duration; required by models that require source-duration metadata
body
aspectRatiostringNo
One exact value from the selected model's aspectRatios returned by GET /models
body
resolutionstringNo
Exact, case-sensitive model-specific output tier from GET /models resolutionPolicy.values. Aliases are not accepted
body
durationSecondsnumberNo
Native generation duration. For fixed mode, one value from durationPolicy.valuesSeconds; for source_asset mode, the verified source duration within its range. This API does not trim output
body
generateAudiobooleanNo
Generate native audio when the selected model supports it
bodydefault: false

Examples

Request

{
  "modelId": "kling-v3",
  "prompt": "Use @image_1 as the opening frame, then move the camera forward slowly",
  "aspectRatio": "16:9",
  "resolution": "720p",
  "durationSeconds": 5,
  "generateAudio": false,
  "assets": [
    {
      "kind": "image",
      "role": "reference",
      "url": "https://cdn.yoursite.com/reference.jpg"
    }
  ]
}

Response

{
  "generationId": "aiapi_...",
  "status": "queued",
  "estimatedCredits": 30,
  "statusUrl": "/api/v1/ai-video-generation/jobs/aiapi_...",
  "downloadUrl": "/api/v1/ai-video-generation/jobs/aiapi_.../download",
  "resultExpiresAt": null
}

Responses

StatusDescription
200Existing job returned for an idempotent replay
202Generation accepted and queued. Public-URL media is fetched and validated asynchronously; poll status for a possible terminal INPUT_INVALID error
400Invalid request or unsupported model setting; upload_missing for a signed-upload draft is retryable after Retry-After
401Missing, invalid, revoked, or unauthorized API key
402Insufficient credits; no generation job is queued
404The supplied generationId was not found or is not accessible with this API key
409Idempotency conflict or the requested draft cannot be started in its current state
410The upload or retained-result deadline passed but the record has not yet been removed by the central cleanup sweep; after removal the generationId returns 404
413A declared or verified signed-upload asset exceeds the media-type size limit
429Rate limit exceeded; wait for the Retry-After interval before retrying
500Unexpected request-processing error
502upload_verification_failed or upload_lock_failed while starting a signed-upload draft; wait for Retry-After, then retry the same generationId, body, and Idempotency-Key
503Generation queueing is temporarily unavailable; retry the identical request with the same Idempotency-Key
GET/api/v1/ai-video-generation/jobs/{generationId}

Get AI video job status

Returns status, progress, credit usage, output metadata, and the access deadline. The public state vocabulary is: awaiting_upload for an unstarted upload draft; queued or processing for an active job; and completed, failed, or canceled for a terminal outcome. Poll the same generationId while it is queued or processing. cancellationPending can be true only after an explicit POST /cancel request; if it is true, keep polling until it becomes false. A public HTTPS reference that fails asynchronous download or media validation ends as failed with error.code INPUT_INVALID. Terminal jobs remain accessible until resultExpiresAt, normally one hour after the final outcome. After that deadline, this endpoint returns 410 until the periodic central cleanup sweep removes the record shortly afterward, then returns 404.

Bearer auth

Parameters

FieldTypeRequiredDetails
generationIdstringYes
Generation ID returned by POST /uploads or POST /jobs
path

Examples

Response

{
  "generationId": "aiapi_...",
  "status": "completed",
  "progress": 100,
  "modelId": "kling-v3",
  "estimatedCredits": 30,
  "creditsUsed": 30,
  "outputReady": true,
  "outputCount": 1,
  "cancellationPending": false,
  "error": null,
  "resultExpiresAt": "2026-07-21T12:30:00.000Z"
}

Responses

StatusDescription
200Job status returned
401Missing, invalid, revoked, or unauthorized API key
404Generation was not found, has been removed by the central cleanup sweep, or is not accessible with this API key
410resultExpiresAt passed, but the periodic central cleanup sweep has not removed the record yet
429Rate limit exceeded; wait for the Retry-After interval before polling again
500Unexpected request-processing error
GET/api/v1/ai-video-generation/jobs/{generationId}/download

Create AI video download URLs

Returns signed URLs only after status is completed and outputReady is true. The downloads array can contain multiple files; iterate over every downloads[] entry instead of assuming one output. Each URL is valid only until the earlier of its expiresAt and the job's resultExpiresAt. resultExpiresAt is normally one hour after the final outcome. After that deadline, this endpoint returns 410 until the periodic central cleanup sweep removes the job and media shortly afterward, then returns 404.

Bearer auth

Parameters

FieldTypeRequiredDetails
generationIdstringYes
Completed generation ID
path

Examples

Response

{
  "generationId": "aiapi_...",
  "downloads": [
    {
      "id": "output-1",
      "index": 0,
      "downloadUrl": "https://signed-download-url...",
      "fileName": "generation.mp4",
      "contentType": "video/mp4",
      "fileSize": 18345678
    }
  ],
  "expiresIn": 1800,
  "expiresAt": "2026-07-21T12:00:00.000Z",
  "resultExpiresAt": "2026-07-21T12:30:00.000Z"
}

Responses

StatusDescription
200Download URLs created for every output
401Missing, invalid, revoked, or unauthorized API key
404Generation was not found, has been removed by the central cleanup sweep, or is not accessible with this API key
409The job is not completed with outputReady true
410resultExpiresAt passed, but the periodic central cleanup sweep has not removed the job and media yet
429Rate limit exceeded; wait for the Retry-After interval before retrying
500Unexpected request-processing or signed-URL creation error
POST/api/v1/ai-video-generation/jobs/{generationId}/cancel

Cancel an AI video job

Requests explicit user cancellation for an awaiting_upload draft or a queued or processing job. Use this endpoint only when the user chose to cancel, not because progress is slow. cancellationPending is exposed only for this explicit-cancel flow; while it is true, resultExpiresAt is null and the client must keep polling the same generationId. When cancellationPending becomes false, resultExpiresAt starts the normal one-hour access window. After that deadline, requests return 410 until the periodic central cleanup sweep removes the job and media shortly afterward, then return 404.

Bearer auth

Parameters

FieldTypeRequiredDetails
generationIdstringYes
awaiting_upload, queued, or processing generation ID
path

Examples

Response

{
  "generationId": "aiapi_...",
  "status": "canceled",
  "cancellationPending": true,
  "resultExpiresAt": null
}

Responses

StatusDescription
200Cancellation state returned; cancellationPending can be true only for this explicit cancel request
401Missing, invalid, revoked, or unauthorized API key
404Generation was not found, has been removed by the central cleanup sweep, or is not accessible with this API key
409The job can no longer be canceled in its current state
410resultExpiresAt passed, but the periodic central cleanup sweep has not removed the record yet
429Rate limit exceeded; wait for the Retry-After interval before retrying
500Unexpected request-processing error

Available models

Call /api/v1/ai-video-generation/models for the current machine-readable limits. Only public model IDs and capabilities are returned.

resolution is a model-specific output tier. Send an exact, case-sensitive value from resolutionPolicy.values (also returned in resolutions); aliases are not accepted. GPT Image 2 uses quality tiers such as Low, Medium, and High, while other models may use resolution tiers such as 1K, 2K, or 4K. These values are not interchangeable.

durationSeconds is the model's native duration, not a final trim target. Read durationPolicy from this endpoint: fixed-duration models require one listed value; Gemini Omni Flash is edit-only and must match a verified 3–10 second source video. No configured text-to-video or image-to-video model currently accepts a native 3-second request. For an exact 3.000-second deliverable, trim a supported generated duration after download. This API does not trim outputs, and its credit estimate uses the native generated duration.

Model IDNameInputsAspect ratiosOutput tiersNative duration
gemini-omni-flashGemini Omni Flashtext, videosourcesourceSource video, 3–10s (must match source)
kling-v3Kling 3text, image16:9, 9:16, 1:1720p, 1080p5s, 10s, 15s
veo-3-1Veo 3.1text, image16:9, 9:16720p, 1080p4s, 6s, 8s
runway-gen-4-5Runway Gen-4.5text, image16:9, 9:16, 1:1720p5s, 10s
seedance-2Seedance 2text, image, video, audioauto, 16:9, 9:16, 1:1, 4:3, 3:4720p, 1080p5s, 10s, 15s
pika-2-2Pika 2.2text, image16:9, 9:16, 1:1, 4:5, 5:4, 3:2, 2:3720p, 1080p5s, 10s
luma-ray-2Luma Ray 2text, image16:9, 9:16, 1:1720p5s, 9s
hailuo-2-3Hailuo 2.3text, image16:9, 9:16768p, 1080p6s, 10s
pixverse-v6PixVerse V6text, image16:9, 9:16, 1:1, 4:3, 3:4720p, 1080p5s, 10s, 15s
wan-2-7Wan 2.7text, audio16:9, 9:16, 1:1, 4:3, 3:4720p, 1080p5s, 10s, 15s
ltx-2-3-proLTX 2.3 Protext, image, video, audio16:9, 9:161080p, 1440p, 2160p6s, 8s, 10s

Text-only generation

Send the prompt and model settings directly to POST /jobs. Idempotency-Key is required: repeating the same request returns the original job, while changing the body for that key returns 409. The only cross-body exception is the signed-upload transition described below: start that same draft with its original key and a body containing its generationId.

curl -X POST https://www.subclip.app/api/v1/ai-video-generation/jobs \
  -H "Authorization: Bearer $SUBCLIP_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: video-request-001" \
  -d '{
  "modelId": "kling-v3",
  "prompt": "A cinematic tracking shot of a sailboat crossing calm water at sunrise",
  "aspectRatio": "16:9",
  "resolution": "720p",
  "durationSeconds": 5,
  "generateAudio": false
}'

Multimodal generation

If the reference is already hosted, include its direct public HTTPS download URL in POST /jobs. Subclip rejects private-network hosts. Downloading and media verification continue asynchronously after acceptance, so an unreachable URL, invalid file, or mismatched media type can later produce status: failed with error.code: INPUT_INVALID.

curl -X POST https://www.subclip.app/api/v1/ai-video-generation/jobs \
  -H "Authorization: Bearer $SUBCLIP_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: video-request-url-001" \
  -d '{
  "modelId": "ltx-2-3-pro",
  "prompt": "Continue @video_1 with a slow cinematic push toward the subject",
  "aspectRatio": "16:9",
  "resolution": "1080p",
  "durationSeconds": 6,
  "generateAudio": false,
  "assets": [
    {
      "kind": "video",
      "role": "source_video",
      "url": "https://cdn.yoursite.com/reference.mp4"
    }
  ]
}'

For a local file, use the signed-upload flow:

  1. Create an awaiting_upload draft with the prompt, model settings, and asset metadata through POST /uploads. Its response includes the 15-minute uploadExpiresAt deadline and uploadExpiresIn.
  2. PUT every file to its returned URL using the declared content type and exact content length. Wait for every PUT response and require a successful status.
  3. Start the saved draft with POST /jobs, a body containing only the returned generationId, and the same Idempotency-Key used for POST /uploads. This upload-to-start step is the intentional exception to the usual same-key/same-body rule.

No client-side delay is required after every PUT completes successfully. Subclip verifies each upload and copies it to immutable job input storage before dispatch. If this step returns upload_missing (400), upload_verification_failed (502), or upload_lock_failed (502), treat Retry-After as the minimum delay and retry POST /jobs with the same generationId, identical body, and same Idempotency-Key. Use exponential backoff with jitter (for example 1, 2, 4, and 8 seconds, capped at 15 seconds) and stop at uploadExpiresAt. Do not create a second draft unless the API returns 410 upload_expired.

curl -X POST https://www.subclip.app/api/v1/ai-video-generation/uploads \
  -H "Authorization: Bearer $SUBCLIP_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: video-request-002" \
  -d '{
  "modelId": "kling-v3",
  "prompt": "Use the reference as the opening frame, then move the camera forward slowly",
  "aspectRatio": "16:9",
  "resolution": "720p",
  "durationSeconds": 5,
  "generateAudio": false,
  "assets": [
    {
      "id": "reference-1",
      "kind": "image",
      "fileName": "reference.jpg",
      "contentType": "image/jpeg",
      "fileSize": 2483921
    }
  ]
}'
curl -X PUT "$UPLOAD_URL" \
  -H "Content-Type: image/jpeg" \
  -H "Content-Length: 2483921" \
  --upload-file ./reference.jpg
curl -X POST https://www.subclip.app/api/v1/ai-video-generation/jobs \
  -H "Authorization: Bearer $SUBCLIP_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: video-request-002" \
  -d '{
  "generationId": "aiapi_..."
}'

Status, download, cancel, and automatic retention

POST /uploads creates an awaiting_upload draft, not a running generation. After POST /jobs returns 200 or 202, keep polling that same generationId every five seconds while it is queued or processing. Do not submit a replacement or change its model while it is active.

Treat status as authoritative. The public vocabulary is awaiting_upload for a draft; queued and processing for a started, non-terminal job; and completed, failed, or canceled for outcomes. Completed and failed are terminal; canceled is terminal when cancellationPending is false. That flag can be true only after an explicit cancel request. When it is true, keep polling that same canceled job until it becomes false.

Download only after status: completed and outputReady: true. Save every item in downloads before the earlier of its signed-URL expiresAt and the job's resultExpiresAt. Use cancel only for an explicit user cancellation. A processing job can temporarily return status: canceled, cancellationPending: true, and resultExpiresAt: null; its one-hour access window starts when cancellation finishes.

Starting or replaying a draft after uploadExpiresAt returns 410 upload_expired. Status, download, or cancel requests for a terminal result past resultExpiresAt return 410 generation_expired. After the central sweep removes either record, its generationId returns 404 generation_not_found.

curl https://www.subclip.app/api/v1/ai-video-generation/jobs/aiapi_... \
  -H "Authorization: Bearer $SUBCLIP_API_KEY"
DOWNLOAD_JSON=$(curl -s https://www.subclip.app/api/v1/ai-video-generation/jobs/aiapi_.../download \
  -H "Authorization: Bearer $SUBCLIP_API_KEY")

echo "$DOWNLOAD_JSON" | jq -r '.downloads[] | [.downloadUrl, .fileName] | @tsv' | \
while IFS=$'\t' read -r DOWNLOAD_URL FILE_NAME; do
  curl -L "$DOWNLOAD_URL" -o "$FILE_NAME"
done
curl -X POST https://www.subclip.app/api/v1/ai-video-generation/jobs/aiapi_.../cancel \
  -H "Authorization: Bearer $SUBCLIP_API_KEY"

Credits and errors

402 insufficient_credits, pass_required, or billing_unavailable means the credit preflight failed. No generation is dispatched and no generation credits are charged.

400 upload_missing means an expected signed upload was not found after Subclip's verification attempts. Confirm every PUT succeeded, then follow Retry-After and retry the same draft.

502 upload_verification_failed means storage returned a non-missing error while Subclip was checking an uploaded object. The bytes may still exist; retry the same draft rather than uploading again.

502 upload_lock_failed means the uploaded bytes were found but could not be copied or streamed into immutable job storage. It is a retryable server-side storage finalization failure.

409 idempotency_conflict means the same idempotency key was reused with a different request body, or the requested lifecycle action is invalid for the current status.

503 enqueue_failed means dispatch was temporarily unavailable. Retry the identical request with the same Idempotency-Key.

When a response includes Retry-After—including rate limits and retryable upload finalization errors—wait at least that many seconds before retrying.

A public-URL asset that cannot be downloaded or validated asynchronously can make the accepted job end as failed with error.code: INPUT_INVALID.

410 upload_expired applies when starting or replaying a retained draft after uploadExpiresAt. 410 generation_expired applies to terminal-result access after resultExpiresAt. After the central sweep deletes the record, its generationId returns 404 generation_not_found.

Once a job is accepted, a polling response is never a reason to retry POST /jobs. Continue polling the same generationId. Only a terminal failed response carries an actionable generation error; use its retryable value before deciding whether to create a new request with a new Idempotency-Key.