视频生成接入指南
用算力汇的 Seedance 模型生成视频。视频接口和文本接口的用法完全不同 —— 它是「异步任务」,必须走提交 → 轮询 → 下载三步。 本文给出可直接复制的完整流程、参数说明和计费规则。
一、和文本接口的区别
POST /v1/videos立刻拿到
task_idGET /v1/videos/{id}每 15~30 秒查一次
GET /v1/videos/{id}/content拿到 MP4 文件
| 文本模型 | 视频模型 | |
|---|---|---|
| 返回方式 | 同步,一次请求拿到结果 | 异步,先返回任务号 |
| 耗时 | 几秒 | 2~5 分钟 |
| 调用次数 | 1 次 | 多次(提交 1 次 + 轮询 N 次 + 下载 1 次) |
| 计费 | 按 token | 按秒(预扣后结算) |
| 单价量级 | ¥1 起 / 百万 token | ¥1 起 / 秒(约贵几百倍) |
二、准备工作
注册并创建 API 令牌
到 控制台注册,然后「令牌」→ 新建,得到形如
sk-xxxxxxxx… 的密钥。新账号赠送 ¥5 试用额度。
先充值,别用试用额度跑视频
视频按秒计费,单条 4 秒视频就要几块钱,是文本的几百倍。
而且提交任务时会预扣整条费用 —— 余额不足会直接返回
insufficient_user_quota,任务都不会创建。
建议先充 ¥50 以上再试。参考:4 秒 720p 最便宜的型号约 ¥4。
记住 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-你的令牌"| status | 含义 | 你要做什么 |
|---|---|---|
queued | 已受理,排队中 | 继续等 |
in_progress | 生成中,progress 有百分比 | 继续等 |
completed | 完成了 | 进入第三步下载 |
failed | 失败 | 预扣额度已自动退还,可重试 |
轮询建议:
- 间隔 15~30 秒一次,别太频繁(没必要,也不会更快)
- 设置总超时(建议 10 分钟),超时后停止轮询并记录
task_id completed时响应里会多一个completed_at字段- 轮询不额外计费,可以放心查
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。
七、完整代码(可直接跑)
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)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
duration | integer | 否 | 时长的整数写法(等价于 seconds)。兼容期两种都接受,服务端会归一化;归一化发生在幂等指纹之前,所以 duration:4 与 seconds:"4" 视为同一请求。 |
model | string | 是 | 可用稳定别名(video-fast / video-quality)或具体型号 |
prompt | string | 是 | 画面描述;写清「主体 + 动作 + 镜头」效果最好 |
quote_id | string | 否 | 来自 /videos/quote,用于锁价 |
ratio | string | 否 | 画面比例。不传会由 size 自动推导;不支持的取值返回 invalid_ratio |
resolution | string | 否 | 分辨率的另一种写法(等价于 size),支持 480p / 720p / 1080p 简写 |
seconds | string | 否 | 时长(秒),必须字符串,默认 4。各模型范围不同(2.0 系列 4~12 秒、2.5 系列 2~30 秒),越界返回 invalid_duration。可用 duration 传整数,两者等价 |
size | string | 否 | 画面尺寸。1280x720(横屏 16:9,默认)/720x1280(竖屏 9:16)/1792x1024/1024x1792 |
webhook_secret | string | 否 | 用于对回调做 HMAC 签名 |
webhook_secret_prev | string | 否 | 密钥轮换过渡期用:上一把密钥。给了它,我们会用新旧两把密钥各签一次,你用任一都能验签,实现零停机换密钥 |
webhook_url | string | 否 | 任务完成时的回调地址(也可用请求头 X-Webhook-Url) |
时长与分辨率上限
| 模型 | 时长范围 | 分辨率 |
|---|---|---|
Doubao-Seedance-2.0-fast | 4 ~ 12 秒 | 720p / 720p(竖) |
Doubao-Seedance-2.0 | 4 ~ 12 秒 | 720p / 720p(竖) |
Doubao-Seedance-2.0-test | 4 ~ 12 秒 | 720p / 720p(竖) |
Doubao-Seedance-2.5-test | 2 ~ 30 秒 | 720p / 720p(竖) / 1080p / 1080p(竖) |
video-fast | 4 ~ 12 秒 | 720p / 720p(竖) |
video-quality | 4 ~ 12 秒 | 720p / 720p(竖) |
video-pro | 2 ~ 30 秒 | 720p / 720p(竖) / 1080p / 1080p(竖) |
invalid_duration,且不会扣费。
请按上表取值 —— 参数不合法时直接提交,只会白白占用时间。
size 设成 720x1280 即可,
ratio 会自动变成 9:16。
查询与下载
| 字段 | 说明 |
|---|---|
id | 任务号,格式 task_xxx |
object | 固定为 video |
model | 提交时用的模型(别名或具体型号) |
status | queued → 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。
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. 先用低成本型号打样
视频是按秒线性计费的,反复「抽卡」很烧钱。推荐流程:
- 用
Doubao-Seedance-2.0-fast+ 4 秒反复试提示词(每条 ~¥4) - 提示词定稿后,一个字都不改,换成最终型号和更长时长重跑一次
2. 提示词写清「主体 + 动作 + 镜头」
| ❌ 太笼统 | ✅ 具体 |
|---|---|
| 一只猫 | 一只橘猫坐在木质窗台上,阳光从右侧斜射进来,镜头缓慢向前推近 |
| 风景 | 雨后的山间公路,镜头贴着地面向前平移,远处有薄雾 |
加上镜头运动(推近 / 平移 / 俯拍)能让画面更稳定,也更容易一次成功。
3. 时长不要多买
费率按秒线性算,生成 10 秒再剪成 5 秒 = 白付一半。 大多数社交内容 4~8 秒就够,先想清楚成片多长再决定生成多长。
4. 分辨率按用途选
- 手机竖屏内容:720p 足够,和 1080p 在小屏上差别很小
- 内部比稿 / 给客户看方向:720p
- 正式交付:再上 1080p(只有
2.5-test支持)
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"
}'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-Signature | sha256=<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)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(防止一次"改需求"变成两笔扣费) |
5. 工程上的注意事项
- 把
task_id存进数据库 —— 服务重启后还能继续轮询 - 轮询要走后台任务(队列 / 定时器),不要占着 HTTP 请求线程等 3 分钟
- 设总超时(建议 10 分钟),超时后标记为「未知状态」,人工介入
- 成片在 24 小时内转存到自己的对象存储(有效期 24 小时 / 限 100 次下载),别把本站链接分发给终端用户
十二、常见问题
| 问题 | 回答 |
|---|---|
| 生成一条要多久? | 4 秒 720p 约 2.5~5 分钟;时长和分辨率越高越久。不要把轮询放在同步请求里 |
| 能同时提交多条吗? | 可以,任务之间相互独立。建议先小批量验证稳定性再加并发 |
| 支持图生视频吗? | 当前开放的是文生视频。图生视频的需求可以联系客服 |
| 失败会扣钱吗? | 不会,预扣额度会自动退还 |
| 成片能存多久? | 24 小时。上游临时地址有效期为 24 小时(且限 100 次下载),过期后本站无法再代取,请及时转存到自己的对象存储 |
| 能开发票吗? | 可以,见 发票与合规 |
| 轮询太频繁会被限流吗? | 不会,但没必要。15~30 秒一次足够 |
| 可以指定种子复现同一个视频吗? | 当前接口未开放 seed 参数 |