视频生成 · Doubao Seedance 2.0

文本 / 图片 / 参考视频 / 音频 → 视频。异步任务制:提交拿 task id,轮询取结果。 提供火山方舟兼容与原生两套接口, 已在用火山 SDK 的只需要改 base_url

模型能力

doubao-seedance-2.0——字节跳动豆包 Seedance 2.0 视频生成模型。

个别参数可能临时不可用(例如运营维护期间)。这种情况会返回 400 ERR_UNSUPPORTED_PARAMETER并指明是哪个参数,去掉它即可——不会被悄悄忽略然后给你一个不符合要求的视频。

能力取值
输入模态文本 / 图片 / 参考视频 / 音频,可混合
输出MP4 视频;可选尾帧静图
分辨率480p · 720p · 1080p
画幅16:9 9:16 1:1 4:3 3:4 21:9 9:21 adaptive
时长1–15 秒(默认 5)
帧率16 / 24(默认由模型决定,实测 24)
其他音频生成 · 水印开关 · 首尾帧 · 参考图
生成耗时5 秒 480p 实测约 2 分钟;分辨率与时长越高越久

两套接口,选一套

火山兼容原生
base_urlhttps://heiyutv.com/api/v3https://heiyutv.com/api/v1
路径/contents/generations/tasks/video/tasks
参数写法prompt 里的 --rs 1080p 后缀JSON 字段
适合已有火山 Ark 代码,想零改动切过来新接入,想要显式字段与费用明细

两者操作的是同一批任务:在火山口提交的任务可以用原生接口查,反之亦然, 控制台的视频生成页也一并列出。

火山方舟兼容接口

路径、请求体、响应字段与火山方舟一致。已经在用 volcengine-python-sdk 的话, 改两行即可:

from volcenginesdkarkruntime import Ark

client = Ark(
    api_key="sk-gw-XXXXXXXXXXXXXXXXXXXXXXXXXX",   # ← 换成本网关的 key
    base_url="https://heiyutv.com/api/v3",         # ← 换成本网关
)

task = client.content_generation.tasks.create(
    model="doubao-seedance-2.0",
    content=[{
        "type": "text",
        "text": "一只橘猫在窗台上打哈欠,阳光洒进来 --resolution 1080p --duration 5 --ratio 16:9",
    }],
)
print(task.id)          # vt_...

while True:
    t = client.content_generation.tasks.get(task_id=task.id)
    if t.status in ("succeeded", "failed", "cancelled"):
        break
    time.sleep(10)

print(t.content.video_url)

cURL

# 建任务
curl https://heiyutv.com/api/v3/contents/generations/tasks \
  -H "Authorization: Bearer sk-gw-XXXXXXXXXXXXXXXXXXXXXXXXXX" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "doubao-seedance-2.0",
    "content": [{"type": "text", "text": "一只橘猫在窗台上打哈欠 --rs 720p --dur 5"}]
  }'
# → {"id":"vt_9f2c1a..."}

# 查任务
curl https://heiyutv.com/api/v3/contents/generations/tasks/vt_9f2c1a... \
  -H "Authorization: Bearer sk-gw-XXXXXXXXXXXXXXXXXXXXXXXXXX"

prompt 参数后缀

火山的写法是把参数拼在 prompt 后面。网关会解析这些后缀、从提示词中剥离, 再以显式字段提交生成——不会--rs 1080p 当成提示词的一部分喂给模型。

后缀简写取值
--resolution--rs480p / 720p / 1080p
--ratio--rt16:9 / 9:16 / 1:1 / 4:3 / 3:4 / 21:9 / 9:21 / adaptive
--duration--dur1–15(秒)
--framespersecond--fps16 / 24
--watermark--wmtrue / false
--camerafixed--cf本模型不支持,传了会返回 400
--seed本模型不支持,传了会返回 400-1 除外,它本来就是「随机」)
这两个参数在 Seedance 2.0 上无效。它们不会报错、也不会生效—— 实测同一段提示词配同一个 seed 生成两次,结果是两个不同的视频, 而响应里照样把 seed 原样返回。既然按住不动做不到, 我们宁可当场告诉你,也不让你拿到一个不符合要求的视频还照付钱。 镜头调度请写进提示词

--key value--key=value 两种写法都接受。写错值(例如--resolution 4k)会返回 400 而不是被悄悄忽略—— 被忽略的后果是你拿到一个不符合要求的视频,并且照样付费。

网关不认识的 --xxx 会原样留在提示词里:提示词是自由文本,我们不替你删。

原生接口

参数用显式 JSON 字段,响应额外带费用明细。

curl https://heiyutv.com/api/v1/video/tasks \
  -H "Authorization: Bearer sk-gw-XXXXXXXXXXXXXXXXXXXXXXXXXX" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "doubao-seedance-2.0",
    "content": [
      {"type": "text", "text": "一只橘猫在窗台上打哈欠,阳光洒进来"},
      {"type": "image_url", "role": "first_frame",
       "image_url": {"url": "https://example.com/first.jpg"}}
    ],
    "resolution": "1080p",
    "ratio": "16:9",
    "duration": 5,
    "generate_audio": false,
    "watermark": false,
    "return_last_frame": true
  }'

content 数组

type说明
text提示词,必填至少一个
image_url参考图。可带 rolefirst_frame(首帧)· last_frame(尾帧)· reference_image(主体参考);不带 role 即普通图生视频
video_url参考视频。注意它会改变计费档位,见下
audio_url参考音频

role 只对 image_url 有意义,写在别的类型上会返回 400—— 静默忽略只会让你以为它生效了。

响应

{
  "id": "vt_9f2c1a...",
  "object": "video.task",
  "model": "doubao-seedance-2.0",
  "status": "succeeded",
  "resolution": "1080p",
  "ratio": "16:9",
  "duration": 5,
  "fps": 24,
  "seed": 59014,
  "video_url": "https://.../video.mp4?...",
  "last_frame_url": "https://.../last_frame.jpg?...",
  "video_url_expires_at": "2026-07-29T15:40:15Z",
  "usage": { "total_tokens": 50638 },
  "cost": {
    "amount_uc": 64715364,
    "tier": "novin_hd",
    "list_uc_per_token": 1417,
    "rate": 0.95,
    "plan": "growth"
  }
}

seed上游生成时实际使用的种子,由它随机 指定并回报;这个模型不接受你指定种子(见上文)。它对你仍然有用—— 两次结果不同、要向我们追问哪一次是哪一次时,这是唯一的凭据。

usage.total_tokens上游报的实际消耗, 不是我们的估算;cost 是按它算出来的实扣金额, 并附上命中的计费档、该档原价与你的折扣,方便自行复算。 任务结算之前不会有 cost 字段。

列表与筛选

两套接口都支持同名的筛选与分页参数,且严格按你的账号隔离——你只能看到自己的任务。

GET /api/v1/video/tasks?filter.status=succeeded&page_size=20&page_num=1
GET /api/v3/contents/generations/tasks?filter.task_ids=vt_a&filter.task_ids=vt_b
  • filter.status · queued / running / succeeded / failed / cancelled / expired。写错值返回 400,不会静默返回全部
  • filter.task_ids · 可重复,按 task id 精确取
  • filter.model · 目前只有一个视频模型
  • page_num / page_size(原生接口也接受 limit),单页上限 100,响应带 total

任务状态

status含义计费
queued已受理,排队中不扣
running生成中不扣
succeeded完成,取 video_url按实际 token 扣费
failed失败,看 error不扣
cancelled你主动取消,且已确认停止不扣
expired超过 48 小时仍未完成,自动关闭不扣

火山兼容接口上没有 expired 这个状态——火山只定义五个。 为了不让照着火山写的轮询循环把一个已经死掉的任务轮询到天荒地老, 该状态在 /api/v3 上报为 status: "failed", error.code: "TaskExpired"。 原生接口保留 expired 原值。

取消不一定成功

DELETE 会先尝试停止这个任务,按是否真的停下来决定它的状态和计费——不是收到你的请求就算取消。

  • 确认已停止 → 200,状态变 cancelled不计费
  • 无法停止(任务已经在生成中,多数情况如此)→ 409 ERR_CANCEL_REFUSED。任务仍在跑,会正常完成并照常计费,你依然能拿到视频
  • 暂时无法确认是否停止 → 502 ERR_CANCEL_FAILED,可以重试

换句话说:想省钱就别等到任务跑起来再取消。 我们不会因为你点了取消就把一个已经产出的视频算作免费—— 那样等于按下删除键就能白拿。

视频链接 24 小时后失效

video_urllast_frame_url 都是预签名地址,24 小时后失效,我们不做转存。 响应里的 video_url_expires_at 就是失效时刻——请及时下载保存。

计费

只按输出 token 计费(该模型不计入 prompt token)。单价分四档, 由是否含视频输入 × 分辨率 决定:

档位条件官网原价
vin_sd含视频输入 · 480p/720p¥56 / 百万 tokens
novin_sd无视频输入 · 480p/720p¥92 / 百万 tokens
vin_hd含视频输入 · 1080p¥62 / 百万 tokens
novin_hd无视频输入 · 1080p¥102 / 百万 tokens

档位按实际交付的分辨率判定,不是你请求的那个: 若实际出片降级,你不会为没拿到的 1080p 付钱。 实际扣费 = 该档原价 × 你的折扣 × 实际生成的 token 数,cost 块把这几项都给你,可自行核对。

token 量级参考(纯文本输入):480p 约 10,100/秒、720p 约 21,700/秒、1080p 约 48,900/秒。带参考视频会让输出 token 翻倍(实测 1.99×)——单价虽然更低,总价通常更高。

举例:5 秒 480p 纯文本任务实测 50,638 tokens,命中 novin_sd,原价约 ¥4.66。

视频消费与 chat 走同一套账:出现在Billing 页、用量统计与发票口径里,不需要另看一处。计价细节见计费与余额

错误

错误体与其他接口一致:{"error": {"type", "code", "message"}}。 视频接口特有的:

  • 400 ERR_INVALID_REQUEST · 参数不合法(分辨率、画幅、时长、seed、role、prompt 后缀)
  • 400 ERR_UNSUPPORTED_PARAMETER · 参数本身合法,但当前不可用。message 里会指明是哪个参数,去掉即可。不会被静默忽略
  • 404 ERR_UNKNOWN_MODEL · 该 model 不由本网关提供
  • 404 ERR_NOT_FOUND · 任务不存在,或不属于你
  • 502 ERR_UPSTREAM · 这次任务被拒绝(含限流),不重试;未建成的任务不计费
  • 503 ERR_SUPPLIER_SUSPENDED / ERR_MODEL_SUSPENDED · 运营侧临时停用,消息里带原因

完整错误码见 错误码章节

几个不兼容点

照着火山的文档来,下面这几处需要知道:

  • task id 是我们的vt_...),不是火山的 cgt-...
  • model 始终回显目录 slug
  • 不接受火山的带日期部署名(如 doubao-seedance-1-0-pro-250528)——那是另一个模型,接受它等于谎称我们有
  • 不支持 callback_url:请轮询。建议起始 10 秒,随任务变老退避到 30 秒、2 分钟
  • 响应额外多出 video_url_expires_atcost——是新增字段,不改动火山原有字段

下一步