Open AIavDocsHome
Keep API keys in server-side environment variables or a secret manager. Never place them in browser bundles, Git, logs, or screenshots.

Official SDK quick start

The official SDKs wrap the whole flow (create → poll → download) with automatic idempotency keys and typed errors. The Node SDK has no dependencies; the Python SDK depends only on httpx. They are distributed as GitHub repositories for now; npm and PyPI releases will be announced separately.

Node

npm install github:openaiav/sdk-node   # npm release to be announced

import { OpenAiav } from '@openaiav/sdk'
const client = new OpenAiav({ apiKey: process.env.OPENAIAV_API_KEY })
const task = await client.generations.createAndWait({
  model: 'seedance-2.5-oa', prompt: 'She walks into the sunlight.',
  resolution: '480p', aspectRatio: 'adaptive', durationSeconds: 4,
  generateAudio: false
})
await client.generations.downloadToFile(task.id, './out.mp4')

Python

pip install git+https://github.com/openaiav/sdk-python   # PyPI release to be announced

from openaiav import OpenAiav
client = OpenAiav(os.environ["OPENAIAV_API_KEY"])
task = client.generations.create_and_wait({
    "model": "seedance-2.5-oa", "prompt": "She walks into the sunlight.",
    "resolution": "480p", "aspectRatio": "adaptive", "durationSeconds": 4,
    "generateAudio": False,
})
client.generations.download_to_file(task["id"], "./out.mp4")

Integrate in four steps

  1. Use the server-side OPENAIAV_API_KEY environment variable to call https://api.openaiav.com/v1/models and read current model keys, pricing and reference_modes.
  2. POST prompt, referenceMode and referenceImages to /v1/generations. Reference images are public HTTPS URLs and need no pre-registration. Success is HTTP 201.
  3. Save the returned id and poll the same GET /v1/generations/{id} after poll_after_ms. Keep waiting for processing, result_pending and submission_unknown; do not create another task.
  4. After status becomes succeeded, download with GET /v1/generations/{id}/content. A full response is HTTP 200 and a single byte range is 206.

Authentication and idempotency

export OPENAIAV_API_KEY='oav_...'
# One Idempotency-Key per create intent: every run of this script makes a new one.
# Reuse it only when retrying this same create. No uuidgen? IDEMPOTENCY_KEY=$(python3 -c 'import uuid;print(uuid.uuid4())')
IDEMPOTENCY_KEY=$(uuidgen | tr 'A-Z' 'a-z')
echo "Idempotency-Key: $IDEMPOTENCY_KEY  (reuse this value only when retrying this same create)"

Send Authorization: Bearer oav_... on every request and keep the key server-side. Creation needs generation:create; polling and download need generation:read. Generate a new Idempotency-Key (a UUID) for each create intent, so every run of a sample gets a new key; reuse it only when retrying that same create after an uncertain network result, and never pair one key with different bodies.

Request headers

ParameterTypeRequiredDescription
AuthorizationstringRequiredEvery /v1 request uses Bearer oav_...; keep the key on your server.
Idempotency-KeystringRequired for task creationGenerate a new value (for example a UUID) for each create intent and reuse it only when retrying that same create; the same value and body do not create or charge twice.
Content-TypestringRequired for POSTGeneration creation uses application/json.

Endpoints

MethodPathSuccessScopeDescription
GET/v1/models200Any valid API keyRead current sellable models, pricing, resolutions, durations and reference_modes. Validate capabilities against the catalog before submission.
POST/v1/generation-quotes201generation:createPreview the current price without locking it. Creation reprices the request and charges exactly the reserved_usd in its response as a fixed amount; a failed task is refunded in full. quoteId remains accepted, and expires_at only suggests when to refresh.
POST/v1/generations201generation:createCreate an asynchronous task; submit reference images and referenceMode in the same JSON request.
GET/v1/generations/{id}200generation:readPoll the same task using poll_after_ms until it reaches a terminal state.
GET/v1/generations/{id}/content200 / 206generation:readDownload the output after success; a single byte-range request returns 206.

Step 1: read model capabilities

Do not hard-code model capabilities. Submit only modes listed in reference_modes, and hide or block incompatible combinations before submission. The minimum example demonstrates a 4-second, 480p, no-audio request with seedance-2.5-oa.

curl --fail-with-body 'https://api.openaiav.com/v1/models' \
  --header "Authorization: Bearer $OPENAIAV_API_KEY"

Minimum public video example

The sample contains the full flow: create → poll the same task → download. first_frame uses one reference image and adaptive; generateAudio=false avoids requesting audio. Success codes are 201, 200 and 200/206; on failure, inspect HTTP first and then error.code.

# Keep OPENAIAV_API_KEY in an environment variable; do not paste secrets into shell history.
# One Idempotency-Key per create intent: every run of this script makes a new one.
# Reuse it only when retrying this same create. No uuidgen? IDEMPOTENCY_KEY=$(python3 -c 'import uuid;print(uuid.uuid4())')
IDEMPOTENCY_KEY=$(uuidgen | tr 'A-Z' 'a-z')
echo "Idempotency-Key: $IDEMPOTENCY_KEY  (reuse this value only when retrying this same create)"

# 1. Create — success: HTTP 201. On failure, the response body contains error.code.
# Lost response (timeout or dropped connection)? Re-run this same command in this shell:
# the unchanged $IDEMPOTENCY_KEY returns the original task instead of creating a second one.
curl --fail-with-body --request POST 'https://api.openaiav.com/v1/generations' \
  --header "Authorization: Bearer $OPENAIAV_API_KEY" \
  --header "Idempotency-Key: $IDEMPOTENCY_KEY" \
  --header 'Content-Type: application/json' \
  --data '{
  "model": "seedance-2.5-oa",
  "prompt": "A young woman finishes a letter and walks from a stone cottage into the morning sunlight",
  "resolution": "480p",
  "aspectRatio": "adaptive",
  "durationSeconds": 4,
  "referenceMode": "first_frame",
  "referenceImages": [
    "https://developers.openaiav.com/samples/seedance-letter-reference.webp"
  ],
  "generateAudio": false,
  "outputFormat": "mp4"
}'

# 2. Save the returned id as TASK_ID. Poll the same id — success: HTTP 200.
curl --fail-with-body 'https://api.openaiav.com/v1/generations/TASK_ID' \
  --header "Authorization: Bearer $OPENAIAV_API_KEY"

# Repeat step 2 after poll_after_ms until status is succeeded or failed.
# Never recreate a task whose status is submission_unknown.

# 3. After succeeded, download — success: HTTP 200 (or 206 with Range).
curl --fail-with-body --location 'https://api.openaiav.com/v1/generations/TASK_ID/content' \
  --header "Authorization: Bearer $OPENAIAV_API_KEY" \
  --output 'result.mp4'

Request parameters

ParameterTypeRequiredDescription
modelstringRequiredUse the model value from GET /v1/models; the minimum video sample uses seedance-2.5-oa.
promptstringRequired1–4000 characters. Omni reference can address images in array order as @Image1, @Image2, and so on.
resolutionstringOptionalMust be present in the selected model's resolutions; the minimum video sample uses 480p.
aspectRatiostringOptionalMust be present in the selected model's aspect_ratios; use adaptive when the model requires it for first-frame requests. For image models that list image_size_options, send a resolution and aspectRatio pair from that list: not every ratio exists at every resolution.
durationSecondsintegerOptionalSpecify an integer durationSeconds within the catalog duration_seconds range for video; omission uses the model minimum. The minimum example uses 4 seconds.
referenceModefirst_frame | omni_referenceRequired with video reference imagesUse only with Seedance video models that explicitly list the mode in reference_modes. One request can select only one mode.
referenceImagesstring[]Provide with referenceModePublic HTTPS image URLs. first_frame requires exactly one image; read the omni_reference limit from reference_images_max.
inputImagesstring[]OptionalInput images (reference images) for image-to-image, image models only. Each item is a public HTTPS image URL or a data:image/png;base64,…, data:image/jpeg;base64,… or data:image/webp;base64,… URL. Read each model's limits in GET /v1/models: input_images_max, input_image_max_bytes (per image, by URL or data URL) and input_image_mime_types. Reference images can raise the charge. Do not combine them with a video reference mode.
generateAudiobooleanOptionalDefaults to true for video models that support audio. The lowest-cost sample explicitly sends false; the current catalog lists no separate audio surcharge, and GET /v1/models is always authoritative for billing.
outputFormatmp4 | movOptionalChoose only from catalog output_formats; the minimum sample uses mp4.

Reference modes

first_frame

first_frame: the reference image becomes the opening frame and sets the composition. Submit exactly one referenceImages item; aspectRatio comes from the model's aspect_ratios, and locks to adaptive when the catalog offers it (output follows the reference image). first_last_frame: submit exactly 2 referenceImages. Array order is first frame, then last frame, and the model fills in between. The two images need not share dimensions. Frame modes cannot be combined with omni_reference.

{
  "model": "seedance-2.5-oa",
  "prompt": "A young woman finishes a letter and walks from a stone cottage into the morning sunlight",
  "resolution": "480p",
  "aspectRatio": "adaptive",
  "durationSeconds": 4,
  "referenceMode": "first_frame",
  "referenceImages": [
    "https://developers.openaiav.com/samples/seedance-letter-reference.webp"
  ],
  "generateAudio": false,
  "outputFormat": "mp4"
}

omni_reference

omni_reference: reference media locks subject, style and scene consistency without becoming the opening frame. Beyond referenceImages, capable models also accept referenceVideos and referenceAudios; per-type limits are in the table below, and any type the catalog does not declare is a 400 at submit time. Prompts address items by their order within each type: @Image1, @Video1, @Audio1.

{
  "model": "seedance-2.5-oa",
  "prompt": "Keep the identity and outfit from @Image1; a young woman walks from a stone cottage into the morning sunlight",
  "resolution": "480p",
  "aspectRatio": "16:9",
  "durationSeconds": 4,
  "referenceMode": "omni_reference",
  "referenceImages": [
    "https://developers.openaiav.com/samples/seedance-letter-reference.webp"
  ],
  "generateAudio": false,
  "outputFormat": "mp4"
}

Live compatibility

One integration for every video model: same endpoint, same fields. Switching models means changing model only. All differences are declared by the catalog, and the table below is that catalog: per model, which reference modes it supports and how much of each media type it accepts. Anything the catalog does not declare is rejected explicitly at submit time, never silently ignored.

The catalog is not loaded. Call GET /v1/models first; do not submit a reference mode for a model without reference_modes.

First-frame details & last-frame relay

First-frame details: first_frame and omni_reference are mutually exclusive, so one request picks exactly one. Under omni reference you may steer a reference image toward the opening shot via the prompt, but when strict first-frame fidelity matters, submit that image in first_frame mode. On Seedance 2.5, first-frame tasks fix the aspect ratio to adaptive (output follows the first frame). Last-frame relay: pass returnLastFrame: true at creation and a succeeded task also returns last_frame_url (a watermark-free PNG at video size, kept for 24 hours). Feed that URL directly as the next request’s first_frame referenceImages to chain multi-shot sequences, with no pre-registration step.

Reference video pricing

  • Reference videos with a measurable duration are billed at a fixed price, which you can preview first. If the duration cannot be measured, the preview is rejected without a charge.
  • Call POST /v1/generation-quotes to preview the current price. URL videos are measured again and repriced at creation; the creation response reserved_usd gives the actual amount. You may still send quoteId, but it does not lock the price. expires_at only suggests when to refresh the preview.
  • The model catalog sets per-video and total duration, count and minimum billable seconds. Each reference video must be 2–15 seconds for the Seedance 2.0 series and 2–30 seconds for other supported models. Measured input outside the model limits returns 422 REFERENCE_DURATION_OUT_OF_RANGE without a charge.
  • Use a public HTTPS direct link to MP4/MOV. Known files above 200 MiB return REFERENCE_VIDEO_TOO_LARGE; an unstated size can still be quoted. Private URLs return REFERENCE_URL_NOT_PUBLIC, and busy probes return 429 REFERENCE_PROBE_BUSY. Console uploads are limited to 32 MiB.
  • Every task is charged its quoted price, reserved_usd, at creation; on success that is the final charge, and on failure it is refunded in full. A preview's price_usd equals what creation would charge at that moment; output duration must still be explicit, not -1.
  • Public video URLs are probed for the preview and again at creation, then passed directly to the generation service without platform storage or asset registration. Keep the URL content stable. Uploads use a separate registered-asset path. Model restrictions on real human faces still apply.

Image model pricing

  • Image models are priced per image by resolution and aspectRatio (image_pricing.rates). For the gpt-image-* and nano-banana-* models, these listed prices are for a short prompt with no reference images.
  • Reference images and longer prompts raise the charge for these models. Before you create a task, call POST /v1/generation-quotes with the same body: price_usd is the charge for that request at current prices. A failed task is refunded in full.

Polling and download

Only succeeded and failed are terminal. submission_unknown means acceptance is still being confirmed and result_pending is finalizing: retain the original task ID, keep polling, and never replay creation. After success, use the authenticated /content path rather than relying on any non-Open AIav location. Results are kept for 24 hours (the detail response carries output_expires_at; expired downloads return 410 OUTPUT_EXPIRED, so save promptly).

StatusTerminalAction
processingNoAccepted; poll again after poll_after_ms.
result_pendingNoThe result is finalizing. Keep the task ID and poll the same task; do not recreate it.
submission_unknownNoAcceptance is being confirmed; do not replay creation. Keep and poll the original task ID.
succeededYesSucceeded; download the output using /content.
failedYesDefinitively failed and refunded in full; inspect error_code.

Retired endpoint migration

Legacy /v1/assets, /v1/assets/content, /v1/assets/{id}, /v1/asset-groups and /v1/asset-groups/{id} are retired; a valid key with the corresponding legacy scope receives HTTP 410 / ENDPOINT_RETIRED. New integrations must not call these paths; put referenceMode and referenceImages in the same generation request.

Error code reference

Full reference: HTTP status, code, meaning, suggested handling, and the SDK error class. This table shares its data source with the SDK error map; unknown new codes fall back by HTTP status in the SDK.

HTTPCodeMeaningSuggested handlingSDK
401UNAUTHENTICATEDMissing or unrecognized API key.Send Authorization: Bearer oav_…; keep the key server-side.AuthenticationError
401INVALID_API_KEYThe API key is invalid or revoked.Check the key in the console; rotate it to get a new one if needed.AuthenticationError
403INSUFFICIENT_SCOPEThe key lacks the scope this operation needs.Creation needs generation:create; polling and download need generation:read.PermissionError
429RATE_LIMITEDRequests exceeded the key rate limit.Back off per Retry-After / retryAfterSeconds; the SDK handles this automatically.RateLimitError
400IDEMPOTENCY_KEY_REQUIREDCreation requires an Idempotency-Key header.Generate a unique key per new request and reuse it on retries. The SDK auto-generates one.InvalidRequestError
409IDEMPOTENCY_KEY_CONFLICTThe same idempotency key was used with a different body.Use a fresh key for different parameters; keep key and body identical on retries.InvalidRequestError
400MODEL_NOT_FOUNDUnknown model id.Use ids from GET /v1/models (e.g. seedance-2.5-oa).InvalidRequestError
503MODEL_NOT_AVAILABLEThe model cannot be called right now.Nothing was charged. Check the model status in GET /v1/models and try again later.ServerError
400INVALID_PROMPTThe prompt is empty or too long.Prompts must be 1–4000 characters.InvalidRequestError
400INVALID_DURATIONdurationSeconds is outside the model range.Specify the video length as an integer durationSeconds within the catalog range.InvalidRequestError
400INVALID_RESOLUTIONThe resolution is not supported by the model.Pick a value from the catalog resolutions.InvalidRequestError
400INVALID_ASPECT_RATIOThe aspect ratio is not supported by the model.Pick from catalog aspect_ratios; first_frame often locks adaptive.InvalidRequestError
400INVALID_REFERENCE_IMAGESreferenceImages must be an array of public HTTPS URLs.Provide https:// image URLs readable for the task lifetime.InvalidRequestError
400INVALID_REFERENCE_IMAGEA reference image entry is not a valid URL.The message carries the index (referenceImages[i]); fix that entry.InvalidRequestError
400REFERENCE_IMAGE_URL_NOT_HTTPSA reference image URL is not public HTTPS.http, private-network and data URLs are rejected; host the image publicly over HTTPS.InvalidRequestError
400REFERENCE_MODE_REQUIRES_IMAGESreferenceMode was set without reference images.Provide referenceMode and referenceImages together.InvalidRequestError
400REFERENCE_MODE_NOT_SUPPORTEDThe model does not support the requested reference mode.Choose first_frame / omni_reference per the catalog reference_modes.InvalidRequestError
400FIRST_FRAME_REQUIRES_ONE_IMAGEfirst_frame takes exactly one reference image.Submit exactly one image; use omni_reference for multiple.InvalidRequestError
400TOO_MANY_REFERENCESReference image count exceeds the catalog limit.See reference_images_max in the catalog.InvalidRequestError
400INVALID_INPUT_IMAGESinputImages must be an array.Each item is an HTTPS URL or data:image/* base64.InvalidRequestError
400INVALID_INPUT_IMAGEAn input image entry is malformed.Each entry must be an HTTPS URL or data:image/* base64; the message carries the index.InvalidRequestError
400INPUT_IMAGE_URL_NOT_HTTPSAn input image URL is not HTTPS.Use an https:// URL or a data URL such as data:image/png;base64,….InvalidRequestError
400INPUT_IMAGE_TOO_LARGEAn embedded image exceeds 10 MiB.Compress it below 10 MiB. Models that list input_image_max_bytes apply the same limit to HTTPS URLs: an oversized image fails the task with REFERENCE_INPUT_INVALID, refunded in full.InvalidRequestError
400IMAGE_INPUT_NOT_SUPPORTEDThe model does not accept image input.Only image models with input_images_max>0 accept inputImages.InvalidRequestError
400TOO_MANY_INPUT_IMAGESToo many input images.See input_images_max in the catalog.InvalidRequestError
422REFERENCE_DURATION_OUT_OF_RANGEMeasured reference-video duration exceeds the model limits.Check per-video, total-duration and count limits in the catalog, then preview again; no charge was made.InvalidRequestError
422REFERENCE_DURATION_UNVERIFIEDThe reference-video duration could not be measured.Use a plain MP4/MOV, preferably faststart, from a host that supports Range requests.InvalidRequestError
422REFERENCE_URL_NOT_PUBLICThe reference-video URL is not public HTTPS.Use a public HTTPS file URL on port 443 without sign-in.InvalidRequestError
422CONTENT_TYPE_MISMATCHThe reference-video URL does not return the required video format.Make sure the URL returns MP4/MOV bytes, not a web page or image.InvalidRequestError
422REFERENCE_VIDEO_TOO_LARGEThe reference video exceeds the file-size limit.Public URLs are limited to 200 MiB; console uploads to 32 MiB. Compress or trim the file and preview again.InvalidRequestError
429REFERENCE_PROBE_BUSYToo many reference videos are being measured at once.Retry after retry_after_seconds; no charge was made.RateLimitError
400INSUFFICIENT_BALANCEBalance is too low for this task.Top up or redeem a code in Wallet, then retry.InvalidRequestError
400LEGACY_ASSET_REFERENCE_RETIREDAsset-id references are retired.Submit referenceMode + referenceImages (public HTTPS) in the same request.InvalidRequestError
404NOT_FOUNDThe task does not exist or belongs to another account.Confirm the task id and the key belong to the same account.InvalidRequestError
409OUTPUT_NOT_READYThe output is not ready yet.Keep polling the task per poll_after_ms until terminal.InvalidRequestError
410OUTPUT_EXPIREDThe download window (24h after success) has closed.Save outputs promptly; regenerate after expiry. Use output_expires_at for countdowns.OutputExpiredError

Image task failure codes

When an image task ends with status=failed, read error_code and match it below. A failed task is refunded in full. The SDKs raise GenerationFailedError with the same code.

CodeMeaning
CONTENT_POLICY_REJECTEDThe content did not pass review. Revise it before submitting again.
REFERENCE_INPUT_INVALIDCheck the reference format, size and model requirements.
REFERENCE_SOURCE_UNREACHABLEThe reference URL cannot be read. Check its expiry or upload again.
REFERENCE_SERVICE_UNAVAILABLEReference processing is unavailable. Contact support with the task ID; replacing the image is not required.
GENERATION_INPUT_INVALIDThe generation parameters do not meet this model’s requirements.
GENERATION_RATE_LIMITEDThe generation service is temporarily rate limited. Try again later.
GENERATION_REJECTEDThe generation request was rejected. Contact support with the task ID to verify why.
GENERATION_SERVICE_UNAVAILABLEThe generation service is temporarily unavailable.
GENERATION_FAILEDThe task could not complete. Contact support with its ID.

Codes returned only in the console

HTTPCodeMeaningSuggested handling
400REFERENCE_UPLOAD_NOT_FOUNDThe upload reference is missing, expired, or owned by another account.Re-upload in the Playground and run again (uploads are short-lived).
429UPLOAD_QUOTA_EXCEEDEDToo many unused uploads.Run a task that uses your uploads, or wait for them to expire.
429WEBHOOK_ENDPOINT_LIMITWebhook endpoint limit reached.Delete or disable an unused endpoint.
400INVALID_WEBHOOK_URLThe callback URL is not publicly reachable HTTPS.Use a public HTTPS address; private/loopback hosts are rejected.