Video generation
primerouter offers Seedance video generation: text-to-video, first/last-frame video, and video driven by reference images, reference videos or reference audio. Tasks are asynchronous: submit to get a task ID, poll for progress, and download the video once it completes.
You need a key in the "Seedance" group
Video models are only in the Seedance group. Create (or edit) an API key in the console, set its group to "Seedance", and use it for the endpoints below.
| Endpoint | Purpose |
|---|---|
POST /v1/video/generations | Submit a task |
POST /v1/videos | Submit a task (same as above, OpenAI video path) |
GET /v1/video/generations/{task_id} | Query a task (generic format) |
GET /v1/videos/{task_id} | Query a task (OpenAI video format) |
The two submit endpoints are equivalent; both take a JSON body.
Models
| Model | Length | Resolutions |
|---|---|---|
seedance-2.0 | 4–15 s | 480P 720P 1080P 2K 4K |
seedance-2.5 | 4–30 s | 480P 720P 1080P 2K 4K |
Text to video
curl https://primerouter.ai/v1/video/generations \
-H "Authorization: Bearer $PRIMEROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "seedance-2.0",
"prompt": "A red paper boat drifts across a calm lake at sunrise, slow push-in",
"duration": 5,
"metadata": { "resolution": "1080P", "ratio": "16:9" }
}'Response:
{
"id": "task_xxxxxxxx",
"task_id": "task_xxxxxxxx",
"object": "video",
"model": "seedance-2.0",
"status": "queued",
"progress": 0,
"created_at": 1761484800
}Keep the id to query the task.
First and last frame
The first entry of images is the first frame, the second the last frame (one entry gives a first frame only; image takes a single URL too):
curl https://primerouter.ai/v1/video/generations \
-H "Authorization: Bearer $PRIMEROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "seedance-2.5",
"prompt": "The bud slowly opens into a full bloom",
"images": ["https://example.com/first.png", "https://example.com/last.png"],
"duration": 8,
"metadata": { "resolution": "720P" }
}'Reference images, videos and audio
Put reference media in the metadata.content array, each item with its type and role:
curl https://primerouter.ai/v1/video/generations \
-H "Authorization: Bearer $PRIMEROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "seedance-2.0",
"prompt": "Follow the camera move of the reference video and walk the character in",
"duration": 6,
"metadata": {
"resolution": "720P",
"content": [
{ "type": "image_url", "image_url": { "url": "https://example.com/character.png" }, "role": "reference_image" },
{ "type": "video_url", "video_url": { "url": "https://example.com/camera-move.mp4" }, "role": "reference_video" }
]
}
}'typemust beimage_url,video_urloraudio_url(textitems are ignored; put text inprompt)roleisreference_image,reference_videoorreference_audio- Reference audio cannot be used alone: add a reference image or reference video, or the task fails
- Reference videos may total at most 15 seconds
Query a task
curl https://primerouter.ai/v1/videos/task_xxxxxxxx \
-H "Authorization: Bearer $PRIMEROUTER_API_KEY"status goes queued → in_progress → completed or failed. Right after submitting, before progress is synced, status may be unknown: treat it as in progress and keep polling. When completed, the video link is in metadata.url:
{
"id": "task_xxxxxxxx",
"object": "video",
"model": "seedance-2.0",
"status": "completed",
"progress": 100,
"metadata": { "url": "https://...mp4?..." }
}On failure, error.message gives the reason.
With the generic format GET /v1/video/generations/{task_id}, the status is in data.status (QUEUED / IN_PROGRESS / SUCCESS / FAILURE; right after submitting it may be NOT_START, which also means in progress), the video link in data.result_url and the failure reason in data.fail_reason.
Most tasks finish in 2–5 minutes; polling every 15–30 seconds is enough.
Download within 12 hours
The download link is temporary. Download and keep the video within 12 hours; it cannot be fetched again after that.
Request parameters
| Field | Description |
|---|---|
model | seedance-2.0 or seedance-2.5, required |
prompt | Prompt, required |
duration | Length in seconds (integer). Defaults to 5; -1 lets the model choose the length |
seconds | Another way to give the length (string, e.g. "8"); duration wins |
image / images | First / last frame image URLs, at most 2 |
metadata.resolution | 480P 720P 1080P 2K 4K, upper case; defaults to 720P |
metadata.ratio | 21:9 16:9 4:3 1:1 3:4 9:16 adaptive |
metadata.duration | A third way to give the length (integer); used only when neither duration nor seconds is set |
metadata.content | Reference images / videos / audio, see above |
metadata.options | Only return_last_frame is accepted (the last frame image is not returned in the result yet; passing it does not affect generation) |
Notes:
- Resolution and length come from these parameters only; writing them in the prompt (e.g.
--rs 1080p --dur 10) has no effect - Media URLs must be publicly reachable
http/httpslinks. If a link cannot be opened, the task fails (not charged) - Images with real human faces may be rejected and the task fails (not charged)
- Other
metadatafields (such asseedorwatermark) have no effect - Form uploads (
multipart/form-data) andinput_referenceare not supported; host the media and pass its URL
Errors
These errors come back on submit as HTTP 400 and are not charged:
| Error message | Cause |
|---|---|
only JSON requests with media URLs are supported | A form upload, or input_reference was set |
images accepts at most 2 entries (first and last frame); put reference images in metadata.content | More than 2 images; put reference images in metadata.content |
metadata.options only supports return_last_frame | metadata.options has another field |
metadata.content items must be image_url, video_url, audio_url or text | metadata.content has an unsupported type |
media URLs must use http or https | A media link is not http / https |
model <name> is not available for video generation | The model cannot be used for video generation; use a model from the table above or contact the administrator |
model <name> has no price configured, please contact the administrator | The model has no price yet; contact the administrator |
A resolution, ratio or length outside the supported values (e.g. lower-case 1080p, or 20 seconds on seedance-2.0) is also rejected on submit with a 400 and not charged.
Billing
- Billed per second: the price depends on the model and output resolution, times the video length
- With a reference video, its seconds are billed too (at the "with reference video" rate of the same resolution; input and output each billed)
- Failed generations are not charged
- On submit, the most the request could cost is held (e.g.
duration: -1holds the model's longest length); once the video is done, the difference is refunded or charged to match actual usage - Per-second prices for each resolution are on the pricing page, "Seedance" tab, group ratio included
