Official SDK quick start
The official SDKs wrap the one-step contract (create → poll → download) with automatic idempotency keys and typed errors; the Node SDK has zero dependencies and Python depends only on httpx. Distributed as GitHub repositories for now — npm/PyPI releases will be announced separately.
Node
npm install github:openaiav/sdk-node # npm 正式发布另行公告
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 正式发布另行公告
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
- 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.
- POST prompt, referenceMode and referenceImages to /v1/generations. Reference images are public HTTPS URLs and need no pre-registration. Success is HTTP 201.
- 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.
- 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_...'
export OPENAIAV_IDEMPOTENCY_KEY='openaiav-quickstart-4s-480p-001'Send Authorization: Bearer oav_... on every request and keep the key server-side. Creation needs generation:create; polling and download need generation:read. Reuse the original Idempotency-Key after an uncertain network result; never pair one key with different bodies.
Request headers
| Parameter | Type | Required | Description |
|---|---|---|---|
| Authorization | string | Required | Every /v1 request uses Bearer oav_...; keep the key on your server. |
| Idempotency-Key | string | Required for task creation | Reuse the same value for a network retry; the same value and body do not create or charge twice. |
| Content-Type | string | Required for POST | Generation creation uses application/json. |
Endpoints
| Method | Path | Success | Scope | Description |
|---|---|---|---|---|
| GET | /v1/models | 200 | valid API key | Read current sellable models, pricing, resolutions, durations and reference_modes. Validate capabilities against the catalog before submission. |
| POST | /v1/generations | 201 | generation:create | Create an asynchronous task; submit reference images and referenceMode in the same JSON request. |
| GET | /v1/generations/{id} | 200 | generation:read | Poll the same task using poll_after_ms until it reaches a terminal state. |
| GET | /v1/generations/{id}/content | 200 / 206 | generation:read | Download 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.
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 both values in environment variables; do not paste secrets into shell history.
# Generate OPENAIAV_IDEMPOTENCY_KEY once for this create intent and keep it unchanged on every retry.
# Set it before the first create, then reuse the exact value after a lost response.
# 1. Create — success: HTTP 201. On failure, the response body contains error.code.
curl --fail-with-body --request POST 'https://api.openaiav.com/v1/generations' \
--header "Authorization: Bearer $OPENAIAV_API_KEY" \
--header "Idempotency-Key: $OPENAIAV_IDEMPOTENCY_KEY" \
--header 'Content-Type: application/json' \
--data '{
"model": "seedance-2.5",
"prompt": "女孩写完一封信,从乡间石屋走入清晨阳光",
"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
| Parameter | Type | Required | Description |
|---|---|---|---|
| model | string | Required | Use the model value from GET /v1/models; the minimum video sample uses seedance-2.5. |
| prompt | string | Required | 1–4000 characters. Omni reference can address images in array order as @Image1, @Image2, and so on. |
| resolution | string | Optional | Must be present in the selected model's resolutions; the minimum video sample uses 480p. |
| aspectRatio | string | Optional | Must be present in the selected model's aspect_ratios. It also applies to image models; use adaptive when the selected model requires it for first-frame requests. |
| durationSeconds | integer | Optional | Video requests default to the model minimum; an explicit value must fall within catalog duration_seconds. The minimum sample uses 4 seconds. |
| referenceMode | first_frame | omni_reference | Required with video reference images | Use only with Seedance video models that explicitly list the mode in reference_modes. One request can select only one mode. |
| referenceImages | string[] | Provide with referenceMode | Public HTTPS image URLs. first_frame requires exactly one image; read the omni_reference limit from reference_images_max. |
| inputImages | string[] | Optional | Image-to-image input for image models only, using HTTPS or data:image/*; read input_images_max and do not mix it with a video reference mode. |
| generateAudio | boolean | Optional | Defaults 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. |
| outputFormat | mp4 | mov | Optional | Choose 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",
"prompt": "女孩写完一封信,从乡间石屋走入清晨阳光",
"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",
"prompt": "@Image1 保持人物身份与服装一致,女孩从乡间石屋走入清晨阳光",
"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.
First-frame details & last-frame relay
First-frame details: first_frame and omni_reference are mutually exclusive — 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.
Polling and download
Only succeeded and failed are terminal. submission_unknown is a reconciliation state: 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).
| Status | Terminal | Action |
|---|---|---|
| processing | No | Accepted; poll again after poll_after_ms. |
| result_pending | No | The output is being finalized for settlement; keep polling the same task. |
| submission_unknown | No | The outcome is being reconciled; do not replay creation. Keep and poll the original task ID. |
| succeeded | Yes | Succeeded; download the output using /content. |
| failed | Yes | Definitively failed and the reservation was refunded; inspect error_code. |
Retired endpoint migration
/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.
| HTTP | Code | Meaning | Suggested handling | SDK |
|---|---|---|---|---|
| 401 | UNAUTHENTICATED | Missing or unrecognized API key. | Send Authorization: Bearer oav_…; keep the key server-side. | AuthenticationError |
| 401 | INVALID_API_KEY | The API key is invalid or revoked. | Check the key in the console; rotate to mint a new one if needed. | AuthenticationError |
| 403 | INSUFFICIENT_SCOPE | The key lacks the scope this operation needs. | Creation needs generation:create; polling and download need generation:read. | PermissionError |
| 429 | RATE_LIMITED | Requests exceeded the key rate limit. | Back off per Retry-After / retryAfterSeconds; the SDK handles this automatically. | RateLimitError |
| 400 | IDEMPOTENCY_KEY_REQUIRED | Creation requires an Idempotency-Key header. | Generate a unique key per new request and reuse it on retries. The SDK auto-generates one. | InvalidRequestError |
| 409 | IDEMPOTENCY_KEY_CONFLICT | The same idempotency key was used with a different body. | Use a fresh key for different parameters; keep key and body identical on retries. | InvalidRequestError |
| 400 | MODEL_NOT_FOUND | Unknown model id. | Use ids from GET /v1/models (e.g. seedance-2.5-oa). | InvalidRequestError |
| 400 | INVALID_PROMPT | The prompt is empty or too long. | Prompts must be 1–4000 characters. | InvalidRequestError |
| 400 | INVALID_DURATION | durationSeconds is outside the model range. | Use an integer within the catalog range, or -1 when auto duration is supported. | InvalidRequestError |
| 400 | INVALID_RESOLUTION | The resolution is not supported by the model. | Pick a value from the catalog resolutions. | InvalidRequestError |
| 400 | INVALID_ASPECT_RATIO | The aspect ratio is not supported by the model. | Pick from catalog aspect_ratios; first_frame often locks adaptive. | InvalidRequestError |
| 400 | INVALID_REFERENCE_IMAGES | referenceImages must be an array of public HTTPS URLs. | Provide https:// image URLs readable for the task lifetime. | InvalidRequestError |
| 400 | INVALID_REFERENCE_IMAGE | A reference image entry is not a valid URL. | The message carries the index (referenceImages[i]); fix that entry. | InvalidRequestError |
| 400 | REFERENCE_IMAGE_URL_NOT_HTTPS | A reference image URL is not public HTTPS. | http, private-network and data URLs are rejected; host the image publicly over HTTPS. | InvalidRequestError |
| 400 | REFERENCE_MODE_REQUIRES_IMAGES | referenceMode was set without reference images. | Provide referenceMode and referenceImages together. | InvalidRequestError |
| 400 | REFERENCE_MODE_NOT_SUPPORTED | The model does not support the requested reference mode. | Choose first_frame / omni_reference per the catalog reference_modes. | InvalidRequestError |
| 400 | FIRST_FRAME_REQUIRES_ONE_IMAGE | first_frame takes exactly one reference image. | Submit exactly one image; use omni_reference for multiple. | InvalidRequestError |
| 400 | TOO_MANY_REFERENCES | Reference image count exceeds the catalog limit. | See reference_images_max in the catalog. | InvalidRequestError |
| 400 | INVALID_INPUT_IMAGES | inputImages must be an array. | Each item is an HTTPS URL or data:image/* base64. | InvalidRequestError |
| 400 | INVALID_INPUT_IMAGE | An input image entry is malformed. | Each entry must be an HTTPS URL or data:image/* base64; the message carries the index. | InvalidRequestError |
| 400 | INPUT_IMAGE_TOO_LARGE | An embedded image exceeds 10 MB. | Compress it or switch to a public HTTPS URL. | InvalidRequestError |
| 400 | IMAGE_INPUT_NOT_SUPPORTED | The model does not accept image input. | Only image models with input_images_max>0 accept inputImages. | InvalidRequestError |
| 400 | TOO_MANY_INPUT_IMAGES | Too many input images. | See input_images_max in the catalog. | InvalidRequestError |
| 400 | INSUFFICIENT_BALANCE | Balance is insufficient to reserve this task. | Redeem a top-up card on the Billing page, then retry. | InvalidRequestError |
| 400 | LEGACY_ASSET_REFERENCE_RETIRED | Asset-id references are retired. | Submit referenceMode + referenceImages (public HTTPS) in the same request. | InvalidRequestError |
| 404 | NOT_FOUND | The task does not exist or belongs to another account. | Confirm the task id and the key belong to the same account. | InvalidRequestError |
| 409 | OUTPUT_NOT_READY | The output is not ready yet. | Keep polling the task per poll_after_ms until terminal. | InvalidRequestError |
| 410 | OUTPUT_EXPIRED | The download window (24h after success) has closed. | Save outputs promptly; regenerate after expiry. Use output_expires_at for countdowns. | OutputExpiredError |
Console-session-only codes
| HTTP | Code | Meaning | Suggested handling |
|---|---|---|---|
| 400 | REFERENCE_UPLOAD_NOT_FOUND | The upload reference is missing, expired, or owned by another account. | Re-upload in the Playground and run again (uploads are short-lived). |
| 429 | UPLOAD_QUOTA_EXCEEDED | Too many unused uploads. | Run a task to consume uploads, or wait for them to expire. |
| 429 | WEBHOOK_ENDPOINT_LIMIT | Webhook endpoint limit reached. | Delete or disable an unused endpoint. |
| 400 | INVALID_WEBHOOK_URL | The callback URL is not publicly reachable HTTPS. | Use a public HTTPS address; private/loopback hosts are rejected. |