Developer Guide · Video Model API

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.

Step 1: submit a generation job
curl example · Submit a job
curl https://tryaiapi.com/videos/v1/videos/generations \ -H "Authorization: Bearer <your API key>" \ -H "Content-Type: application/json" \ -d '{ "model": "doubao-seedance-2.0", "prompt": "A sunrise over the ocean, camera slowly pushing in", "resolution": "720p", "duration": 5, "aspect_ratio": "16:9" }'

The response's status starts as queued, then moves to running; poll_url is the address you poll next:

Response example · Submission accepted
{ "job_id": "...", "status": "queued", "poll_url": "https://tryaiapi.com/videos/v1/videos/jobs/<job_id>", "created_at": "2026-08-22T10:03:11Z" }
(Optional) get an estimate before you submit

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.

Response example · Estimate (doubao-seedance-2.0 · 720p · 5s)
{ "model": "doubao-seedance-2.0", "estimated_cost": 0.8132, "estimated_cost_cny": 5.69, "fx_cny_per_usd": 7, "currency": "USD", "basis": "pre_hold", "final_cost_may_adjust": true }
Step 2: poll job status

Take the poll_url from the previous response and request it as-is:

curl example · Poll
curl <poll_url> \ -H "Authorization: Bearer <your API key>"
Step 3: download the result

Once status becomes completed, video_url is a direct, downloadable link to the video:

Response example · Completed, ready to download
{ "job_id": "...", "status": "completed", "model": "doubao-seedance-2.0", "video_url": "<the generation engine's own direct link, valid for about 24 hours>", "platform_video_url": null, "expires_at": "2026-08-23T10:05:40Z", "cost_final": 0.81, "duration_ms": 118342, "metadata": { "duration": 5, "resolution": "720p" } }

⚠️ video_url expires after about 24 hours — download and save it promptly; do not rely on it staying reachable long-term.

Currently available video models
ModelCall name (use this in the model field)Supported resolutions
Seedance 2.0doubao-seedance-2.0480p / 720p / 1080p
Seedance 2.0 Fastdoubao-seedance-2.0-fast480p / 720p
Wan 3.0wan3.0-video480p / 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

Why it exists

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.

When you must use it

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.

Three steps
  • 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, write asset://<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.

(Optional) check whether a model supports the asset library before integrating
curl example · Check capability
BASE="https://tryaiapi.com" curl "$BASE/videos/v1/videos/assets/capability?model=doubao-seedance-2.0" \ -H "Authorization: Bearer <your API key>"
Response example
{ "enabled": true, "upload_max_bytes": 31457280, "upload_max_pixels": 36000000 }
Step 1: create an asset
curl example · Create an asset
BASE="https://tryaiapi.com" curl "$BASE/videos/v1/videos/assets" \ -H "Authorization: Bearer <your API key>" \ -H "Content-Type: application/json" \ -d '{ "url": "<your public image link>", "name": "front-desk-alex" }'
Response example
{ "id": "...", "asset_id": "...", "ref": "asset://<asset_id>", "name": "front-desk-alex", "status": "Processing", "usable": true, "source_url": "<your public image link>", "created_at": "2026-08-22T10:00:02Z" }

Asset status: ProcessingActive (usable for generation) / Failed (processing failed). Only Active assets can be referenced in a generation request.

Step 2: reference it 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:

curl example · Generate using an asset reference
curl https://tryaiapi.com/videos/v1/videos/generations \ -H "Authorization: Bearer <your API key>" \ -H "Content-Type: application/json" \ -d '{ "model": "doubao-seedance-2.0", "prompt": "This person walks into the store and greets the camera with a smile", "image_urls": ["asset://<asset_id>"], "resolution": "720p", "duration": 5 }'

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):

Error response shape
{ "error": { "code": "stable code", "message": "human-readable description", "type": "error category", "field": "the offending field (optional)", "details": { } } }

Please branch on code, not on the message text — copy may change, code is stable.

5.1 Submit a generation job · POST /videos/v1/videos/generations
FieldTypeRequiredDescription
modelstringModel name, ≤100 characters
promptstringPrompt text, ≤2000 characters
durationintegerSeconds, 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)
resolutionstring480p / 720p / 1080p / 4k, defaults to 720p
aspect_ratiostring16:9 / 4:3 / 1:1 / 3:4 / 9:16 / 21:9 / adaptive
image_urlsarrayReference images, up to 9 items; two accepted forms below
video_urlsstring[]Reference videos, up to 3 items; asset references are not accepted here
audio_urlsstring[]Reference audio, up to 3 items; asset references are not accepted here
generate_audiobooleanWhether to generate an audio track
watermarkboolean
seedinteger-1 to 4294967295
generation_typestringomni_reference / first_and_last_frames
callback_url / callback_secretNot 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" }, where role may be first_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:

Response example · Submission accepted
{ "job_id": "...", "status": "queued", "poll_url": "https://tryaiapi.com/videos/v1/videos/jobs/<job_id>", "created_at": "2026-08-21T..." }
5.2 Estimate before submitting · POST /videos/v1/videos/generations/estimate

Same request fields as 5.1. It does not create a job, charge you, or trigger generation.

Response fieldDescription
estimated_costEstimated cost (USD)
estimated_cost_cnyCNY-converted amount (reference only)
fx_cny_per_usdDisplay exchange rate
currencyUSD (the billing currency of record)
final_cost_may_adjusttrue — the final settled amount may differ slightly
5.3 Query a job · GET /videos/v1/videos/jobs/{job_id}
FieldDescription
statusSee the status table below
status_notePresent only when there is something worth explaining (a short human-readable note)
video_urlThe generation engine's own direct link, valid for about 24 hours
expires_atExpiry time of the link above
platform_video_urlThis platform's own copy; defaults to null (not produced unless the 7-day copy is enabled)
platform_expires_atExpiry 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_finalHeld / settled amount (USD)
duration_msWall-clock time from submission to completion
metadataduration / resolution / ratio / seed / usage, etc.

Job status values:

ValueMeaning
queuedAccepted, waiting in the queue
runningGenerating
completedDone — ready to download
failedFailed (the hold is auto-refunded)
cancelledCancelled
timeoutTimed 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.

5.4 List jobs · GET /videos/v1/videos/jobs
ParameterDescription
page / page_sizePage number (≥1) / items per page (1–200, default 20)
statusFilter by status, values as in the table above
start_date / end_dateYYYY-MM-DD, both inclusive
api_key_idFilter by key

Responds with { items: [...], total, page, page_size }, sorted newest-first by submission time (not adjustable).

5.5 Usage stats · GET /videos/v1/videos/jobs/stats

Same filter parameters as 5.4. Returns total_jobs / completed_jobs / failed_jobs / in_flight_jobs / total_cost (completed jobs only) / avg_duration_ms.

5.6 Export · GET /videos/v1/videos/jobs/export.csv

Same filter parameters as 5.4. ⚠️ A single export is capped at 200 rows — for more, page through 5.4 and aggregate on your side.

5.7 Asset library

Create an asset POST /videos/v1/videos/assets

FieldTypeRequiredDescription
urlstringAn http(s) image link, ≤2048 characters
namestring≤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.

5.8 Upload endpoint (video workbench only — a platform integration usually doesn't need it)

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.

6.1 Something your users need to know and resolve themselves (tell them the failure reason plainly)
codeHTTPMeaningTell the user
input_image_real_person400A reference image may contain a real personThis image needs to be added to the asset library first
input_image_too_large400Image exceeds the pixel limitUse a smaller image (the actual size and the limit are in details)
output_content_policy400The generated content may involve copyright or sensitive materialAdjust the reference images or prompt and retry
reference_media_unfetchable400A reference image/video could not be readCheck whether the link is publicly reachable and not too slow
invalid_asset_reference_format400The 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_reference400The referenced asset isn't in your library, or isn't usable on that model's channelCreate the asset again
asset_unavailable400The asset isn't ready yetWait until it becomes Active, then resubmit
asset_name_taken / asset_name_reserved / asset_name_too_long400Asset naming issuePick a different name
asset_library_full400Your asset library has hit its capDelete unused assets
6.2 Handle these yourself — no need to bother your users
codeHTTPMeaningSuggested action
invalid_api_key401/403Invalid key, or the account is disabledCheck your configuration; contact us
ip_not_allowed403The key is restricted to an IP range and the current source isn't in itContact us to adjust it
insufficient_credits402Insufficient account balanceTop up; we recommend building your own low-balance alert
idempotency_key_reused409The same idempotency key was reused with different request contentUse a new key
model_not_found404Model name doesn't exist or isn't enabledCheck the model name
task_not_found404Job doesn't exist, or doesn't belong to youCheck the job ID
upload_quota_exceeded429Upload quota reachedA platform integration shouldn't be using the upload endpoint — see "Reference images"
rate_limit_exceeded429Requests are coming in too fastBack off and retry
upstream_channel_unavailable502The generation channel is temporarily unavailableBack off and retry
upstream_timeout504Generation timed out (the hold is auto-refunded)Retryable
upstream_generation_failed502/503Failure on the generation provider's side (the hold is auto-refunded)Back off and retry
submission_result_ambiguous502The submission result could not be confirmed🔴 Contact us first — do not retry blindly
internal_error500Internal error on this platformContact us
6.3 Retry guidance
  • 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.

When this section applies to you

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
Your users never call this API directly — isolation is on you

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

ItemLimitScope
Prompt length2000 charactersPer request
Reference images9Per request
Reference videos / audio3 eachPer request
Pixels per image36 million (width × height)Per image
File size per image30 MBPer image
Total request body size1 MB (excluding the upload endpoint)Per request
Asset library capacity100,000Per account (not per key)
CSV export200 rowsPer 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?
Three common reasons — it contains a real person (use the asset library, see "Asset library: the real-person channel"); it exceeds the pixel limit (downscale first, see "Reference images"); or the content involves copyright or sensitive material. The error message tells you which one.
Why doesn't @Alex work?
Over the API you must write @图片1 — see "Naming people & mention syntax".
My users can see each other's assets — what do I do?
See "Tenancy & isolation boundaries" — you need to implement isolation in your own layer.
How long does a job take?
Turnaround varies with the model, duration, and scene complexity — submitting one job is the most direct way to see it for yourself. Poll the response's poll_url asynchronously; do not wait synchronously.
Can I cancel a job?
Not currently supported.

Want more integration detail? Contact [email protected] and we'll confirm the integration details with you as soon as we can.