Video Generation
POST /v1/videos · GET /v1/videos/{id} · DELETE /v1/videos/{id}
Generate short videos from a text prompt, optionally steered by first/last reference frames. This is an asynchronous job API in the industry-standard create + poll shape: POST /v1/videos creates a task that returns in ~2 seconds, you poll it until it completes, and a Prefer: wait header turns fast generations into a single synchronous call. Seedance models, one API key, the same billing as every other endpoint.
The video API is in limited preview (invited testing). Models appear in the catalog and pricing, but creating tasks requires your workspace to be allowlisted — contact us to enable it for your workspace.
Endpoints & task lifecycle
One task resource, four operations. A task moves queued → in_progress → one of the terminal states completed, failed or cancelled. The create response carries ready-made status_url / cancel_url links, so you never assemble URLs by hand.
| Endpoint | Description |
|---|---|
POST /v1/videos | Create a generation task. Returns in ~2 s with the task object (status queued) plus polling links. |
GET /v1/videos/{id} | Poll the task. On completed you get data[0].url, usage.completion_tokens and the parameters that actually took effect; on failed a structured error with the provider’s code and message. |
DELETE /v1/videos/{id} | Cancel the task. Only queued tasks can be cancelled; cancelled tasks are never billed. |
GET /v1/videos/models | List the video models available to your key, with per-model resolution/duration constraints and prices. |
Synchronous sugar — Prefer: wait
Add a Prefer: wait or Prefer: wait=N header (N clamped to 1..60 seconds) to the create call and the gateway holds the request open. If the task finishes inside the window you get the terminal object straight back — video URL and usage included, no polling needed. If it does not finish in time, the call degrades gracefully: you get the current status object (no error, the task keeps running) and continue with normal polling.
Request Body
| Parameter | Type | Description |
|---|---|---|
model* | string | The video model to use (e.g. seedance-1-5-pro-251215). List all available models via GET /v1/videos/models. |
prompt* | string | Text description of the desired video. |
image | string | string[] | Image-to-video input: one image = first frame, two images = first + last frame. Each entry is a data URI or an https URL. |
resolution | string | Output resolution: 480p, 720p, 1080p or 4k. The supported set varies per model — see the model table. |
ratio | string | Aspect ratio: 16:9, 4:3, 1:1, 3:4, 9:16, 21:9 or adaptive. |
duration | integer | Clip length in seconds (per-model range within 2..15). Some models also accept -1 to let the model choose a length. |
seed | integer | Optional seed for reproducible output (where supported). |
watermark | boolean | Whether to render the provider watermark. |
generate_audio | boolean | Generate an audio track (default true). On models with split rates this changes the price — see the model table. |
Available models
Model availability depends on your deployment. Fetch the live, compliance-aware list — including each model’s resolution/duration constraints and prices — via GET /v1/videos/models. The current lineup:
| Model | Resolutions | Duration | Price | Notes |
|---|---|---|---|---|
dreamina-seedance-2-0-260128 | 480p · 720p · 1080p · 4k | 4–15 s | 480p/720p $7.0 · 1080p $7.7 · 4k $4.0 | 2.0 flagship; up to 4K |
dreamina-seedance-2-0-fast-260128 | 480p · 720p | 4–15 s | $5.6 | Faster 2.0 variant |
dreamina-seedance-2-0-mini-260615 | 480p · 720p | 4–15 s | $3.5 | Smallest 2.0 variant |
seedance-1-5-pro-251215 | 480p · 720p · 1080p | 4–12 s | $2.4 audio · $1.2 muted | Rate depends on generate_audio |
seedance-1-0-pro-fast-251015 | 480p · 720p · 1080p | 2–12 s | $1.0 | Cheapest |
Price: USD per 1M video tokens. seedance-1-5-pro-251215 bills $2.4 with the audio track on (generate_audio: true, the default) and $1.2 with it off; the other models bill one rate per resolution tier.
Video tokens & pricing
Video is billed by video tokens, derived from the actual output: tokens ≈ width × height × 24 fps × seconds / 1024. Rates are per 1M video tokens and follow the parameters that actually took effect (echoed back in the completed task) — so ratio: "adaptive" and duration: -1 are billed by what the model really produced.
Intuition anchors on seedance-1-5-pro-251215 with audio: a 480p / 16:9 / 4 s clip ≈ 40.6K video tokens ≈ $0.10; a 720p / 5 s clip ≈ 108.9K tokens ≈ $0.26.
Billing
Billed only on success: the charge lands exactly once, when the task first reaches completed, priced by the returned video-token usage. Failed tasks (including content-moderation rejections) and cancelled tasks are never billed — the provider’s error is passed through verbatim so you can see why.
The returned data[0].url is a pre-signed vendor URL that expires in ~24 hours — download promptly and re-host the file on your own storage if you need it long-term.
Example — curl, one shot (Prefer: wait)
curl https://synthorai.io/v1/videos \
-H "Authorization: Bearer $SYNTHORAI_API_KEY" \
-H "Content-Type: application/json" \
-H "Prefer: wait=60" \
-d '{
"model": "seedance-1-0-pro-fast-251015",
"prompt": "A corgi puppy chasing a butterfly across a meadow, cinematic",
"resolution": "480p",
"ratio": "16:9",
"duration": 4
}'
# Finished inside the wait window → the terminal object comes straight back:
{
"id": "vid_6091d8d1af805818b1470488",
"object": "video",
"model": "seedance-1-0-pro-fast-251015",
"status": "completed",
"data": [{ "url": "https://…tos….mp4?…" }],
"usage": { "completion_tokens": 40594, "total_tokens": 40594 },
"resolution": "480p", "ratio": "16:9", "duration": 4, "fps": 24,
"seed": 34142
}
# Not done in time → the current status object is returned instead (no error,
# the task keeps running) — continue polling status_url as usual. Example — curl, create + poll
# 1) Create — returns in ~2 s with a queued task object
curl https://synthorai.io/v1/videos \
-H "Authorization: Bearer $SYNTHORAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "seedance-1-5-pro-251215",
"prompt": "A corgi puppy chasing a butterfly across a meadow, cinematic",
"resolution": "720p",
"duration": 5,
"generate_audio": true
}'
{
"id": "vid_6091d8d1af805818b1470488",
"object": "video",
"model": "seedance-1-5-pro-251215",
"status": "queued",
"created_at": 1784619862,
"status_url": "https://synthorai.io/v1/videos/vid_6091d8d1af805818b1470488",
"cancel_url": "https://synthorai.io/v1/videos/vid_6091d8d1af805818b1470488"
}
# 2) Poll until status is terminal (completed | failed | cancelled)
curl https://synthorai.io/v1/videos/vid_6091d8d1af805818b1470488 \
-H "Authorization: Bearer $SYNTHORAI_API_KEY"
{
"id": "vid_6091d8d1af805818b1470488",
"object": "video",
"model": "seedance-1-5-pro-251215",
"status": "completed",
"data": [{ "url": "https://…tos….mp4?…" }],
"usage": { "completion_tokens": 108900, "total_tokens": 108900 },
"resolution": "720p", "ratio": "16:9", "duration": 5, "fps": 24,
"seed": 34142, "generate_audio": true
}
# 3) Download the video — the URL is pre-signed (no auth header) and expires
# in ~24 h: fetch promptly and re-host if you need it long-term.
curl -o out.mp4 "https://…tos….mp4?…" Example — Python (requests)
import time
import requests
BASE = "https://synthorai.io/v1"
HEADERS = {"Authorization": "Bearer YOUR_API_KEY"}
task = requests.post(f"{BASE}/videos", headers=HEADERS, json={
"model": "seedance-1-5-pro-251215",
"prompt": "A corgi puppy chasing a butterfly across a meadow, cinematic",
"resolution": "720p",
"duration": 5,
}).json()
while task["status"] not in ("completed", "failed", "cancelled"):
time.sleep(5)
task = requests.get(task["status_url"], headers=HEADERS).json()
if task["status"] == "completed":
url = task["data"][0]["url"] # pre-signed, valid ~24 h — re-host promptly
with open("out.mp4", "wb") as f:
f.write(requests.get(url).content)
else:
print(task["error"]) # provider code + message, passed through verbatim Example — Node (fetch)
import fs from "node:fs";
const BASE = "https://synthorai.io/v1";
const HEADERS = { Authorization: `Bearer ${process.env.SYNTHORAI_API_KEY}` };
let task = await (await fetch(`${BASE}/videos`, {
method: "POST",
headers: { ...HEADERS, "Content-Type": "application/json" },
body: JSON.stringify({
model: "seedance-1-5-pro-251215",
prompt: "A corgi puppy chasing a butterfly across a meadow, cinematic",
resolution: "720p",
duration: 5,
}),
})).json();
while (!["completed", "failed", "cancelled"].includes(task.status)) {
await new Promise((r) => setTimeout(r, 5000));
task = await (await fetch(task.status_url, { headers: HEADERS })).json();
}
if (task.status === "completed") {
const url = task.data[0].url; // pre-signed, valid ~24 h — re-host promptly
fs.writeFileSync("out.mp4", Buffer.from(await (await fetch(url)).arrayBuffer()));
} else {
console.error(task.error); // provider code + message, passed through verbatim
} Idempotency
Pass an X-Idempotency-Key header on the create call to make retries safe: a repeat request with the same key returns 409 instead of creating (and billing) a second task. Video generation is slow and retry-prone, so this prevents double charges.