03 查询任务
拿到 task_id 后, 怎么轮询任务状态 / 怎么拿视频地址 / 状态机说明
Endpoint
| 协议风格 | URL |
|---|---|
| OpenAI 风格 (推荐) | GET /v1/videos/{task_id} |
| Volcengine 风格 | GET /api/v3/contents/generations/tasks/{task_id} |
两个路由响应结构不同:
- OpenAI 风格: OpenAIVideo 对象 (
id/object/progress/metadata.*), status 枚举queued/in_progress/completed/failed - Volcengine 风格: Volcengine 官方 Seedance 对象 (
content.video_url/usage/顶层seed/resolution/duration/ratio/framespersecond), status 枚举queued/running/succeeded/failed/expired/cancelled(6 个)
见下方 字段说明 段。
请求
curl https://www.dianlitoken.com/v1/videos/cgt-xxx \
-H "Authorization: Bearer sk-你的KEY"响应
两个路由的响应结构不一样
- OpenAI 风格
/v1/videos/{task_id}返回OpenAIVideo形态(id,object,progress,metadata.*) - Volcengine 风格
/api/v3/contents/generations/tasks/{task_id}返回 Volcengine 官方 Seedance 形态(content.video_url, 顶层usage/seed/resolution/duration/ratio/framespersecond)
两个路由都可以拿到视频, 选与你 SDK 匹配的那个就行。
OpenAI 风格响应 /v1/videos/{task_id}
排队中 / 生成中
{
"id": "cgt-20260527165600-kf7rt",
"task_id": "cgt-20260527165600-kf7rt",
"object": "video",
"model": "seedance-2.0-fast",
"status": "queued",
"progress": 0,
"created_at": 1779869660,
"completed_at": 1779869660,
"metadata": {}
}status 在生成中变为 in_progress, progress 约 50%, metadata 仍为空。
完成 (status = completed)
{
"id": "cgt-20260527165600-kf7rt",
"task_id": "cgt-20260527165600-kf7rt",
"object": "video",
"model": "seedance-2.0-fast",
"status": "completed",
"progress": 100,
"created_at": 1779869660,
"completed_at": 1779869780,
"metadata": {
"url": "https://ark-aigc-cn-beijing.tos-cn-beijing.volces.com/.../xxxxxx.mp4?X-Tos-Signature=...",
"usage": {"completion_tokens": 40594, "total_tokens": 40594},
"resolution": "720p",
"duration": 5,
"ratio": "16:9",
"framespersecond": 24,
"seed": 42
}
}字段说明(OpenAI 风格)
| 字段 | 类型 | 何时存在 | 说明 |
|---|---|---|---|
id | string | 总是 | 任务 ID, 格式 cgt-yyyymmddhhmmss-xxxxx(跟 Volcengine 即梦官方一致) |
task_id | string | 总是 | id 的别名(deprecated, 推荐用 id) |
object | string | 总是 | 固定 "video" |
model | string | 总是 | 客户端传入的模型名,不是上游 ant-2-* |
status | string | 总是 | queued / in_progress / completed / failed |
progress | int | 总是 | 0 / 50 / 100 |
created_at | int64 | 总是 | 创建时间 Unix 秒 |
completed_at | int64 | 总是 | 最后状态变化时间 |
metadata.url | string | 仅 completed | 视频下载 URL, 24h 有效 |
metadata.usage | object | 仅 completed | {completion_tokens, total_tokens} |
metadata.resolution | string | 仅 completed | 实际生成分辨率 |
metadata.duration | int | 仅 completed | 实际生成时长(秒) |
metadata.ratio | string | 仅 completed | 实际宽高比 |
metadata.framespersecond | int | 仅 completed | 实际帧率 |
metadata.seed | int64 | 仅 completed,且 prompt 给定 | 实际使用的种子 |
metadata.last_frame_url | string | 仅 return_last_frame=true | 末帧静态图 URL |
error | object | 仅 failed | {"message": "...", "code": "..."} |
Volcengine 风格响应 /api/v3/contents/generations/tasks/{task_id}
跟 Volcengine 即梦 / ByteDance Ark 官方 Seedance 响应字段完全一致,包括 id = cgt-yyyymmddhhmmss-xxxxx 格式 — 任何为 Volcengine SDK 写的客户端代码可以直接对接。
排队中 / 生成中
{
"id": "cgt-20260527165600-kf7rt",
"model": "seedance-2.0-fast",
"status": "queued",
"created_at": 1779869660,
"updated_at": 1779869660
}status 在生成中变为 running,其他字段不变。
完成 (status = succeeded)
{
"id": "cgt-20260527165600-kf7rt",
"model": "seedance-2.0-fast",
"status": "succeeded",
"content": {
"video_url": "https://ark-aigc-cn-beijing.tos-cn-beijing.volces.com/.../xxxxxx.mp4?X-Tos-Signature=...",
"last_frame_url": "https://..."
},
"usage": {
"completion_tokens": 40594,
"total_tokens": 40594
},
"seed": 42,
"resolution": "720p",
"duration": 5,
"ratio": "16:9",
"framespersecond": 24,
"created_at": 1779872160,
"updated_at": 1779872279,
"execution_expires_after": 172800,
"billing": {
"amount": 1.50,
"group_ratio": 1.0
}
}字段说明(Volcengine 风格)
| 字段 | 类型 | 何时存在 | 说明 |
|---|---|---|---|
id | string | 总是 | cgt-yyyymmddhhmmss-xxxxx(跟官方一致) |
model | string | 总是 | 客户端传入的模型名 |
status | string | 总是 | queued / running / succeeded / failed / expired / cancelled |
content.video_url | string | 仅 succeeded | 视频下载 URL, 24h 有效 |
content.last_frame_url | string | 仅 return_last_frame=true | 末帧静态图 URL |
usage.completion_tokens | int | 仅 succeeded | 生成阶段消耗 token |
usage.total_tokens | int | 仅 succeeded | 总消耗 token(计费维度) |
seed | int64 | 仅 succeeded | 实际使用的种子 |
resolution | string | 仅 succeeded | 实际分辨率 |
duration | int | 仅 succeeded | 实际时长(秒) |
ratio | string | 仅 succeeded | 实际宽高比 |
framespersecond | int | 仅 succeeded | 实际帧率 |
created_at / updated_at | int64 | 总是 | Unix 秒 |
execution_expires_after | int | 总是 | 任务记录过期窗口(秒),通常 172800 = 48h |
error | object | 仅 failed/expired/cancelled | {"message": "...", "code": "..."} |
billing | object | 仅 succeeded | 网关扩展字段(Volcengine 官方没有): {amount: CNY, group_ratio} |
失败 (两个路由都是 failed)
OpenAI 风格:
{
"id": "cgt-xxx",
"object": "video",
"status": "failed",
"error": {"message": "upstream task failed: content moderation rejected prompt", "code": "1"},
"metadata": {}
}Volcengine 风格(还会区分 expired / cancelled):
{
"id": "cgt-xxx",
"model": "seedance-2.0-fast",
"status": "failed",
"error": {"message": "upstream task failed: content moderation rejected prompt", "code": "1"}
}终态为
failed/expired/cancelled时 预扣费会全额退回, 不用人工申请。
状态机
queued ──→ in_progress / running ──→ completed / succeeded
│ │ (terminal,按实际 token 结算)
│ │
│ └──→ failed (terminal,全额退款)
│
└──→ failedOpenAI 路由 (/v1/videos/...)
| 状态 | 含义 | 是否终态 | 是否退款 |
|---|---|---|---|
queued | 排队中 | 否 | — |
in_progress | 生成中 | 否 | — |
completed | 完成 | 是 | 按实际 token 结算,多退少补 |
failed | 失败 | 是 | 全额退款 |
unknown | 未知 (基本不出现) | 否 | — |
Volcengine 路由 (/api/v3/contents/generations/tasks/...)
| 状态 | 等价 OpenAI |
|---|---|
queued | queued |
running | in_progress |
succeeded | completed |
failed | failed |
两个路由背后是同一个任务对象, 只是网关在序列化时按路由替换了状态字符串。 同一个 task_id, 通过两个路由查会看到不同的 status 字符串, 但语义完全一样。
一旦进入终态, 状态不会再变。
轮询策略
推荐间隔
| 任务阶段 | 间隔 |
|---|---|
| 任务刚提交 (< 30s) | 5-10 秒 |
| 一段时间后 (> 30s) | 10-15 秒 |
| 很长时间后 (> 5min) | 30 秒 |
最长等待时间
- Fast 模型: 通常 1-2 分钟完成
- Pro 模型: 通常 2-5 分钟完成
- 极端情况: 上游忙时可能 10 分钟以上
超过 24 小时未完成由上游标记失败,自动退款。
不要做的事
- ❌ 1 秒查一次(会触发 rate limit)
- ❌ 同时查 100 个任务(并发限流)
- ❌ 任务完成后还继续查(浪费 API 调用)
- ❌ 在循环里
while True: get_status()不带 sleep —— 会被 429
完整 Python 轮询示例
import requests
import time
def wait_for_video(task_id: str, api_key: str, max_wait_seconds: int = 600):
"""
轮询直到任务终态, 返回视频 URL。
使用 OpenAI 风格路由, 所以终态字符串是 'completed' / 'failed'。
"""
base_url = 'https://www.dianlitoken.com'
headers = {'Authorization': f'Bearer {api_key}'}
deadline = time.time() + max_wait_seconds
poll_interval = 10
while time.time() < deadline:
resp = requests.get(f'{base_url}/v1/videos/{task_id}', headers=headers)
resp.raise_for_status()
data = resp.json()
status = data['status']
if status == 'completed':
return data['metadata']['url']
if status == 'failed':
err = data.get('error', {})
raise RuntimeError(
f"Task failed: {err.get('message', 'unknown')}"
)
time.sleep(poll_interval)
raise TimeoutError(f'Task {task_id} did not complete within {max_wait_seconds}s')
# 使用
video_url = wait_for_video('cgt-20260527165600-kf7rt', 'sk-xxx')
print(f'Video ready: {video_url}')兼容两种状态枚举
如果你需要写跨网关 / 兼容两种风格的代码:
TERMINAL_OK = {'completed', 'succeeded'}
TERMINAL_FAIL = {'failed'}
if data['status'] in TERMINAL_OK:
...
elif data['status'] in TERMINAL_FAIL:
...视频下载
直接下载
curl -o video.mp4 "https://ark-aigc-cn-beijing.tos-cn-beijing.volces.com/.../xxxxxx.mp4?X-Tos-Signature=..."URL 有效期
⚠️ 24 小时(上游 Volcengine TOS 临时签名 URL)。 过期后:
- 链接会返回 403 Forbidden
- 无法恢复, 必须重新生成任务
推荐做法 — 立即转存
import requests
# 任务完成立即下载, 转存到自己的 S3 / OSS / 本地
video_data = requests.get(video_url).content
with open(f"videos/{task_id}.mp4", "wb") as f:
f.write(video_data)或者上传到自己的对象存储 (七牛, 阿里 OSS, 腾讯 COS 等):
import boto3 # AWS S3 SDK
s3 = boto3.client('s3')
s3.put_object(Bucket='my-bucket', Key=f"{task_id}.mp4", Body=video_data)错误响应
任务 ID 不存在 / 不属于你 (404)
{
"error": {
"type": "new_api_error",
"message": "task cgt-xxx not found"
}
}为了安全, 任务属于其他用户时也返回 404(防止暴力枚举 task_id)。
任务还没完成就尝试拿视频 URL
如果你直接调代理下载接口 /v1/videos/{task_id}/content 而任务还没完成, 会返 4xx。 正确做法: 先用 /v1/videos/{task_id} 查 status, 看到终态再访问视频 URL。
替代方案: 用回调代替轮询
如果不想轮询, 提交任务时传 callback_url, 任务完成后上游主动 POST 通知你:
{
"model": "seedance-2.0-fast",
"content": [{"type": "text", "text": "..."}],
"callback_url": "https://your-server.com/webhook/seedance"
}回调字段跟查询字段不一样 — 回调是上游官方 直发的 envelope, 字段更丰富(包含 tokens / extra.money)。 详见 04 回调。
下一篇: 04 回调 (Webhook) →