视频生成 · Seedance

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

按秒计费的定制模型

三个产品独立上架,使用下表中的模型 ID。支持文本、图片、参考视频和音频输入;参考视频已开放。各档位价格见 模型价格,账户实际价格见控制台。

模型 ID分辨率指定时长参考视频
seedance-2.0-custom720p / 1080p1–60 秒最多 3 个,总计不超过 15 秒
seedance-2.0-fast-custom720p / 1080p1–60 秒最多 3 个,总计不超过 15 秒
seedance-2.5-custom720p / 1080p / 2k / 4k4–30 秒最多 10 个,总计不超过 30 秒

默认 5 秒、720p、自适应画幅、生成音频。可用 generate_audio: false 关闭音频。图片最多分别为 9 / 9 / 30 张;音频数量和总时长限制与参考视频相同。媒体应为可公开访问的 HTTP(S) URL,素材格式和实际时长由模型服务校验。

费用 = 请求指定的秒数 × 对应档位的每秒单价 × 账户费率。档位由分辨率和是否传入参考视频决定。提交时锁定价格,成功后扣费,失败不计费;图片、音频开关不会切换“含参考视频”档位。视频文件可能多一个封装帧,不会因此多收一秒。

{
  "model": "seedance-2.5-custom",
  "content": [
    {
      "type": "text",
      "text": "参考视频中的构图,生成一艘蓝色小船在湖面行驶的新视频"
    },
    {
      "type": "video_url",
      "video_url": {
        "url": "https://your-public-host/reference.mp4"
      },
      "role": "reference_video"
    }
  ],
  "resolution": "720p",
  "duration": 4,
  "generate_audio": false
}

将以上 JSON 提交至 POST /api/v1/video/tasks,再通过 GET /api/v1/video/tasks/任务ID 查询。查询返回的 cost.unitsecondcost.quantity 为计费秒数,cost.unit_price 为人民币/秒,cost.quantity_sourcerequested_duration

参考视频用于生成指定时长的新视频。2.5 的“直接编辑原视频并沿用原时长”模式需要自动时长 -1,本接口暂不支持该模式。定制模型不支持取消、沙箱、seed、fps、camera_fixed、watermark;首尾帧模式不能与多模态参考素材混用。2.0 两个产品可传 scene_optimize: realisticanime

两套接口,选一套

火山兼容原生
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="seedance-2.0-custom",
    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": "seedance-2.0-custom",
    "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--dur4–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": "seedance-2.0-custom",
    "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": "seedance-2.0-custom",
  "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": {
    "currency": "CNY",
    "amount": "4.659506",
    "tier": "novin_hd"
  }
}

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

usage.total_tokens实际消耗, 不是我们的估算;cost.amount 是按它算出来的实扣金额,单位人民币(字符串,最多 6 位小数),cost.tier 是命中的计费档,可对照下面的价表自行复算。 任务结算之前不会有 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 就是失效时刻——请及时下载保存。

计费

视频模型按请求指定的秒数计费: 费用 = 秒数 × 命中档位的每秒单价 × 账户费率。档位由模型 ×分辨率 × 是否传入参考视频决定,分档单价见价格页控制台 · 模型目录

提交时锁定价格,成功后扣费;失败与未建成的任务不计费。 实际成片可能比指定时长多一个封装帧,不会因此多收一秒。 响应的 cost 块给出最终金额(unit second)与命中的档位;逐条明细与月度汇总见计费与余额

视频消费与 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——是新增字段,不改动火山原有字段

下一步