doubao-seedance-2.5-face
ByteDance Seedance 2.5 — async video generation up to 30 seconds with native audio on reAPI. Full parameter reference, request modes, and billing dimensions.
ByteDance's next-generation async video model, live on reAPI. Seedance 2.5
turns text, photos, clips, or audio into video up to 30 seconds long, with
generated speech, sound effects and music. Like the rest of the Seedance
family, mode is implicit: which media fields you set (prompt,
image_urls, image_with_roles, video_urls, audio_urls) decides whether
the request runs as text-to-video, image-to-video, first/last-frame
transition, or reference-driven generation. See pricing on the
model page.
Quick example
curl https://reapi.ai/api/v1/videos/generations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "doubao-seedance-2.5-face",
"prompt": "A kitten yawning at the camera, cinematic warm tones",
"resolution": "720p",
"size": "16:9",
"duration": 5
}'import requests
resp = requests.post(
"https://reapi.ai/api/v1/videos/generations",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"model": "doubao-seedance-2.5-face",
"prompt": "A kitten yawning at the camera, cinematic warm tones",
"resolution": "720p",
"size": "16:9",
"duration": 5,
},
timeout=30,
)
print(resp.json())const r = await fetch("https://reapi.ai/api/v1/videos/generations", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "doubao-seedance-2.5-face",
prompt: "A kitten yawning at the camera, cinematic warm tones",
resolution: "720p",
size: "16:9",
duration: 5,
}),
});
console.log(await r.json());package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "doubao-seedance-2.5-face",
"prompt": "A kitten yawning at the camera, cinematic warm tones",
"resolution": "720p",
"size": "16:9",
"duration": 5,
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/videos/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}Authentication
Every call needs a Bearer token. Generate keys at reapi.ai/settings/apikeys.
Authorization: Bearer YOUR_API_KEYKeys carry the active workspace's billing scope — there is no separate project header.
Endpoint
POST /api/v1/videos/generations
GET /api/v1/tasks/{id}Submission is async. The POST returns immediately with a task_id; the task
endpoint returns the same envelope until completion. Polling does not consume
credits.
Variants
Two variants, one parameter shape. Pick via model:
| Model id | Resolutions |
|---|---|
doubao-seedance-2.5-face | 480p / 720p / 1080p |
doubao-seedance-2.5-eco | 720p / 1080p |
doubao-seedance-2.5-eco is the economy tier: the same request body and the
same modes documented below, starting at 720p rather than 480p. It bills
on its own per-second rates — see the
model page for the current numbers.
Picture quality on the economy tier may be below the standard variant. The
price reflects that. Generation also takes roughly 50–100 s longer than the standard tier. That is a typical range, not a guarantee. Everything else is identical:
content_filter, duration: -1, omni_reference_task_type, reference media,
output_format, the response envelope and the error codes.
reAPI never silently substitutes one variant for another: resolution: "480p"
on the economy tier returns 400, never an auto-substituted clip.
Request body
Mode is implicit — which media fields you set decides text-to-video,
image-to-video, first/last-frame, or reference-driven generation. Audio-only
reference (just audio_urls) is supported, and reference images / videos may
contain real people.
| Field | Type | Default | Notes |
|---|---|---|---|
model | string | — | doubao-seedance-2.5-face or doubao-seedance-2.5-eco. Required. |
prompt | string | — | Describe the video, up to 20000 chars. Optional when any reference material is present (with content_filter: true); required for pure text-to-video and whenever content_filter is false. Wrap spoken lines in double quotes to steer the generated speech. |
duration | integer | 5 | Output length in seconds: 4–30, or -1 to let the model pick the best length (required for video-edit prompts — see Task types). -1 reserves credits at the 30 s cap and settles to the actual output length once the video is ready — the difference is refunded automatically. |
size | string | adaptive | 16:9 / 9:16 / 1:1 / 4:3 / 3:4 / 21:9 / adaptive. Locked to adaptive in three cases: a first_frame/last_frame entry in image_with_roles (output follows the first frame), and omni_reference_task_type of edit or extend (output keeps the source clip's shape). Plain reference images place no restriction — pick any ratio. |
resolution | string | 720p | 480p / 720p / 1080p. The economy tier starts at 720p — see Variants. |
bitrate_mode | enum | "default" | default: no processing; standard: smaller files; high: lighter compression. No extra generation charge. |
generate_audio | boolean | true | Generate synced speech, sound effects and background music (mono). |
output_format | string | mp4 | mp4 / mov. mov uses high color-precision encoding (H.264 4:4:4, video/quicktime) for pro post-production; some players can't play it. Delivered URLs end in .mov and are served as video/quicktime. |
omni_reference_task_type | string | auto | auto / reference / edit / extend. Declares which reference job this is so its rules are checked at submit instead of mid-run — see Task types. Declaring edit also makes duration default to -1, so you can simply omit it. |
watermark | boolean | false | Stamp an "AI generated" mark in the bottom-right corner. |
return_last_frame | boolean | false | Return output.last_frame_url for continuous chaining. |
seed | integer | — | Random seed. Sent upstream on every request; the same seed gives a similar result but is not guaranteed to be byte-identical. Omitted → random. |
tools | object[] | — | [{ "type": "web_search" }] to let the model query the web. |
image_urls | string[] | — | Up to 30 reference images (jpeg / png / webp / bmp / tiff / gif / heic / heif, each < 30 MB). Public HTTP(S) URLs. |
image_with_roles | object[] | — | Images with an explicit role: { url, role } where role is first_frame, last_frame, or reference_image. At most one first_frame and one last_frame (they may be the same image); up to 30 entries in total. May be combined with video_urls / audio_urls — the upstream then treats the frames as reference images (multimodal reference job). |
video_urls | string[] | — | Up to 10 reference clips (mp4 / mov, 480p–4K, each ≤ 200 MB), each 2–30 s, combined ≤ 30 s. |
audio_urls | string[] | — | Up to 10 reference audio tracks (wav / mp3, each ≤ 15 MB), each 2–30 s, combined ≤ 30 s. |
content_filter | boolean | true | Safety checking. Set false to run the task on the unmoderated route — see content_filter below. |
Checked before anything is submitted or charged:
- at least one of
promptor a reference field must be present image_urlsandimage_with_rolesare mutually exclusive (both carry reference images — pick one)- at most one
first_frameand onelast_frame, and alast_framealways needs afirst_frame(there is no last-frame-only task type) - frame roles and
reference_imagecannot share oneimage_with_rolesarray — they are separate task types; put reference images inimage_urls - a
first_frame/last_frameentry forcessizetoadaptive - reference clips: each 2–30 s, 10 max, 30 s combined; reference audio: each 2–30 s, 10 max, 30 s combined
- whatever
omni_reference_task_typeyou declare, its own rules (below)
image_with_roles can be combined with video_urls / audio_urls: the
upstream converts the frames to reference images and runs a multimodal
reference job.
bitrate_mode — enum, default "default"
Available across Seedance 2.0, Fast, Mini, and 2.5, including all channels. This is reAPI output processing after generation.
"default"or omitted: return the original file without processing or re-encoding. This is the default setting."standard": compress for smaller files and faster downloads."high": apply lighter compression to retain more detail thanstandard, usually producing a larger file. It does not enhance the original video.
Processing preserves resolution, frame timing, audio, and the MP4/MOV container. If encoding would increase file size, the original file is kept. The generation charge is unchanged. Processing adds time and supports source files up to 1 GiB; savings vary by video.
Legacy booleans remain accepted: false maps to "default", and true maps
to "standard". Other values return 400 before charging. Processing or
storage failures follow the existing task failure/refund policy; an
unprocessed source URL is not silently substituted after a processing failure.
{ "bitrate_mode": "default" }content_filter — boolean, default true
With the default (true) the request runs on the moderated route: prompts,
reference material and output are checked, and a rejection fails the task with
a refund.
Set content_filter: false to run the same request on the unmoderated route
instead. What changes:
- API keys only. A website session that sends
content_filter: falseis rejected with400before anything is charged; the playground always runs moderated. promptbecomes required in every mode, including reference jobs.- Same parameters, same price. Every field, rule and rate in this document applies unchanged — which channel served the request is internal.
- Output lands in an isolated bucket and its URLs stay valid for 30 days — mirror the files if you need them longer.
Platform terms apply to everything you generate on either route.
Task types and constraints
Seedance 2.5 does more than generate new clips: give it a source video and it can also edit that video or extend it. Which job you get is decided by the model from your reference material and the wording of your prompt.
Three of these jobs carry hard parameter rules:
| Job | What it does | Needs | Hard rules |
|---|---|---|---|
| Text-to-video | new clip from a prompt | prompt | none |
| First / last frame | animates between given frames | image_with_roles | size must be adaptive |
Reference (reference) | new clip guided by your images / clips / audio | any reference field | none — any ratio, any duration |
Edit (edit) | changes the content of a source clip | ≥1 video_urls, source 4–30 s | duration must be -1, size must be adaptive |
Extend (extend) | continues a source clip forwards or backwards | ≥1 video_urls | size must be adaptive |
Declare the job and get errors for free
By default (omni_reference_task_type: "auto") the model reads your prompt and
picks the job itself — and it only discovers a rule violation after the task
is created and credits are reserved. You get a late failure and a refund.
Set the field explicitly and the rules are checked while you are still holding the HTTP connection:
{
"model": "doubao-seedance-2.5-face",
"prompt": "remove the parked car on the left",
"video_urls": ["https://example.com/street.mp4"],
"omni_reference_task_type": "edit"
}duration is omitted on purpose: an edit keeps the source clip's length, so
declaring edit sets it to -1 for you. Passing a fixed duration alongside
edit is rejected — by us and by the upstream.
auto (default) | declared explicitly | |
|---|---|---|
Wrong duration on an edit | task starts, fails minutes later, refunded | HTTP 400 at submit, no task, nothing charged |
| Feedback speed | after generation | immediate |
Declaring the job does not force it. The model still reads your prompt, and if
its own classification disagrees with what you declared the task fails with a
task-type mismatch. Write the prompt to match the job you asked for — an edit
prompt should describe a change to the existing footage ("remove the car",
"make it night"), not a brand-new scene.
Leaving the field out keeps the previous behaviour exactly, so existing integrations need no changes.
Troubleshooting
| Failure | What it means | What to do |
|---|---|---|
| Synchronous 400 on submit | A field violates the schema (bad resolution, size with first/last frames, missing prompt on text-to-video, …) | The error names the field — fix and resubmit; nothing was charged |
Synchronous 400 naming a format (… format matroska is not supported) | A reference file is not in a format the model accepts — see Reference media formats. The check reads the file itself, so a renamed extension does not help | Re-encode: images to jpeg / png / webp, clips to H.264 mp4, audio to wav / mp3 |
| Task fails with a parameter error after starting | The model classified your prompt as a video edit/extend task and the parameters don't match its rules (see the table above) | Set duration: -1 for edits, keep size adaptive, or remove edit/extend wording; the reserve is fully refunded. To turn this into an instant 400 next time, declare omni_reference_task_type |
| Task fails with a task-type mismatch | You declared omni_reference_task_type but the model read your prompt as a different job | Match the prompt to the job — an edit prompt must describe a change to the existing footage, not a new scene |
| Task fails citing sensitive content | Reference material or the generated output was rejected by moderation | Fully refunded. Real-person references are supported and reviewed automatically — persistent rejections mean the material itself violates content policy |
402 on submit | Not enough credits for the reserve (auto-duration reserves at the 30 s cap before settling down) | Top up, or set an explicit shorter duration |
Task stays processing | Normal for up to a few minutes — generation averages 2–5 min depending on length and references | Keep polling; tasks that can never finish are failed and refunded automatically |
No data: URIs. reAPI rejects base64 inputs platform-wide — every URL
field must be a public HTTP(S) URL. Upload to your own object storage (S3, R2,
OSS, …) and pass the URL.
Response envelope
Submit and poll share the same shape — only status and output fill in over
time.
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "doubao-seedance-2.5-face",
"status": "completed",
"created_at": 1735000000,
"output": {
"video_urls": ["https://cdn.reapi.ai/media/tasks/.../0.mp4"],
"last_frame_url": "https://cdn.reapi.ai/media/tasks/.../0.png"
},
"error": null
}Poll GET /api/v1/tasks/{id} (see the Tasks reference) until
status === "completed". output.video_urls holds the generated video URL
(mp4, or mov when output_format: "mov" was set).
output.last_frame_url is present when the request set return_last_frame: true.
Pricing
Seedance 2.5 bills per second of video, at a rate that depends on the resolution and on whether you supplied a reference video:
credits = ceil(per_second_rate × billable_seconds × 1000) 1 credit = $0.001Live per-second rates are on the model page — that table is generated from the current price, so it is always the authoritative number.
The part worth reading is billable_seconds, because three rules make it
differ from the duration you asked for. Failed jobs are always refunded in
full.
1. A reference video is billed too, on top of the output
Send video_urls and you pay for the source footage plus the generated
clip:
billable_seconds = ceil(total_length_of_video_urls) + durationreAPI measures every reference clip server-side, so this is the real length of the file, not a value you declare. Reference images and audio are free — they add no seconds.
A 6-second source clip with duration: 5 bills 11 seconds, not 5.
2. Video-input requests have a minimum
When the input contains a video, the upstream charges a floor of
⌈5 × duration / 3⌉ seconds however short your source clip is. You pay the
larger of the two numbers:
billable_seconds = max( ceil(reference_seconds) + duration, ceil(5 × duration / 3) )duration | reference clip | input + output | floor | you pay |
|---|---|---|---|---|
| 5 s | 2 s | 7 s | 9 s | 9 s |
| 5 s | 6 s | 11 s | 9 s | 11 s |
| 10 s | 3 s | 13 s | 17 s | 17 s |
| 10 s | 12 s | 22 s | 17 s | 22 s |
Short source clips are where this bites: a 2-second reference with a 5-second output costs the same as a 9-second job. Longer sources pass the floor and pay their real length.
3. duration: -1 reserves at 30 seconds, then refunds
-1 asks the model to choose the length, so there is no number to price at
submit. reAPI reserves credits at the 30-second cap and settles to the real
length once the video is delivered — the difference is refunded automatically.
This is why a -1 request can return 402 on an account that could easily
afford the video it actually produces: the reserve, not the final price, has to
fit. Set an explicit duration if you need a tighter hold.
Both other rules still apply to -1: with a reference video the reserve is
computed from 30 seconds of output, and the settled charge is recomputed from
the delivered length.