视频生成
异步视频:OpenAI `/v1/videos` 与火山方舟 `/api/v3/...` 两套协议,共用路由与计费。
两套协议,一条链路#
视频是异步任务:先创建拿到任务 id,再轮询(或等 callback_url)到终态后取产物。把视频模型发到 /v1/chat/completions 会返回 invalid_request。
同一个 key,同一套计费
两条入口共用鉴权、路由、计费与产物托管,差别只在请求与响应的协议形状。方舟路径返回的
id 是平台任务号,不是上游的 cgt-...。OpenAI:创建任务#
POST /v1/videos。JSON 体用于文生视频;带参考图时请改用 multipart/form-data(见下)。
modelstring必填- 视频模型名,可在 模型广场 按视频模态筛选。
promptstring- 文本提示词。
prompt与参考图至少要给一个,两个都没有返回invalid_request。 input_referencefile- 参考图/首帧。只支持 multipart 文件字段(映射为平台
first_frame)。当前 JSON 体里的 URL / data URL 不会被读取;多图、尾帧、参考视频请改用下方 Ark 协议。 secondsstring | integer必填- 时长秒数,字符串与整数都接受。
sizestring- 如
1280x720,等价于分别指定resolution与aspect_ratio。 resolutionstring480p/720p/1080p/4k。aspect_ratiostring- 如
16:9、9:16。 fpsinteger- 帧率。
generate_audioboolean- 是否同时生成音轨(取决于模型是否支持)。
watermarkboolean- 是否加水印。
seedinteger- 随机种子,用于复现同一结果。
callback_urlstring- 任务终态回调地址。提供该地址后即无需轮询。
重复提交是安全的
带上
Idempotency-Key 请求头,重复提交会返回首次创建的任务,而不是再生成一条。文生视频(JSON)
bash
curl https://ogrouter.ai/v1/videos \ -H "Authorization: Bearer $AIROUTER_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: my-job-001" \ -d '{ "model": "doubao-seedance-1-0-pro", "prompt": "A cat surfing a wave, cinematic", "seconds": 5, "resolution": "720p", "aspect_ratio": "16:9" }'单张首帧(multipart)
bash
curl https://ogrouter.ai/v1/videos \ -H "Authorization: Bearer $AIROUTER_API_KEY" \ -H "Idempotency-Key: my-job-002" \ -F model=doubao-seedance-1-0-pro \ -F prompt="让照片里的人物轻轻转头微笑" \ -F seconds=5 \ -F resolution=720p \ -F aspect_ratio=16:9 \ -F input_reference=@./first-frame.png火山方舟:多参考图与角色#
若已在使用方舟官方 SDK,将 base URL 指向 {origin}/api/v3、密钥换成 sk-ar- 即可,content[] 无需改动。多参考图、首尾帧或参考视频请使用本协议;OpenAI 协议仅支持单个 multipart input_reference。
modelstring必填- 与
/v1/videos同一目录里的视频模型 slug。 contentarray必填- 内容块列表。可含多个
text/image_url/video_url/audio_url;text会拼成提示词,素材块进入参考列表。 content[].rolestringfirst_frame/last_frame/reference_image/reference_video/reference_audio。缺省时按参考素材处理。durationinteger必填- 时长秒数(方舟字段名是
duration,不是seconds)。 resolution / ratio / …string | boolean | integerresolution、ratio、framespersecond、generate_audio、watermark、seed、callback_url与方舟一致。
首帧 + 一张参考图
bash
curl https://ogrouter.ai/api/v3/contents/generations/tasks \ -H "Authorization: Bearer $AIROUTER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "doubao-seedance-1-0-pro", "content": [ { "type": "text", "text": "镜头从近景缓缓拉开,电影感光线" }, { "type": "image_url", "role": "first_frame", "image_url": { "url": "https://example.com/first.png" } }, { "type": "image_url", "role": "reference_image", "image_url": { "url": "https://example.com/style.png" } } ], "resolution": "720p", "ratio": "16:9", "duration": 5 }'参考素材必须是公网 URL
方舟协议只接受 URL,不支持 multipart。素材若在内网,请先放到上游可访问的地址。使用 OpenAI multipart 上传时,平台会托管素材并以签名 URL 传递给上游。
轮询状态#
用创建时同一套协议的 GET 轮询到终态即可。OpenAI 与方舟的状态字不同,不要混用。
OpenAI 协议查询
bash
curl https://ogrouter.ai/v1/videos/$VIDEO_ID \ -H "Authorization: Bearer $AIROUTER_API_KEY"方舟协议查询
bash
curl https://ogrouter.ai/api/v3/contents/generations/tasks/$VIDEO_ID \ -H "Authorization: Bearer $AIROUTER_API_KEY"建议自 2 秒起轮询,并逐步拉长至约 15 秒。提高轮询频率并不会缩短生成时间,只会更快触发限流。
Python:OpenAI 协议创建并退避轮询
python
import os, time, requests
API = "https://ogrouter.ai/v1"HEADERS = {"Authorization": f"Bearer {os.environ['AIROUTER_API_KEY']}"}
task = requests.post( API + "/videos", headers=HEADERS, json={ "model": "volcengine/doubao-seedance", "prompt": "一只柴犬在维港边慢跑,电影感镜头", "seconds": 5, }, timeout=60,).json()
delay = 2while True: time.sleep(delay) delay = min(delay * 1.5, 15) state = requests.get(API + "/videos/" + task["id"], headers=HEADERS, timeout=30).json() if state["status"] == "completed": print(state["content"]["video_url"]) break if state["status"] == "failed": raise RuntimeError(state.get("error", {}).get("message", "failed")) print(state["status"])Node.js:创建任务并退避轮询
javascript
const API = 'https://ogrouter.ai/v1';const headers = { Authorization: `Bearer ${process.env.AIROUTER_API_KEY}` };
const created = await fetch(API + '/videos', { method: 'POST', headers: { ...headers, 'Content-Type': 'application/json' }, body: JSON.stringify({ model: 'volcengine/doubao-seedance', prompt: '一只柴犬在维港边慢跑,电影感镜头', seconds: 5, }),});const task = await created.json();
// 从 2 秒起退避到 15 秒;轮询更密不会让任务更快完成let delay = 2;for (;;) { await new Promise((resolve) => setTimeout(resolve, delay * 1000)); delay = Math.min(delay * 1.5, 15);
const state = await (await fetch(API + '/videos/' + task.id, { headers })).json(); if (state.status === 'completed') { console.log(state.content.video_url); break; } if (state.status === 'failed') { throw new Error(state.error?.message ?? 'failed'); } console.log(state.status);}Java:创建任务并退避轮询
java
import java.net.URI;import java.net.http.HttpClient;import java.net.http.HttpRequest;import java.net.http.HttpResponse;import com.fasterxml.jackson.databind.JsonNode;import com.fasterxml.jackson.databind.ObjectMapper;
// JSON 解析用 Jackson(com.fasterxml.jackson.core:jackson-databind)String api = "https://ogrouter.ai/v1";String auth = "Bearer " + System.getenv("AIROUTER_API_KEY");HttpClient http = HttpClient.newHttpClient();ObjectMapper mapper = new ObjectMapper();
String createBody = """ { "model": "volcengine/doubao-seedance", "prompt": "一只柴犬在维港边慢跑,电影感镜头", "seconds": 5 } """;
HttpResponse<String> created = http.send( HttpRequest.newBuilder(URI.create(api + "/videos")) .header("Authorization", auth) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(createBody)) .build(), HttpResponse.BodyHandlers.ofString());
String taskId = mapper.readTree(created.body()).get("id").asText();
// 从 2 秒起退避到 15 秒;轮询更密不会让任务更快完成double delay = 2;while (true) { Thread.sleep((long) (delay * 1000)); delay = Math.min(delay * 1.5, 15);
HttpResponse<String> polled = http.send( HttpRequest.newBuilder(URI.create(api + "/videos/" + taskId)) .header("Authorization", auth) .GET() .build(), HttpResponse.BodyHandlers.ofString());
JsonNode state = mapper.readTree(polled.body()); String status = state.get("status").asText(); if ("completed".equals(status)) { System.out.println(state.get("content").get("video_url").asText()); break; } if ("failed".equals(status)) { throw new IllegalStateException(polled.body()); } System.out.println(status);}能力边界#
- OpenAI 协议:最多一张 multipart 首帧;多图 / 尾帧 / 参考视频请用 Ark
content[]。 - 参考素材能否真正出站,取决于路由到的上游:Ark / 可灵等可收 URL;若命中 OpenAI Videos 上游,带参考图会被拒绝并提示改走 Ark / 可灵通道。
- 单模型允许多少参考、多久、哪些分辨率,以 模型广场 卡片上的视频能力(含
max_refs、时长与分辨率列表)为准。 - 产物 URL 有留存期;需要长期保存请自行转存。签名地址不需要再带 API Key。
列表、下载与取消#
计费#
视频有两种对客计费模式:按秒(per_second)与按 token(per_token,含/不含参考视频两档)。具体用哪种取决于模型接的上游,模型广场 的卡片与详情页显示的就是这两种口径。含参考视频(不是参考图)时,per_token 走「含参考视频」那一档。
- 按秒计费的模型,单价可能随分辨率分档,开音轨也可能另算——详情页显示的是实际生效的那一档。
- 创建时按估算的最终金额预扣,并在任务结束前保持冻结;对话请求的预扣口径与此不同。
- 失败与取消不计费,口径与对话端点一致,见 计费口径。