在火山方舟上轮询 Seedance 任务,四条事实就够用。查询端点是 GET https://ark.cn-beijing.volces.com/api/v3/contents/generations/tasks/{id}。状态字段叫 status,一共六个取值。成片地址在 content.video_url。这个地址的有效期是 24 小时。API 总览讲的是创建那一侧,下面全是创建之后的循环。

轮询到底能拿回哪些字段

创建调用只还你一个任务 ID,能直接用的信息很少。官方文档列了 22 个响应字段,其中六个决定 worker 的下一步动作。

字段它告诉你的代码什么
status任务在跑、跑完,还是已经死了
content.video_url托管好的视频,成功后才出现
content.last_frame_url尾帧图像,同一个 24 小时窗口
error失败的任务失败在哪
execution_expires_after任务超时阈值,单位秒
usagetoken 用量,成功才计费

官方那条响应示例是 2.0 的真实回包,不是 2.5 的:"model": "doubao-seedance-2-0-260128""resolution": "720p""duration": 5"usage": { "completion_tokens": 108900, "total_tokens": 108900 }。那个 token 数只能按 2.0 的形态看,别拿它估 2.5。

端点与状态字段

查询调用是对创建时那个集合发 GET,任务 ID 是必选的 Path 参数。鉴权用同一把长效方舟 Key:Authorization: Bearer $ARK_API_KEY

六个状态取值

status 是字符串,官方给了六个取值。把每个取值对应的处理动作写进分支语句,能省掉大部分猜测:

  • queued — 排队中,继续等
  • running — 任务运行中,继续等
  • cancelled — 取消任务,取消状态 24h 自动删除,属于终态
  • succeeded — 任务成功,去取 content.video_url
  • failed — 任务失败,读 error
  • expired — 超出 execution_expires_after,属于终态

其中两个会改变循环的出口。cancelled 只能从 queued 到达,记录会在 24 小时后自己删掉。expired 说明任务熬过了 execution_expires_after,官方给的默认值是 172800 秒。还有一点容易忽略:这套词表里没有 pending,所以等 pendingprocessing 的循环会一直等下去。端点参考里有请求侧的完整字段。

轮询循环怎么写

一张视频生成任务的状态机示意:created 节点分叉到 running,再分叉到 succeeded 或 failed,结果 URL 上画着一条到期计时。

启动循环的创建调用长这样:

curl -X POST https://ark.cn-beijing.volces.com/api/v3/contents/generations/tasks \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $ARK_API_KEY" \
  -d '{
    "model": "doubao-seedance-2-5-260628",
    "content": [
      { "type": "text", "text": "A workshop at dawn, hands assembling a clock movement." }
    ],
    "ratio": "16:9",
    "duration": 5
  }'

bash 版循环

TASK_ID="your_task_id"

while :; do
  payload=$(curl -s -X GET \
    "https://ark.cn-beijing.volces.com/api/v3/contents/generations/tasks/${TASK_ID}" \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer $ARK_API_KEY")
  status=$(printf '%s' "$payload" | jq -r .status)

  case "$status" in
    queued|running) sleep 10 ;;
    succeeded)
      curl -sL -o "seedance-${TASK_ID}.mp4" \
        "$(printf '%s' "$payload" | jq -r .content.video_url)"
      break ;;
    failed|expired|cancelled)
      printf '%s' "$payload" | jq -r .error
      break ;;
    *) echo "unexpected status: $status"; break ;;
  esac
done

这个 case 没有兜底的 sleep,遇到不认识的字符串会让循环结束,而不是在里面空转。这一点比间隔更重要:官方没有公布推荐的轮询间隔。

官方 SDK 版循环

方舟提供官方 Python SDK,包名是 volcenginesdkarkruntime

import os
import time

from volcenginesdkarkruntime import Ark

client = Ark(api_key=os.environ["ARK_API_KEY"])

task = client.content_generation.tasks.create(
    model="doubao-seedance-2-5-260628",
    content=[{"type": "text", "text": "A workshop at dawn."}],
    ratio="16:9",
    duration=5,
)

print(task.id)

TERMINAL = {"succeeded", "failed", "expired", "cancelled"}

while True:
    result = client.content_generation.tasks.get(task_id=task.id)
    if result.status in TERMINAL:
        break
    time.sleep(10)

print(result.status)
print(getattr(result.content, "video_url", None))

取消或删除任务

同样是这六个取值,决定 DELETE 能不能发出去。DELETE https://ark.cn-beijing.volces.com/api/v3/contents/generations/tasks/{id} 干的是两件不同的事,还直接拒绝两个状态。

当前任务状态是否支持 DELETE操作含义操作后状态
queued任务取消排队,状态被变更为 cancelledcancelled
running
succeeded删除记录,后续将不支持查询
failed删除记录,后续将不支持查询
cancelled
expired删除记录,后续将不支持查询

成功的 DELETE 返回 HTTP 200 和一个空对象 {}。运行中的任务停不下来,所以界面上的取消按钮得接受「点了可能什么也没发生」。

同一个路径还有一个列表调用,收 page_numpage_size(默认 1 和 20,取值 [1, 500]),外加 filter.statusfilter.task_idsfilter.model。对账时以它为准,不要把自己的账本当唯一真相。

结果 URL 的 24 小时窗口

三个窗口各自公布,互不对齐。

窗口取值
任务记录可查询最近 7 天
结果 URL 有效期24 小时
2.5 结果 URL 的下载次数上限100 次

官方措辞很精确。记录窗口:仅支持查询最近 7 天的任务记录,时间区间为 [T-7 天, T),其中 T 为请求发起时刻的 UTC 时间戳(精确到秒)。 URL:视频 URL 有效期为 24 小时,请及时下载或转存。 次数:Seedance 2.5 模型生成的视频 URL 下载次数上限为 100 次。 尾帧图像走的是同一个时钟。

在 URL 过期之前做什么

轮询一次,然后就转存。不要把方舟那条 URL 直接发给用户,第 100 次下载会让它对所有人失效。

import os

import requests

from volcenginesdkarkruntime import Ark

client = Ark(api_key=os.environ["ARK_API_KEY"])
result = client.content_generation.tasks.get(task_id="your_task_id")

if result.status == "succeeded":
    response = requests.get(result.content.video_url, timeout=120)
    response.raise_for_status()

    with open(f"seedance-{result.id}.mp4", "wb") as handle:
        handle.write(response.content)

    print("archived", len(response.content), "bytes")

纪律就这一条:一次下载,进自己的存储,立刻做。每次轮询都下载一边,会把下载额度和时间一起烧掉。如果在意 token 那一侧,价格拆解里有公式。

官方没有公布什么

本次核查中官方没有公布:

  • 推荐的轮询间隔和退避策略。官方只说支持轮询、也可以用 Webhook 通知,就到此为止。
  • 请求被限流时返回的 HTTP 状态码与响应头。本次读到的官方页面里没有出现 429,也没有出现 Retry-After
  • 任何请求追踪头。想把一次轮询和一张工单对上,官方只给了任务 ID。
  • 任务耗时区间、队列深度、延迟和成功率保证。
  • 这个模型的免费额度。

官方示例里的 ID 形如 cgt-2026****-****,但那个形态从没被写成规则。分支语句按 status 走,不要按 ID 前缀走。

常见问题

用哪个端点轮询任务? GET https://ark.cn-beijing.volces.com/api/v3/contents/generations/tasks/{id},任务 ID 放在 Path 参数里,方舟 Key 放在 Authorization 头里。

六个状态取值是哪六个? queuedrunningcancelledsucceededfailedexpired。没有 pendingprocessing,等这两个的循环会一直等下去。

我有多久时间去取视频? 24 小时,2.5 的结果 URL 最多下载 100 次。任务记录本身可查 7 天。

运行中的任务能停吗? 不能。只有 queued 能被取消,runningcancelled 都会拒绝 DELETE。

能不能完全不轮询? 官方文档另有回调方式作为替代,但本次核查没能读到它的字段细节,所以这条路线在这里只算「有文档、未核实」。

上线前把每个字段和每个窗口对着官方页面核一遍;限流和有效期这两套规则都不太预告就变。