Video

Video generation

Make videos with Seedance from a prompt, from a first frame (and optionally a last one), or from up to 9 reference images. Videos take a few minutes, so you start one and check back.

POST/v1/videos
ModelIDPrice per secondLength
Seedance 2.5seedance-2.5480p $0.14 · 720p $0.305 · 1080p $0.754 to 30 s
Seedance 2.0seedance-2.0480p $0.097 · 720p $0.2074 to 15 s
Seedance 2.0 Fastseedance-2.0-fast480p $0.074 · 720p $0.1594 to 15 s
Seedance 2.0 Miniseedance-2.0-mini480p $0.047 · 720p $0.104 to 15 s

Start, poll, download

import os, time, requests
API = "https://api.zurelay.com/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['ZURELAY_API_KEY']}"}
# 1. Start the video
response = requests.post(f"{API}/videos", headers=HEADERS, json={
"model": "seedance-2.0",
"prompt": "A paper boat drifting down a rain-soaked street at night, neon reflections, slow dolly shot",
"seconds": 5,
"resolution": "720p",
"aspect_ratio": "16:9",
})
response.raise_for_status() # a 4xx says what to fix
job = response.json()
# 2. Poll until it's done (usually 2 to 6 minutes)
while job["status"] in ("queued", "in_progress"):
time.sleep(10)
job = requests.get(f"{API}/videos/{job['id']}", headers=HEADERS).json()
if job["status"] == "failed":
raise SystemExit(job["error"]["message"])
# 3. Download the MP4
video = requests.get(f"{API}/videos/{job['id']}/content", headers=HEADERS)
open("boat.mp4", "wb").write(video.content)

Parameters

modelstringrequired
A video model ID.
promptstring
What happens, up to 8,000 characters. Required unless you give a first frame or reference images.
secondsinteger
Length, within the model’s range (see the table). Default 5.
resolutionstring
480p, 720p or 1080p, as the model offers. Default 720p.
aspect_ratiostring
16:9, 9:16, 1:1, 4:3, 3:4, 21:9, or adaptive (the first frame’s shape; the default when you give one). Otherwise the default is 16:9.
sizestring
Instead of resolution and ratio: pixels like 1280x720.
generate_audioboolean
Sound and speech made with the video. Default true. (audio works too.)
seedinteger
The same seed and settings give similar results.
first_framestring
An image the video starts on: a link or a data URL.
last_framestring
An image it ends on. Needs a first frame.
reference_imagesarray
Up to 9 images of characters, products, places or a style to use. Not with frames.

Starting from images

First and last frame

Animate a still: the video opens exactly on first_frame. Add last_frame and it lands on that image, so you control both ends of the shot.

Request body
{
"model": "seedance-2.0",
"first_frame": "https://example.com/storefront-day.jpg",
"last_frame": "https://example.com/storefront-night.jpg",
"prompt": "Time passes from afternoon to night; lights come on inside the shop.",
"seconds": 6,
"resolution": "720p"
}

Reference images

Give up to 9 images and mention them in the prompt by order (@Image1, @Image2…): the people, products and places in them appear in the video.

Request body
{
"model": "seedance-2.5",
"reference_images": [
"https://example.com/chef.jpg",
"https://example.com/kitchen.jpg",
"https://example.com/dish.jpg"
],
"prompt": "The chef from @Image1 plates the dish from @Image3 in the kitchen from @Image2, close-up, warm light.",
"seconds": 8,
"resolution": "720p",
"aspect_ratio": "9:16"
}

Rules for images

  • PNG, JPEG or WebP, as a public https link or a data URL, up to 10 MB.
  • 300 to 6000 pixels on each side, and no wider or taller than 5:2. Data URLs outside these are refused up front with a message saying why. Links are read when the video starts: one that isn’t a usable image fails the video with invalid_image, free of charge.
  • Frames or reference images, not both in one request. Reference videos and audio aren’t supported.
  • Requests are JSON: images go in as links or data URLs, not as multipart uploads.

The video object

GET /v1/videos/video_abc123
{
"id": "video_abc123",
"object": "video",
"model": "seedance-2.0",
"status": "completed",
"progress": 100,
"created_at": 1790870400,
"completed_at": 1790870562,
"expires_at": 1793462400,
"seconds": 5,
"resolution": "720p",
"aspect_ratio": "16:9",
"size": "1280x720",
"generate_audio": true,
"url": "https://...signed link...",
"url_expires_at": 1790956962,
"cost": 1.035,
"error": null
}
FieldMeans
statusqueued, in_progress, completed or failed.
progressAn estimate from 0 to 100, from how long the model usually takes.
urlThe MP4, once completed. The link works for 24 hours; fetch the job again for a fresh one.
expires_atWhen the video is deleted: 30 days after it was made.
costWhat it cost, once completed.
errorFor failed videos: code (content_policy, invalid_image, generation_failed, or generation_timeout after 90 minutes) and a message.

Other endpoints

EndpointReturns
GET /v1/videos/{id}One video, as above.
GET /v1/videos/{id}/contentThe MP4 itself.
GET /v1/videos?limit=20&after=video_abcYour workspace’s videos, newest first, with first_id and last_id for paging.

Paying for video

A video costs its price per second (by resolution) times its length. When you start one, its price is set aside from your balance; it’s charged when the video is done, and released if it fails. A failed video is free.

Draft at 480p and a short length, then make the final cut at 720p or 1080p once the prompt is right. Most of the wait is the queue, so drafts aren’t much faster, but they cost a fraction.

Questions, or something missing? Ask support in your dashboard or email support@zurelay.com.