视频生成接入指南

用算力汇的 Seedance 模型生成视频。视频接口和文本接口的用法完全不同 —— 它是「异步任务」,必须走提交 → 轮询 → 下载三步。 本文给出可直接复制的完整流程、参数说明和计费规则。

和文本接口的区别 准备工作 支持的模型与价格 第一步:提交任务 第二步:轮询状态 第三步:下载成片 完整代码 参数详解 计费规则 错误排查 最佳实践 常见问题
一句话理解:文本模型一次请求就返回结果;视频模型先返回一个「任务号」, 你要反复问「好了没」,好了再去把文件取回来。整个过程通常 2~5 分钟。

一、和文本接口的区别

① 提交任务POST /v1/videos
立刻拿到 task_id
② 轮询状态GET /v1/videos/{id}
每 15~30 秒查一次
③ 下载成片GET /v1/videos/{id}/content
拿到 MP4 文件
文本模型视频模型
返回方式同步,一次请求拿到结果异步,先返回任务号
耗时几秒2~5 分钟
调用次数1 次多次(提交 1 次 + 轮询 N 次 + 下载 1 次)
计费按 token按秒(预扣后结算)
单价量级¥1 起 / 百万 token¥1 起 / 秒(约贵几百倍)

二、准备工作

1

注册并创建 API 令牌

到 控制台注册,然后「令牌」→ 新建,得到形如 sk-xxxxxxxx… 的密钥。新账号赠送 ¥5 试用额度。

2

先充值,别用试用额度跑视频

视频按秒计费,单条 4 秒视频就要几块钱,是文本的几百倍。 而且提交任务时会预扣整条费用 —— 余额不足会直接返回 insufficient_user_quota,任务都不会创建。

建议先充 ¥50 以上再试。参考:4 秒 720p 最便宜的型号约 ¥4。

3

记住 Base URL

https://api.ideamine.cn/v1,鉴权头 Authorization: Bearer sk-你的令牌。

三、支持的模型与价格

模型单价4 秒10 秒说明
Doubao-Seedance-2.0-fast¥1.40 / 秒¥5.60¥14.00出片最快,性价比首选
Doubao-Seedance-2.0¥1.23 / 秒¥4.93¥12.31画质更好
Doubao-Seedance-2.0-test¥1.23 / 秒¥4.93¥12.31上游测试型号
Doubao-Seedance-2.5-test¥1.87 / 秒¥7.48¥18.70支持更长时长与更高分辨率
video-fast¥1.40 / 秒¥5.60¥14.00稳定别名 → 2.0-fast
video-quality¥1.23 / 秒¥4.93¥12.31稳定别名 → 2.0
video-pro¥1.87 / 秒¥7.48¥18.70稳定别名 → 2.5-test(上游测试型号)
怎么选?先用 Doubao-Seedance-2.0-fast 打样(便宜、快), 确认画面和提示词之后再换 2.0 或 2.5-test 重跑一次。 -test 结尾的是上游的测试型号,随时可能下线,正式业务建议优先用不带 test 的。

价格以 模型广场 实时为准。

用「稳定别名」还是「具体型号」?

写法示例适合谁
稳定别名(推荐)video-fast / video-quality 希望平台负责选型、升级与容灾,代码不用改
具体型号Doubao-Seedance-2.0-fast 需要锁定输出特征,或有合规要求指定模型
建议长跑业务用别名。某个具体型号下线或调价时,别名会自动切到替代型号, 你的代码无需改动。别名只会指向正式型号,不会指向测试型号。

四、第一步:提交任务

POST /v1/videos —— 提交后立刻返回,不会等待生成完成。

curl https://api.ideamine.cn/v1/videos \
  -H "Authorization: Bearer sk-你的令牌" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "Doubao-Seedance-2.0-fast",
    "prompt": "一只橘猫坐在窗台上,阳光洒进来,镜头缓慢推近",
    "seconds": "4",
    "size": "1280x720"
  }'

成功响应(把这些字段存下来,后面两步要用):

{
  "id": "task_8HqutzmlCQUp0dLEPE1oUce2lXLB8SDt",
  "object": "video",
  "model": "Doubao-Seedance-2.0-fast",
  "status": "queued",
  "progress": 0,
  "created_at": 1791013994
}
seconds 必须传字符串,不能传数字。 传 "seconds": 4 会返回 json: cannot unmarshal number into ... seconds of type string。 正确写法是 "seconds": "4"。这是最常见的一个报错。

五、第二步:轮询状态

GET /v1/videos/{task_id},用上一步返回的 id。

curl https://api.ideamine.cn/v1/videos/task_8HqutzmlCQUp0dLEPE1oUce2lXLB8SDt \
  -H "Authorization: Bearer sk-你的令牌"
queued→ in_progress→ completed /failed
status含义你要做什么
queued已受理,排队中继续等
in_progress生成中,progress 有百分比继续等
completed完成了进入第三步下载
failed失败预扣额度已自动退还,可重试

轮询建议:

4 秒 720p 的实测耗时: 2.0-fast 约 2 分 30 秒,2.0 约 4 分 30 秒,2.5-test 约 3 分钟。 时长越长、分辨率越高越慢。

六、第三步:下载成片

GET /v1/videos/{task_id}/content —— 直接返回 video/mp4 二进制,存盘即可。

curl -L https://api.ideamine.cn/v1/videos/task_8HqutzmlCQUp0dLEPE1oUce2lXLB8SDt/content \
  -H "Authorization: Bearer sk-你的令牌" \
  -o output.mp4

返回示例:

HTTP/1.1 200 OK
Content-Type: video/mp4
Content-Length: 1103232

(二进制 MP4 内容)
必须在 status = completed 之后才能取。 任务还没完成就去取,会返回 artifact_gone。
成片有效期 24 小时,拿到就转存。 上游返回的临时地址有效期 24 小时,并且限 100 次下载, 过期或超次后本站也无法再代取。请取到后立刻保存到自己的对象存储(OSS / S3 / COS), 不要把本站链接当成长期地址分发给终端用户。

七、完整代码(可直接跑)

Python

import time
import requests

BASE = "https://api.ideamine.cn/v1"
HEADERS = {"Authorization": "Bearer sk-你的令牌"}

# ── 1) 提交任务 ──────────────────────────────────────────────
resp = requests.post(f"{BASE}/videos", headers=HEADERS, json={
    "model": "Doubao-Seedance-2.0-fast",
    "prompt": "一只橘猫坐在窗台上,阳光洒进来,镜头缓慢推近",
    "seconds": "4",            # 注意:字符串,不是数字
    "size": "1280x720",
}, timeout=60)
resp.raise_for_status()
task_id = resp.json()["id"]
print("任务已提交:", task_id)

# ── 2) 轮询状态 ──────────────────────────────────────────────
deadline = time.time() + 600          # 总超时 10 分钟
status = None
while time.time() < deadline:
    r = requests.get(f"{BASE}/videos/{task_id}", headers=HEADERS, timeout=30).json()
    status = r["status"]
    print(f"  {status} {r.get('progress', '')}")
    if status in ("completed", "failed"):
        break
    time.sleep(20)

if status != "completed":
    raise SystemExit(f"任务未完成: {status}")

# ── 3) 下载成片 ──────────────────────────────────────────────
video = requests.get(f"{BASE}/videos/{task_id}/content", headers=HEADERS, timeout=180)
video.raise_for_status()
with open("output.mp4", "wb") as f:
    f.write(video.content)
print(f"已保存 output.mp4,{len(video.content)} 字节")

Node.js

import { writeFile } from 'node:fs/promises'

const BASE = 'https://api.ideamine.cn/v1'
const HEADERS = {
  Authorization: 'Bearer sk-你的令牌',
  'Content-Type': 'application/json',
}

// 1) 提交
const submit = await fetch(`${BASE}/videos`, {
  method: 'POST',
  headers: HEADERS,
  body: JSON.stringify({
    model: 'Doubao-Seedance-2.0-fast',
    prompt: '一只橘猫坐在窗台上,阳光洒进来',
    seconds: '4',          // 字符串
    size: '1280x720',
  }),
}).then(r => r.json())
const taskId = submit.id
console.log('任务已提交:', taskId)

// 2) 轮询
let status = ''
const deadline = Date.now() + 10 * 60 * 1000
while (Date.now() < deadline) {
  const s = await fetch(`${BASE}/videos/${taskId}`, { headers: HEADERS }).then(r => r.json())
  status = s.status
  console.log(' ', status, s.progress ?? '')
  if (status === 'completed' || status === 'failed') break
  await new Promise(r => setTimeout(r, 20000))
}
if (status !== 'completed') throw new Error(`任务未完成: ${status}`)

// 3) 下载
const buf = Buffer.from(
  await fetch(`${BASE}/videos/${taskId}/content`, { headers: HEADERS }).then(r => r.arrayBuffer())
)
await writeFile('output.mp4', buf)
console.log('已保存 output.mp4,', buf.length, '字节')

bash 脚本(存成 gen-video.sh)

#!/usr/bin/env bash
set -euo pipefail
TOKEN="sk-你的令牌"
BASE="https://api.ideamine.cn/v1"
PROMPT="${1:-一只橘猫坐在窗台上,阳光洒进来}"
SECONDS_LEN="${2:-4}"
SIZE="${3:-1280x720}"

# 1) 提交
TASK=$(curl -sS -X POST "$BASE/videos" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d "{\"model\":\"Doubao-Seedance-2.0-fast\",\"prompt\":\"$PROMPT\",\"seconds\":\"$SECONDS_LEN\",\"size\":\"$SIZE\"}" \
  | python3 -c 'import sys,json;print(json.load(sys.stdin)["id"])')
echo "任务: $TASK"

# 2) 轮询(最多 20 次 × 20 秒)
for i in $(seq 1 20); do
  S=$(curl -sS "$BASE/videos/$TASK" -H "Authorization: Bearer $TOKEN" \
      | python3 -c 'import sys,json;print(json.load(sys.stdin)["status"])')
  echo "  [$i] $S"
  [ "$S" = "completed" ] && break
  [ "$S" = "failed" ] && { echo "生成失败(额度已退还)"; exit 1; }
  sleep 20
done
[ "$S" = "completed" ] || { echo "超时"; exit 1; }

# 3) 下载
curl -sS -L "$BASE/videos/$TASK/content" -H "Authorization: Bearer $TOKEN" -o output.mp4
echo "已保存 output.mp4($(wc -c < output.mp4) 字节)"

用法:bash gen-video.sh "你的提示词" 4 1280x720

八、参数详解

提交任务(POST /v1/videos)

字段类型必填说明
durationinteger否时长的整数写法(等价于 seconds)。兼容期两种都接受,服务端会归一化;归一化发生在幂等指纹之前,所以 duration:4 与 seconds:"4" 视为同一请求。
modelstring是可用稳定别名(video-fast / video-quality)或具体型号
promptstring是画面描述;写清「主体 + 动作 + 镜头」效果最好
quote_idstring否来自 /videos/quote,用于锁价
ratiostring否画面比例。不传会由 size 自动推导;不支持的取值返回 invalid_ratio
resolutionstring否分辨率的另一种写法(等价于 size),支持 480p / 720p / 1080p 简写
secondsstring否时长(秒),必须字符串,默认 4。各模型范围不同(2.0 系列 4~12 秒、2.5 系列 2~30 秒),越界返回 invalid_duration。可用 duration 传整数,两者等价
sizestring否画面尺寸。1280x720(横屏 16:9,默认)/720x1280(竖屏 9:16)/1792x1024/1024x1792
webhook_secretstring否用于对回调做 HMAC 签名
webhook_secret_prevstring否密钥轮换过渡期用:上一把密钥。给了它,我们会用新旧两把密钥各签一次,你用任一都能验签,实现零停机换密钥
webhook_urlstring否任务完成时的回调地址(也可用请求头 X-Webhook-Url)

时长与分辨率上限

模型时长范围分辨率
Doubao-Seedance-2.0-fast4 ~ 12 秒720p / 720p(竖)
Doubao-Seedance-2.04 ~ 12 秒720p / 720p(竖)
Doubao-Seedance-2.0-test4 ~ 12 秒720p / 720p(竖)
Doubao-Seedance-2.5-test2 ~ 30 秒720p / 720p(竖) / 1080p / 1080p(竖)
video-fast4 ~ 12 秒720p / 720p(竖)
video-quality4 ~ 12 秒720p / 720p(竖)
video-pro2 ~ 30 秒720p / 720p(竖) / 1080p / 1080p(竖)
超出范围会被网关直接拒绝,返回 invalid_duration,且不会扣费。 请按上表取值 —— 参数不合法时直接提交,只会白白占用时间。
竖屏短视频?把 size 设成 720x1280 即可, ratio 会自动变成 9:16。

查询与下载

字段说明
id任务号,格式 task_xxx
object固定为 video
model提交时用的模型(别名或具体型号)
statusqueued → in_progress → completed;失败为 failed,内容拒绝为 rejected,超时为 expired
progress进度百分比字符串,如 100%
created_at创建时间(Unix 秒)
completed_at完成时间(Unix 秒)
expires_at成片有效期(Unix 秒),默认完成时间 +24 小时;过期后取片返回 artifact_gone
requested_duration请求时长(秒),等于提交时的 seconds
actual_duration实际时长(秒);completed 时等于结算时长,未完成时为 null
billing账单对象:currency / unit_price / reserved / settled / refunded / quota / seconds
error失败时的错误对象(code / message);成功时为 null

九、计费规则

计费方式按秒计费,与 token 无关
扣费时机提交任务时预扣整条费用(按请求的 seconds)
结算任务完成后按实际时长结算
失败退款任务 failed 时自动退还预扣额度
轮询开销不额外计费
计算公式费用 = 秒数 × 单价

举例:用 Doubao-Seedance-2.0-fast 生成 4 秒视频, 费用 = 4 × ¥0.99 = ¥3.96。

预扣的额度要够。提交时会按请求的秒数一次性预扣。 比如 10 秒的 2.5-test 需要预扣 ¥18.70 —— 余额不足会直接返回 insufficient_user_quota,任务根本不会创建。

十、错误排查

报错原因处理
json: cannot unmarshal number into ... seconds of type string seconds 传了数字 改成字符串 "4"
invalid_duration 时长超出该模型支持范围(如 2.0 系列传 20 秒) 按「时长与分辨率上限」表取值;此错误不扣费
insufficient_user_quota 余额不足以预扣 充值。报错信息里会写明「需要预扣多少」
artifact_gone 任务还没 completed 就去下载 先轮询到完成再取
model_not_found 模型名写错,或该模型已下线 用 模型广场 里的准确名称
failed(status) 生成失败(提示词被拦截、上游繁忙等) 额度已退还;换提示词或稍后重试
401 Invalid token 令牌错误或已删除 检查 Authorization 头里的 sk- 令牌
403 model_not_allowed 等 令牌设了模型白名单 到控制台给令牌放开该模型,或换用无限制令牌
排查顺序:先看 status 是不是 failed(有 fail_reason), 再看 HTTP 状态码,最后看 error.code。每一步的 request id 都可以提供给客服定位。

参数校验错误长什么样

参数在网关侧就会被拦下(不会到达上游,不产生费用)。 返回 HTTP 400,响应体形如:

{
  "code": "plugin_request_invalid",
  "message": "... Error: invalid_duration ...",
  "data": null
}
怎么解析:用字符串包含判断即可 —— message 里出现 invalid_duration 就代表时长越界。 我们正在把稳定错误码提到顶层 code 字段,届时按 code 精确匹配。

十一、最佳实践

1. 先用低成本型号打样

视频是按秒线性计费的,反复「抽卡」很烧钱。推荐流程:

2. 提示词写清「主体 + 动作 + 镜头」

❌ 太笼统✅ 具体
一只猫一只橘猫坐在木质窗台上,阳光从右侧斜射进来,镜头缓慢向前推近
风景雨后的山间公路,镜头贴着地面向前平移,远处有薄雾

加上镜头运动(推近 / 平移 / 俯拍)能让画面更稳定,也更容易一次成功。

3. 时长不要多买

费率按秒线性算,生成 10 秒再剪成 5 秒 = 白付一半。 大多数社交内容 4~8 秒就够,先想清楚成片多长再决定生成多长。

4. 分辨率按用途选

5. 先报价、后下单:报价单与锁价

视频是按秒计费的,批量业务需要在下单前就把预算算准。 先调报价接口拿到确定的价格,再用 quote_id 下单。

# ① 报价
curl https://api.ideamine.cn/v1/videos/quote \
  -H "Authorization: Bearer sk-你的令牌" \
  -H "Content-Type: application/json" \
  -d '{"model": "video-quality", "seconds": "10"}'
{
  "quote_id": "quote_d714ec7e080e43d298062e12",
  "currency": "CNY",
  "model": "video-quality",
  "seconds": 10,
  "unit_price": 1.231402,
  "estimated_cost": 12.314,
  "quota": 879573,
  "duration_limit": {"min": 4, "max": 12},
  "expires_at": 1791191172
}
# ② 下单时带上 quote_id
curl https://api.ideamine.cn/v1/videos \
  -H "Authorization: Bearer sk-你的令牌" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "video-quality",
    "prompt": "一只橘猫坐在窗台上",
    "seconds": "10",
    "size": "1280x720",
    "quote_id": "quote_d714ec7e080e43d298062e12"
  }'
锁价承诺:报价单有效期 30 分钟。 提交时若价格已变动,会直接返回 409 quote_price_changed 并告诉你新旧价格,绝不会按新价静默扣费。 一张报价单只能用一次。
错误码含义
invalid_duration时长超出该模型范围(报价与提交都会校验)
model_not_found该模型没有定价
quote_not_found报价单不存在
quote_expired报价单超过 30 分钟
quote_price_changed价格在报价后变动,请重新报价
quote_duration_mismatch / quote_model_mismatch提交内容与报价单不一致
quote_already_used该报价单已用过一次
quote_token_mismatch报价单是别的令牌创建的

5. 用 Webhook 代替轮询(批量业务必备)

同时跑几十上百条视频时,轮询会很累。提交时给一个回调地址, 任务完成(或失败)后我们会主动推事件给你。

curl https://api.ideamine.cn/v1/videos \
  -H "Authorization: Bearer sk-你的令牌" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "video-fast",
    "prompt": "一只橘猫坐在窗台上",
    "seconds": "4",
    "size": "1280x720",
    "webhook_url": "https://your-app.example.com/hooks/video",
    "webhook_secret": "your-signing-secret"
  }'

也可以用请求头 X-Webhook-Url: https://... 传地址(此时用请求头 X-Webhook-Secret 传密钥)。

你会收到的事件体(POST,JSON):

{
  "event_id": "evt_c4334e56781e49bfad1d1616",
  "event": "video.completed",
  "task_id": "task_710pIEcWFjYyhEFkgfMaOX3FgwR9O6pN",
  "status": "completed",
  "progress": "100%",
  "model": "video-fast",
  "fail_reason": null,
  "created_at": 1791188934,
  "timestamp": 1791189125,
  "attempt": 1
}
请求头说明
X-Webhook-Event-Id事件唯一 id,可用它做幂等去重(同一事件可能重投)
X-Webhook-Timestamp投递时间戳(秒)
X-Webhook-Signaturesha256=<hex>,验签算法见下

验签(Python 示例):

import hmac, hashlib, time

def verify(secret: str, timestamp: str, raw_body: bytes, signature: str) -> bool:
    # 1) 防重放:时间戳与当前时间相差超过 5 分钟就拒绝
    if abs(int(time.time()) - int(timestamp)) > 300:
        return False
    # 2) 复算签名:signed = f"{timestamp}." + raw_body
    expected = "sha256=" + hmac.new(
        secret.encode(), (timestamp + ".").encode() + raw_body, hashlib.sha256
    ).hexdigest()
    # 3) 常量时间比较,避免时序攻击
    return hmac.compare_digest(expected, signature)
投递策略:失败按 30 秒 → 2 分 → 10 分 → 1 小时 → 6 小时 → 24 小时 共重试 6 次,之后不再投递。请以 event_id 去重(重试可能造成重复投递)。 收到事件后,成片仍通过 GET /v1/videos/{task_id}/content 获取。

5. 防重复扣费:用 Idempotency-Key

视频任务单价高,网络中断后的重试很容易造成重复生成、重复扣费。 在提交时带上一个你自己生成的唯一键,网关就会自动去重:

curl https://api.ideamine.cn/v1/videos \
  -H "Authorization: Bearer sk-你的令牌" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-20261005-0001" \
  -d '{"model":"video-fast","prompt":"一只橘猫","seconds":"4","size":"1280x720"}'
场景行为
不传 Idempotency-Key与以前完全一致,不去重
同一个 Key + 完全相同的请求体,24 小时内重复提交 不再生成新任务,直接返回上次的响应,并带 Idempotency-Replayed: true
同一个 Key + 不同的请求体 返回 409 idempotency_key_reuse(防止一次"改需求"变成两笔扣费)
怎么用:用你的订单号 / 业务主键当 Key 最合适 —— 重试时原样再发一次即可,不用担心多扣钱。去重窗口 24 小时, 且只在同一令牌内生效。错误响应(4xx/5xx)不会被缓存,改正后可以立即重试。

5. 工程上的注意事项

十二、常见问题

问题回答
生成一条要多久?4 秒 720p 约 2.5~5 分钟;时长和分辨率越高越久。不要把轮询放在同步请求里
能同时提交多条吗?可以,任务之间相互独立。建议先小批量验证稳定性再加并发
支持图生视频吗?当前开放的是文生视频。图生视频的需求可以联系客服
失败会扣钱吗?不会,预扣额度会自动退还
成片能存多久?24 小时。上游临时地址有效期为 24 小时(且限 100 次下载),过期后本站无法再代取,请及时转存到自己的对象存储
能开发票吗?可以,见 发票与合规
轮询太频繁会被限流吗?不会,但没必要。15~30 秒一次足够
可以指定种子复现同一个视频吗?当前接口未开放 seed 参数
还没解决?把任务号(task_id)和报错里的 request id 一起发给客服,定位最快。 相关页面:接入文档 · 模型价格 · 服务状态 · 控制台