Video Model API Platform Integration Guide
Integrate the video generation API into your own product or tool, for your users or team.
01Get your first video running in 3 minutes
Want to try it before you write any code? Use your API key in the video workbench to generate a video directly — no code required.
The minimal loop is three steps: submit → poll → download.
⚠️ Generation is asynchronous. Turnaround time varies with the model, duration, and scene complexity. Submission returns a job ID, not a video — poll the response's poll_url to get the result. Do not wait synchronously.
The response's status starts as queued, then moves to running; poll_url is the address you poll next:
The request fields are identical to submission — just swap the path for /generations/estimate. It does not create a job, charge you, or trigger generation.
Take the poll_url from the previous response and request it as-is:
Once status becomes completed, video_url is a direct, downloadable link to the video:
⚠️ video_url expires after about 24 hours — download and save it promptly; do not rely on it staying reachable long-term.
| Model | Call name (use this in the model field) | Supported resolutions |
|---|---|---|
| Seedance 2.0 | doubao-seedance-2.0 | 480p / 720p / 1080p |
| Seedance 2.0 Fast | doubao-seedance-2.0-fast | 480p / 720p |
| Wan 3.0 | wan3.0-video | 480p / 720p / 1080p |
The table above lists the currently available video models; for the complete, real-time list see the model market or the response of GET /v1/models.
02Reference images
🔴 When submitting reference images via the API, provide publicly accessible URLs in image_urls. This matches the generation engine's own upstream requirement — upstream only accepts links, never raw files.
For images containing real people: first create an asset using that URL, then reference it in your generation request as asset://<asset_id> — see "Asset library: the real-person channel" below.
(This platform also offers a separate upload endpoint, used by the video workbench when a user selects a local image; a platform integration built on the API does not need it — see "API reference → 5.8".)
⚠️ Pixel limit: each image's width × height must not exceed 36 million pixels. A typical phone photo at 6048×8064 (about 49 million pixels) will be rejected. We cannot pre-screen this for you — downscale on your own side, or the failure will only surface at generation time.
⚠️ Up to 9 reference images per request.
03Asset library: the real-person channel
Models apply content moderation to reference images that contain real people — submitting a raw image link for one will be rejected. The asset library is this platform's compliance channel for that case: register the likeness as an asset first, then reference it in your generation request.
Whenever a reference image contains a real human face. If every video in your product features a real person (store walkthroughs, voiceover hosts, creator appearances), then the asset library isn't optional — it's the only path through.
- Get an image link (your own public URL, or one from the upload endpoint)
- Call the create-asset endpoint with that link and a name → get back an asset ID
- In your generation request's
image_urls, writeasset://<asset_id>
⚠️ After creating an asset there is a brief processing window (about 10 seconds in practice) before it can be used for generation. The status field tells you where it is in that process.
Asset status: Processing → Active (usable for generation) / Failed (processing failed). Only Active assets can be referenced in a generation request.
Take the ref from the previous step (asset://<asset_id>) and place it in image_urls, exactly like a regular image link:
04Naming people & mention syntax
When several reference images appear together, you can tell the prompt "have this person do X" by naming them.
🔴 Over the API you must use the positional mention syntax @图片1, @图片2 — the number matches the order of the image_urls array (starting at 1). These two tokens are a literal, untranslated string the upstream model requires; do not substitute an English phrase for them.
⚠️ The video workbench lets you type a name like @Alex — the API does not. The video workbench rewrites that name into a positional token before it ever reaches the API; the API itself performs no such rewrite, so whatever name you type is treated as plain text and the person binding will not take effect.
⇒ If you want your users to be able to use names, the rewrite has to happen in your own product: maintain a "name → which position this image is" mapping, and rewrite it to @图片N before you submit.
05API reference
Authentication: every endpoint uses Authorization: Bearer <your key>.
Error envelope (uniform across every endpoint):
Please branch on code, not on the message text — copy may change, code is stable.
POST /videos/v1/videos/generations| Field | Type | Required | Description |
|---|---|---|---|
model | string | ✅ | Model name, ≤100 characters |
prompt | string | ✅ | Prompt text, ≤2000 characters |
duration | integer | Seconds, defaults to 5; the standard range is 4–15 (some models support a wider range — up to 30 seconds, and can pass -1 to let the model auto-pick a duration; out-of-range values return 400 at submission time — the actual supported range depends on the model) | |
resolution | string | 480p / 720p / 1080p / 4k, defaults to 720p | |
aspect_ratio | string | 16:9 / 4:3 / 1:1 / 3:4 / 9:16 / 21:9 / adaptive | |
image_urls | array | Reference images, up to 9 items; two accepted forms below | |
video_urls | string[] | Reference videos, up to 3 items; asset references are not accepted here | |
audio_urls | string[] | Reference audio, up to 3 items; asset references are not accepted here | |
generate_audio | boolean | Whether to generate an audio track | |
watermark | boolean | ||
seed | integer | -1 to 4294967295 | |
generation_type | string | omni_reference / first_and_last_frames | |
callback_url / callback_secret | — | ❌ | Not supported — passing these returns an error. Poll poll_url instead. |
Each item in image_urls can be:
- A string: a public image URL, or
asset://<asset_id> - An object:
{ "url": "...", "role": "reference_image" }, whererolemay befirst_frame/last_frame/reference_image
Request header Idempotency-Key (optional, strongly recommended): send the same key on a network retry and it will not create a duplicate job or charge you twice. Reusing a key with different request content returns 409 — use a new key for a new job.
Success response:
POST /videos/v1/videos/generations/estimateSame request fields as 5.1. It does not create a job, charge you, or trigger generation.
| Response field | Description |
|---|---|
estimated_cost | Estimated cost (USD) |
estimated_cost_cny | CNY-converted amount (reference only) |
fx_cny_per_usd | Display exchange rate |
currency | USD (the billing currency of record) |
final_cost_may_adjust | true — the final settled amount may differ slightly |
GET /videos/v1/videos/jobs/{job_id}| Field | Description |
|---|---|
status | See the status table below |
status_note | Present only when there is something worth explaining (a short human-readable note) |
video_url | The generation engine's own direct link, valid for about 24 hours |
expires_at | Expiry time of the link above |
platform_video_url | This platform's own copy; defaults to null (not produced unless the 7-day copy is enabled) |
platform_expires_at | Expiry of the copy above (returned only when the 7-day copy is enabled) |
error | {code, message, details} on failure; always null while in progress |
cost_pending / cost_final | Held / settled amount (USD) |
duration_ms | Wall-clock time from submission to completion |
metadata | duration / resolution / ratio / seed / usage, etc. |
Job status values:
| Value | Meaning |
|---|---|
queued | Accepted, waiting in the queue |
running | Generating |
completed | Done — ready to download |
failed | Failed (the hold is auto-refunded) |
cancelled | Cancelled |
timeout | Timed out (the hold is auto-refunded) |
⚠️ When you see running with a status_note, we are confirming the result with the generation provider — the charge has not been settled yet; do not resubmit.
GET /videos/v1/videos/jobs| Parameter | Description |
|---|---|
page / page_size | Page number (≥1) / items per page (1–200, default 20) |
status | Filter by status, values as in the table above |
start_date / end_date | YYYY-MM-DD, both inclusive |
api_key_id | Filter by key |
Responds with { items: [...], total, page, page_size }, sorted newest-first by submission time (not adjustable).
GET /videos/v1/videos/jobs/statsSame filter parameters as 5.4. Returns total_jobs / completed_jobs / failed_jobs / in_flight_jobs / total_cost (completed jobs only) / avg_duration_ms.
GET /videos/v1/videos/jobs/export.csvSame filter parameters as 5.4. ⚠️ A single export is capped at 200 rows — for more, page through 5.4 and aggregate on your side.
Create an asset POST /videos/v1/videos/assets
| Field | Type | Required | Description |
|---|---|---|---|
url | string | ✅ | An http(s) image link, ≤2048 characters |
name | string | ≤64 characters; cannot be "图片" or "图片N" (reserved for the mention syntax); must be unique within your account |
The response includes id (for deletion), ref (shaped like asset://xxx, placed directly into image_urls), and status (Processing / Active / Failed). Only Active assets can be used for generation.
List assets GET /videos/v1/videos/assets?model=<model name>
Returns { assets: [...] }. With model supplied, each item carries usable indicating whether it's usable under that model; without model, usable is null (undetermined — do not treat it as usable).
Delete an asset DELETE /videos/v1/videos/assets/<asset_id>
Removes it from your library. already_retired: true in the response means it was already deleted (repeat calls are safe).
Check capability GET /videos/v1/videos/assets/capability?model=<model name>
Returns enabled (whether that model supports the asset library), upload_max_bytes, and upload_max_pixels. We recommend reading this endpoint at integration time rather than hardcoding these limits in your own code.
This platform provides an upload endpoint used by the video workbench when a user selects a local image — it turns a local file into a usable URL. A platform integration built on the API does not need it: you already hold or can produce your own public URL, which matches upstream's own contract (see "Reference images" above).
POST /videos/v1/videos/uploads, with the raw image bytes as the request body (not a form) — declare the type via Content-Type (image/jpeg / png / webp / gif / heic / heif); the response includes url.
06Error codes
Branch on code, not on the message text.
| code | HTTP | Meaning | Tell the user |
|---|---|---|---|
input_image_real_person | 400 | A reference image may contain a real person | This image needs to be added to the asset library first |
input_image_too_large | 400 | Image exceeds the pixel limit | Use a smaller image (the actual size and the limit are in details) |
output_content_policy | 400 | The generated content may involve copyright or sensitive material | Adjust the reference images or prompt and retry |
reference_media_unfetchable | 400 | A reference image/video could not be read | Check whether the link is publicly reachable and not too slow |
invalid_asset_reference_format | 400 | The asset reference is malformed (not shaped like asset://<asset_id>, or placed in video_urls/audio_urls where it isn't accepted) | This is a bug on your side, not the user's — do not surface this raw to your user; check your own code |
invalid_asset_reference | 400 | The referenced asset isn't in your library, or isn't usable on that model's channel | Create the asset again |
asset_unavailable | 400 | The asset isn't ready yet | Wait until it becomes Active, then resubmit |
asset_name_taken / asset_name_reserved / asset_name_too_long | 400 | Asset naming issue | Pick a different name |
asset_library_full | 400 | Your asset library has hit its cap | Delete unused assets |
| code | HTTP | Meaning | Suggested action |
|---|---|---|---|
invalid_api_key | 401/403 | Invalid key, or the account is disabled | Check your configuration; contact us |
ip_not_allowed | 403 | The key is restricted to an IP range and the current source isn't in it | Contact us to adjust it |
insufficient_credits | 402 | Insufficient account balance | Top up; we recommend building your own low-balance alert |
idempotency_key_reused | 409 | The same idempotency key was reused with different request content | Use a new key |
model_not_found | 404 | Model name doesn't exist or isn't enabled | Check the model name |
task_not_found | 404 | Job doesn't exist, or doesn't belong to you | Check the job ID |
upload_quota_exceeded | 429 | Upload quota reached | A platform integration shouldn't be using the upload endpoint — see "Reference images" |
rate_limit_exceeded | 429 | Requests are coming in too fast | Back off and retry |
upstream_channel_unavailable | 502 | The generation channel is temporarily unavailable | Back off and retry |
upstream_timeout | 504 | Generation timed out (the hold is auto-refunded) | Retryable |
upstream_generation_failed | 502/503 | Failure on the generation provider's side (the hold is auto-refunded) | Back off and retry |
submission_result_ambiguous | 502 | The submission result could not be confirmed | 🔴 Contact us first — do not retry blindly |
internal_error | 500 | Internal error on this platform | Contact us |
- 400-class: don't auto-retry — the request itself needs to change.
- 401 / 402 / 404 / 409: don't auto-retry.
- 429: exponential backoff.
- 502 / 503 / 504: retryable with backoff, except
submission_result_ambiguous— it means we could not confirm whether that job was accepted by the generation provider; retrying blindly risks a duplicate video and a duplicate charge.
07Tenancy & isolation boundaries 🔴 read before integrating
Assets and jobs are scoped to your account, not to an individual API key.
If you only call this API from within your own service and you decide who sees what, none of this affects your users — everything they see is determined entirely by your product. But if you plan to hand off listing, querying, or deleting assets to them (for example, an asset-management panel for your users, or handing them a key directly), read this section first.
The reason: even if you issue different keys to different users, this platform sees all of them as the same tenant. As a result:
- Once you expose "list assets" / "delete assets" to your users, User A can see, and can delete, an asset uploaded by User B
- The asset-count cap is account-level — all of your users share the same quota: up to 100,000 per account
- Usage breakdowns are available at the granularity of a key, at finest
This platform can only see down to your account — it cannot see, or distinguish, which of your users is behind a given call on your product. Who-can-see-whom and what-permissions-they-have among your own users can only be implemented in your own product — this is a layer this platform cannot do for you.
Isolation between different customers (i.e. different accounts) is guaranteed: accounts cannot see each other; you cannot see another customer's assets.
🔴 Authorization for real-person material is your responsibility. This platform's asset library channel does not include the generation engine's own "verified real-name / liveness check" flow — an asset can be registered and used for generation directly, and this platform does not generate any proof that "the person photographed has consented" on your behalf.
You must ensure that every user on your product who uploads a real-person asset lawfully holds the right to use that likeness with the necessary authorization, and that you retain the corresponding proof of authorization; you should also address this in the service agreement you have with your own users. Content involving a minor's likeness or voice is subject to stricter compliance requirements.
08Quotas & limits
| Item | Limit | Scope |
|---|---|---|
| Prompt length | 2000 characters | Per request |
| Reference images | 9 | Per request |
| Reference videos / audio | 3 each | Per request |
| Pixels per image | 36 million (width × height) | Per image |
| File size per image | 30 MB | Per image |
| Total request body size | 1 MB (excluding the upload endpoint) | Per request |
| Asset library capacity | 100,000 | Per account (not per key) |
| CSV export | 200 rows | Per request |
🔴 Asset library capacity is account-level — all of your users share the same quota: up to 100,000 assets per account, which covers the large majority of use cases. If you have a large user base and need a higher cap, contact us to raise it.
⚠️ Result links expire: by default only the generation engine's own direct link is provided (valid for about 24 hours) — download and move it to your own storage promptly; if you need this platform to retain a 7-day copy, contact us to enable it.
⚠️ Cancelling a job is not currently supported.
09Integration FAQ
Why was my image rejected?
Why doesn't @Alex work?
@图片1 — see "Naming people & mention syntax".My users can see each other's assets — what do I do?
How long does a job take?
poll_url asynchronously; do not wait synchronously.Can I cancel a job?
Want more integration detail? Contact [email protected] and we'll confirm the integration details with you as soon as we can.