GPT Image 2.5 is live — OpenAI's newest image model, targeted edits that leave the rest of the frame alone
rreAPI Docs

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_KEY

Keys 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 idResolutions
doubao-seedance-2.5-face480p / 720p / 1080p
doubao-seedance-2.5-eco720p / 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.

FieldTypeDefaultNotes
modelstringdoubao-seedance-2.5-face or doubao-seedance-2.5-eco. Required.
promptstringDescribe 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.
durationinteger5Output length in seconds: 430, 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.
sizestringadaptive16: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.
resolutionstring720p480p / 720p / 1080p. The economy tier starts at 720p — see Variants.
bitrate_modeenum"default"default: no processing; standard: smaller files; high: lighter compression. No extra generation charge.
generate_audiobooleantrueGenerate synced speech, sound effects and background music (mono).
output_formatstringmp4mp4 / 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_typestringautoauto / 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.
watermarkbooleanfalseStamp an "AI generated" mark in the bottom-right corner.
return_last_framebooleanfalseReturn output.last_frame_url for continuous chaining.
seedintegerRandom seed. Sent upstream on every request; the same seed gives a similar result but is not guaranteed to be byte-identical. Omitted → random.
toolsobject[][{ "type": "web_search" }] to let the model query the web.
image_urlsstring[]Up to 30 reference images (jpeg / png / webp / bmp / tiff / gif / heic / heif, each < 30 MB). Public HTTP(S) URLs.
image_with_rolesobject[]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_urlsstring[]Up to 10 reference clips (mp4 / mov, 480p–4K, each ≤ 200 MB), each 2–30 s, combined ≤ 30 s.
audio_urlsstring[]Up to 10 reference audio tracks (wav / mp3, each ≤ 15 MB), each 2–30 s, combined ≤ 30 s.
content_filterbooleantrueSafety 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 prompt or a reference field must be present
  • image_urls and image_with_roles are mutually exclusive (both carry reference images — pick one)
  • at most one first_frame and one last_frame, and a last_frame always needs a first_frame (there is no last-frame-only task type)
  • frame roles and reference_image cannot share one image_with_roles array — they are separate task types; put reference images in image_urls
  • a first_frame/last_frame entry forces size to adaptive
  • 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_type you 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 than standard, 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: false is rejected with 400 before anything is charged; the playground always runs moderated.
  • prompt becomes 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:

JobWhat it doesNeedsHard rules
Text-to-videonew clip from a promptpromptnone
First / last frameanimates between given framesimage_with_rolessize must be adaptive
Reference (reference)new clip guided by your images / clips / audioany reference fieldnone — any ratio, any duration
Edit (edit)changes the content of a source clip≥1 video_urls, source 4–30 sduration must be -1, size must be adaptive
Extend (extend)continues a source clip forwards or backwards≥1 video_urlssize 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 edittask starts, fails minutes later, refundedHTTP 400 at submit, no task, nothing charged
Feedback speedafter generationimmediate

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

FailureWhat it meansWhat to do
Synchronous 400 on submitA 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 helpRe-encode: images to jpeg / png / webp, clips to H.264 mp4, audio to wav / mp3
Task fails with a parameter error after startingThe 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 mismatchYou declared omni_reference_task_type but the model read your prompt as a different jobMatch the prompt to the job — an edit prompt must describe a change to the existing footage, not a new scene
Task fails citing sensitive contentReference material or the generated output was rejected by moderationFully refunded. Real-person references are supported and reviewed automatically — persistent rejections mean the material itself violates content policy
402 on submitNot 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 processingNormal for up to a few minutes — generation averages 2–5 min depending on length and referencesKeep 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.001

Live 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) + duration

reAPI 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) )
durationreference clipinput + outputflooryou pay
5 s2 s7 s9 s9 s
5 s6 s11 s9 s11 s
10 s3 s13 s17 s17 s
10 s12 s22 s17 s22 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.

Table of Contents