Video Model API Platform Integration Guide
Integrate the video generation API into your own product or tool, for your users or team.
Developers already using the Volcano Ark native request format can use this platform's Ark-compatible endpoint directly (POST /videos/api/v3/contents/generations/tasks); see the differences from Ark native below — Ark-compatible endpoint: differences from Ark native.
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 |
| Seedance 2.5 | doubao-seedance-2.5 | 480p / 720p / 1080p |
| MiniMax H3 | MiniMax-H3 | 768P / 2K |
The table above lists the currently available video models; for the complete, real-time list see the model market. Video models are not returned by the text-gateway GET /v1/models endpoint.
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. When you submit a public URL, this platform does not pre-screen pixels — downscale on your own side, or the failure will only surface at generation time. The upload endpoint does reject oversized files at upload 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 before it can be used for generation. Trust the usable field from the list endpoint — poll until usable is true before submitting a generation request, rather than guessing a fixed wait time.
Asset status: Processing → Active (processing finished) / Failed (processing failed).
⚠️ The usable field in this create response is always true. It only means "this asset was created on the current channel" and does not mean it is ready — in the example above status is still Processing.doubao-seedance-2.0 / -fast require waiting for Active. Use the usable value from the list endpoint with ?model=<model name> — it folds in both "sits on the right channel" and "is ready on that channel", which makes it more accurate than status alone.
When in doubt, call GET /videos/v1/videos/assets/capability?model=<model name> — its enabled field tells you directly whether that model can reference assets.
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.
Request format, status values, and content.video_url semantics match Ark native; the following are platform-specific behaviors:
- Address:
POST /videos/api/v3/contents/generations/tasks·GET …/tasks/{id} - Task ID: this platform returns a UUID; a task is queried on whichever endpoint it was submitted through
- Assets: reference images with a real person's likeness must first be registered as an asset on this platform (
asset://) — see the asset library - Callbacks:
callback_urlis not supported; pollGET …/tasks/{id}instead - Cancel / list: not yet supported (501)
- Output:
content.video_urlis the engine's direct link (~24 hours); this platform also returnscontent.platform_video_url(kept 7 days) andplatform_expires_at - Error codes: failed tasks use this platform's stable
error.codevalues (input_image_real_person/asset_unavailable/output_content_policy/content_safety_rejected/rate_limit_exceeded/upstream_generation_failed/task_not_found), with a human-readablemessage - Top-level parameters:
model/content/resolution/ratio/duration/generate_audio/seed/watermark/omni_reference_task_type/camera_fixed/frames; anything else returns 400 naming the offending parameter — never silently dropped - Inline prompt parameters (
--rs/--rt/--dur/--wm/--seed/--cf/--frames): this platform parses these and fills the matching field; giving both the inline value and the top-level field with different values returns 400 - Seedance 2.5 reference images: images with no
rolespecified are treated as reference-to-video (reference_image) and honor whatever aspect ratio you send; first/last-frame mode (first_frame/last_frame) requiresadaptiveupstream — this platform does not rewrite it for you; passadaptiveyourself, fixed ratios are rejected upstream
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. Do not put inline parameters such as --dur / --rs / --rt in the prompt — this endpoint rejects them (prompt_reserved_params); use request fields. Only the Ark-compatible endpoint parses inline parameters |
duration | integer | Seconds, defaults to 5. doubao-seedance-2.0 / -fast / doubao-seedance-2.5 / MiniMax-H3 all accept 4–15 and reject -1. Out-of-range values return 400 at submission time | |
resolution | string | Seedance family: 480p / 720p / 1080p, defaults to 720p, doubao-seedance-2.0-fast tops out at 720p; MiniMax-H3: 768p / 2k (lowercase on the wire, no 1080p), defaults to 768p. No model accepts 4k | |
aspect_ratio | string | 16:9 / 4:3 / 1:1 / 3:4 / 9:16 / 21:9 / adaptive. With a reference image, doubao-seedance-2.5 defaults to reference-to-video and honors whatever aspect ratio you send; only first/last-frame mode (generation_type: "first_and_last_frames") requires adaptive upstream (not rewritten by this platform; pass adaptive yourself) | |
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; cannot be used alone — combine with a reference image or a reference video | |
generate_audio | boolean | Whether to generate an audio track. Pass true for audio, false for silent; do not rely on a default | |
watermark | boolean | Whether to add a watermark | |
seed | integer | -1 to 4294967295 | |
generation_type | string | omni_reference (multimodal reference, the default reading) / first_and_last_frames (first/last frame; reference video and audio are ignored in this mode) | |
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>. With norole, this is treated as first-frame image-to-video; exception:doubao-seedance-2.5automatically addsreference_imageoutside first-and-last-frames mode (treated as reference-to-video) - An object:
{ "url": "...", "role": "reference_image" }, whererolemay befirst_frame/last_frame/reference_image. For multimodal reference, setroletoreference_imageexplicitly
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. Submitting again with the same key returns the first job, even if the body changed — it looks like success, but the video is the previous one. Use a new key for a new job. This endpoint does not return 409 when the body differs.
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 |
content_safety_rejected | 400 | The submission was rejected by the content-safety screen (it screens the prompt and reference media; no job was created) | Review the prompt and reference media and retry; the same content is occasionally rejected at random, so a single retry usually succeeds |
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 |
unsupported_resolution | 400 | This model does not support that resolution (for example 4k, or 1080p on the fast tier) | Use a resolution from the model table above |
frame_role_conflict | 400 | First/last-frame roles cannot be combined with a reference video | Drop one of the two; do not mix them |
prompt_reserved_params | 400 | The prompt contains inline parameters such as --dur / --rs (not parsed on this native endpoint) | Use duration / resolution request fields instead |
inline_param_conflict | 400 | Ark-compatible endpoint only: an inline prompt parameter disagrees with the top-level field | Keep one of the two |
audio_requires_visual | 400 | Reference audio cannot be used alone | Add a reference image or a reference video |
| 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 |
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: 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.