Sora2U 视频生成开放 API
通过本 API,任何 Sora2U 用户都可以用自己的 API 密钥,在自己的程序 / 脚本 / 服务端里调用网站的视频生成能力。每次生成都会从该用户的积分(GP)余额中正常扣费, 计费规则与网页端完全一致。
- Base URL:
https://sora2u.com - 协议:HTTPS,请求/响应均为 JSON(创建任务的参考图用 base64 内联)
- 鉴权:API 密钥(
Authorization: Bearer <key>) - 风格:任务为异步——创建后先返回任务 ID,再轮询查询结果
机器可读的接口定义见同目录下的
openapi.yaml,可直接导入 Postman / Swagger UI / openapi-generator。
目录
- 快速开始(5 分钟跑通)
- 获取与管理 API 密钥
- 鉴权方式
- 生成一条视频
- 查询任务状态
- 其他接口
- 计费与积分扣减
- 任务状态机
- 错误码
- 最佳实践
- 完整示例(Node.js / Python)
- 给维护者:上线步骤
快速开始(5 分钟跑通)
# 0. 先在网页端登录后创建一把密钥(见下一节),拿到形如 sk_sora_xxx 的明文密钥
export SORA_KEY="sk_sora_你的密钥"
# 1. 查询余额,确认有积分
curl -s https://sora2u.com/api/v1/credits \
-H "Authorization: Bearer $SORA_KEY"
# 2. 创建一条文生视频任务
curl -s -X POST https://sora2u.com/api/v1/videos \
-H "Authorization: Bearer $SORA_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt":"一只柯基在海边奔跑,电影质感,黄昏光线","model":"seedance-2.0","duration":5}'
# => { "success": true, "task": { "id": "ckxxx", "status": "pending", ... } }
# 3. 用上一步返回的 task.id 轮询结果(每 5 秒一次)
curl -s https://sora2u.com/api/v1/videos/ckxxx \
-H "Authorization: Bearer $SORA_KEY"
# 当 status = "completed" 时,video_url 即为成片地址获取与管理 API 密钥
API 密钥与你的账号绑定,代表你本人调用接口并消耗你的积分。请像对待密码一样保管它。
密钥的创建 / 列出 / 吊销使用网页端登录态(Cookie 会话)完成,通常由控制台页面调用:
创建密钥 · POST /api/api-keys
请求体(均可选):
| 字段 | 类型 | 说明 |
|---|---|---|
name | string | 密钥备注名,便于区分用途,最长 60 字符,默认「默认密钥」 |
expires_in_days | number | 有效天数(1–3650)。不传则永不过期 |
响应(201)——明文密钥只在这一次返回,请立刻保存:
{
"success": true,
"api_key": "sk_sora_AbC1d2Ef...完整明文...",
"key": {
"id": "ckkey123",
"name": "我的脚本",
"key_prefix": "sk_sora_AbC1…",
"expires_at": null,
"created_at": "2026-06-17T12:00:00.000Z"
},
"warning": "请妥善保存该密钥,它只会显示这一次,无法再次查看。"
}列出密钥 · GET /api/api-keys
只返回前缀与状态,永不返回明文:
{
"success": true,
"keys": [
{
"id": "ckkey123",
"name": "我的脚本",
"key_prefix": "sk_sora_AbC1…",
"status": "active",
"last_used_at": "2026-06-17T12:30:00.000Z",
"expires_at": null,
"revoked_at": null,
"created_at": "2026-06-17T12:00:00.000Z"
}
]
}status 取值:active(有效)、expired(已过期)、revoked(已吊销)。
吊销密钥 · DELETE /api/api-keys/{id}
立即失效,操作幂等(重复吊销也返回成功)。一旦泄露,请第一时间吊销并重建。
单账号最多保留 20 把有效密钥,超出需先吊销旧密钥。
鉴权方式
所有 /api/v1/* 接口都需要在请求头携带密钥,支持两种写法(推荐第一种):
Authorization: Bearer sk_sora_你的密钥x-api-key: sk_sora_你的密钥鉴权失败统一返回 401:
{ "error": { "code": "unauthorized", "message": "缺少或无效的 API 密钥..." } }服务端只保存密钥的 SHA-256 哈希,不存明文;因此一旦丢失只能重新创建,无法找回。
跨域调用(CORS)
/api/v1/* 已开启 CORS,可从任意网页来源直接在浏览器里调用(OpenAI 兼容客户端的「Base URL + API Key」直连模式):
- 所有响应都带
Access-Control-Allow-Origin: *; - 浏览器在带
Authorization头或用POST/DELETE前会先发OPTIONS预检,本 API 已正确响应(204+Access-Control-Allow-*); - 鉴权用
Authorization: Bearer/x-api-key(非 Cookie),因此放行任意来源且不需要credentials。
注意:密钥即代表你本人扣费。在纯前端(浏览器)使用时,密钥会暴露给终端用户——仅在可信场景(如用户填自己的密钥)这样用;面向公众的页面请改由你自己的后端代理转发,不要把密钥写进前端代码。
生成一条视频
POST /api/v1/videos
请求体(JSON):
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
prompt | string | ✅ | 视频描述,至少 10 个字符。可嵌入 `< |
model | string | ❌ | 模型名,默认 seedance-2.0(见 GET /api/v1/models) |
duration | number | ❌ | 时长(秒)。会按模型支持范围自动取整夹取 |
aspect_ratio | string | ❌ | 画幅,如 9:16、16:9(取决于模型支持) |
resolution | string | ❌ | 分辨率,如 720p |
mute / disable_audio | boolean | ❌ | 静音生成:为 true 时请求引擎不自动配乐 / 不输出音轨,用于规避 Seedance 2.0 自动 BGM 触发 output_audio_copyright(版权 / 敏感)导致的失败。默认 false |
reference | string | ❌ | 内联参考素材(图片 / 视频 / 音频)的 base64,可带 data:<mime>;base64, 前缀。受请求体 ~4.5MB 上限约束,适合图片与较小文件。详见下文「参考素材上传」一节 |
reference_url | string | ❌ | 参考素材的公开 https 直链,服务端下载后转交引擎。较大的视频 / 音频用它(绕过请求体大小限制)。与 reference 同传时两者都会作为参考一并提交(合并计数) |
references | string[] | ❌ | 多参考素材的 base64 数组(可混合图片 / 视频 / 音频:图片最多 9 张,视频与音频合用一个 3 槽池、合计 ≤ 3,总数最多 12 个);每个元素规则同 reference。详见「参考素材上传 · 多参考(叠加)」 |
reference_urls | string[] | ❌ | 多参考素材的公开 https 直链数组(上限同 references,两者合并计数);每个元素规则同 reference_url。可与 references 混用 |
image / image_base64 | string | ❌ | reference 的向后兼容别名(图片场景) |
响应(202 Accepted)——任务已创建并已预扣积分,开始后台生成:
{
"success": true,
"task": {
"id": "ckxxx",
"status": "pending",
"model": "seedance-2.0",
"model_name": "Seedance 2.0",
"mode": "image-to-video",
"duration": 5,
"estimated_credits": 100,
"estimated_time": "2-6分钟",
"created_at": "2026-06-17T12:00:00.000Z"
},
"links": { "self": "/api/v1/videos/ckxxx" }
}注意:
estimated_credits是预扣金额,最终以任务完成时的实际成本结算(多退少补,见计费)。
示例(图生视频,兼容 image 字段、裸 base64):
B64=$(base64 -i ./ref.png | tr -d '\n')
curl -s -X POST https://sora2u.com/api/v1/videos \
-H "Authorization: Bearer $SORA_KEY" \
-H "Content-Type: application/json" \
-d "{\"prompt\":\"让画面里的角色缓缓转头微笑\",\"model\":\"seedance-2.0\",\"image\":\"$B64\"}"智能模型选择(无参考素材时自动「文生视频」)
视频模型有两种工作形态:图生视频 / 视频生视频 / 音频驱动(需要参考素材)与文生视频(纯文本,无需素材)。你无需关心、也无法单独指定内部的文生视频模型——后端按「有没有参考素材」自动判断:
- 提供了参考素材(
reference/reference_url/references/reference_urls任一)→ 按你选的模型做图生视频 / 视频生视频 / 音频驱动; - 没有提供任何参考素材 → 自动切换到同系的「文生视频」形态,直接按
prompt生成视频。
也就是说,想纯文本生成视频时,只要不传任何参考字段即可(model 用 seedance-2.0 或省略走默认)。计费、时长、画幅都按实际生效的形态结算。GET /api/v1/models 中 supports_text_only: true 的模型即代表「无素材也能生成」。
响应里看到的是什么? 无论是否自动回退,响应中的
model/model_name始终是你选择的可见模型(如seedance-2.0)——你不会看到任何内部 / 隐藏的回退模型名。响应另带一个mode字段标明本次实际形态:text-to-video(没传素材,自动文生视频)、image-to-video(传了图片 / 视频 / 音频参考)或image-generation(图片模型)。所以「没传素材时返回text-to-video」是预期行为,并不代表接口不支持上传素材——上传素材时它就是image-to-video。mode字段在创建响应和任务查询响应里都有。
# 纯文本生成视频:不带任何 reference 字段即可,后端自动走文生视频
curl -s -X POST https://sora2u.com/api/v1/videos \
-H "Authorization: Bearer $SORA_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt":"赛博朋克城市夜景,霓虹灯下的雨夜街道,镜头缓缓推进","model":"seedance-2.0","duration":5}'模型能力一览
以 GET /api/v1/models 返回为准;下表为常用模型的参考素材能力速查:
模型(model) | 输出 | 文生 | 图参考 | 视频参考 | 音频参考 |
|---|---|---|---|---|---|
seedance-2.0 | 视频 | ✅ | ✅ | ✅ | ✅ |
seedance-2.0-character | 视频 | ✅ | ✅ | ✅ | ✅ |
seedance-1.5 | 视频 | ✅ | ✅ | ❌ | ❌ |
gemini-image / kontext-image | 图片 | ✅ | ✅ | ❌ | ❌ |
- 全模态(
seedance-2.0/seedance-2.0-character):文 / 图 / 视频 / 音频参考都支持。 - 仅图(
seedance-1.5):只支持图片参考;传视频 / 音频参考会在创建时400 unsupported_reference。 - 图片生成(
gemini-image/kontext-image):只支持图片参考(最多 4 张),不支持视频 / 音频。
参考素材上传(图片 / 视频 / 音频)
POST /api/v1/videos 支持三类参考素材:图片(图生视频)、视频(视频生视频)、音频(音频驱动)。
有两种把素材交给接口的方式,按文件大小二选一:
| 方式 | 字段 | 适合 | 上限 | 说明 |
|---|---|---|---|---|
| 内联 base64 | reference | 图片、较小的短视频 / 音频 | 单次请求体 ~4.5MB(平台硬限制) | 直接把 base64 放进 JSON。简单,但大文件会触发 413 FUNCTION_PAYLOAD_TOO_LARGE |
| 远程直链 | reference_url | 较大的视频 / 音频 | 图片 10MB、视频 100MB、音频 50MB | 把文件放在公开 https 地址,服务端下载后转交引擎,绕过请求体上限 |
通用规则:
- 类型能力由模型决定:
GET /api/v1/models的supports_image/supports_video/supports_audio标明每个模型支持哪些参考类型。seedance-2.0(Seedance 2.0)支持图片 / 视频 / 音频;seedance-1.5(Seedance 1.5)仅支持图片。给不支持该类型的模型传对应参考会在创建时直接返回400 unsupported_reference(如给seedance-1.5传视频 / 音频参考),不会等到渲染阶段才失败。 - ⚠️ 参考图必须是真实、有内容的图片:请勿用纯色块、占位图或明显合成 / AI 生成的图去「试上限」。这类低信息量图片会触发引擎的低质内容审核而失败(错误码
10001305,文案常写作「社区准则」),这并非真正的内容违规——换成真实照片后同样张数即可通过。用占位图测试会 100% 失败并让你误判上限。 - 视频参考须宽度 ≥ 300px、时长 < 15 秒(音频同样须 < 15 秒):
- 宽度 < 300px:对 MP4 / MOV 视频,创建时即预判并返回
400 invalid_reference(message形如「参考视频宽度须 ≥ 300px(当前约 200px)」);无法预判的容器(如 WebM)仍由引擎在渲染阶段兜底。 - 时长超 15 秒:走
reference_url时在创建请求时同步返回400 invalid_reference_url,message形如Video is 18.0s — reference must be under 15s.;走reference(base64) 时在后台处理阶段判失败,error给出原因,预扣积分自动退还。
- 宽度 < 300px:对 MP4 / MOV 视频,创建时即预判并返回
- base64 传视频 / 音频务必用 data URL:裸 base64 没有类型信息会被当成图片。即
reference要写成data:video/mp4;base64,...、data:audio/mpeg;base64,...。 reference与reference_url同时提供时,两者都会作为参考一并提交(与references/reference_urls合并为一组,按同一套数量上限计数)。- 多参考(叠加):用
references(base64 数组)/reference_urls(直链数组)一次传多份素材,引擎会把它们一并作为参考输入。视频生成(如seedance-2.0)支持混合参考:图片最多 9 张(可叠加)+ 视频与音频合用一个「3 槽池」(合计 ≤ 3,可任意搭配)+ 总数最多 12 个(例如 9 图 + 3 视频 = 12,或 7 图 + 2 视频 + 1 音频 = 10);图片生成模型仅支持图片参考,最多 4 张。超出返回400 too_many_references。单复数字段可混用,会合并为一组参考,提交顺序即reference_urls → references。
支持的常见格式:图片 png/jpg/jpeg/webp,视频 mp4/mov/webm,音频 mp3/wav/m4a(以 Content-Type 为准)。
⚠️ 参考素材必踩坑速查
- 参考图要用真实照片,不要用纯色 / 占位 / 合成图(否则触发低质审核
10001305,误报「社区准则」)。- base64 视频 / 音频必须带前缀
data:video/mp4;base64,/data:audio/mpeg;base64,,否则被当图片。- 视频参考宽度 ≥ 300px、时长 < 15 秒;音频时长 < 15 秒。
- 数量:图片 ≤ 9、视频 + 音频合用一个 3 槽池(合计 ≤ 3),总数 ≤ 12。
202只代表已受理,不等于成功——必须轮询到status = completed且拿到video_url才算成;失败读error/error_code/retryable(见「错误码」)。
A. 图片(base64,最常用)
B64=$(base64 -i ./ref.png | tr -d '\n')
curl -s -X POST https://sora2u.com/api/v1/videos \
-H "Authorization: Bearer $SORA_KEY" \
-H "Content-Type: application/json" \
-d "{\"prompt\":\"让画面里的角色缓缓转头微笑\",\"model\":\"seedance-2.0\",\"reference\":\"data:image/png;base64,$B64\"}"B. 视频 / 音频(大文件,用 reference_url,推荐)
# 先把 < 15 秒的参考视频放到任意公开 https 地址(你的 OSS / S3 / CDN 等)
curl -s -X POST https://sora2u.com/api/v1/videos \
-H "Authorization: Bearer $SORA_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "延续参考视频的运镜,让镜头继续推进",
"model": "seedance-2.0",
"reference_url": "https://your-cdn.com/clips/ref-12s.mp4"
}'# 音频驱动同理
curl -s -X POST https://sora2u.com/api/v1/videos \
-H "Authorization: Bearer $SORA_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt":"人物跟随这段语音的节奏说话","model":"seedance-2.0","reference_url":"https://your-cdn.com/clips/voice-10s.mp3"}'C. 视频 / 音频(小文件,内联 base64)
B64=$(base64 -i ./ref.mp4 | tr -d '\n') # 解码后须 < ~3MB,否则请改用 reference_url
curl -s -X POST https://sora2u.com/api/v1/videos \
-H "Authorization: Bearer $SORA_KEY" \
-H "Content-Type: application/json" \
-d "{\"prompt\":\"延续参考视频的运镜\",\"model\":\"seedance-2.0\",\"reference\":\"data:video/mp4;base64,$B64\"}"D. 多张参考图(叠加,最多 9 张)
用 references(base64 数组)一次传多张图;也可用 reference_urls 传多个直链,或两者混用(图片最多 9 张;还可混入视频 / 音频,视频与音频合用一个 3 槽池、合计 ≤ 3)。
A=$(base64 -i ./char.png | tr -d '\n') # 角色参考
B=$(base64 -i ./scene.png | tr -d '\n') # 场景参考
curl -s -X POST https://sora2u.com/api/v1/videos \
-H "Authorization: Bearer $SORA_KEY" \
-H "Content-Type: application/json" \
-d "{\"prompt\":\"让这个角色出现在这个场景里,自然走动\",\"model\":\"seedance-2.0\",\"references\":[\"data:image/png;base64,$A\",\"data:image/png;base64,$B\"]}"# 用多个公开直链(适合较大图片),可与 references 混用
curl -s -X POST https://sora2u.com/api/v1/videos \
-H "Authorization: Bearer $SORA_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "把这两张参考图融合成一个连贯运镜",
"model": "seedance-2.0",
"reference_urls": ["https://your-cdn.com/a.jpg", "https://your-cdn.com/b.jpg"]
}'
reference_url的下载行为与限制:仅接受https,会拒绝解析到内网 / 回环地址的域名,且不跟随 301/302/307 等任何跳转——请提供可直接访问、无重定向的直链。单个素材下载超时 120 秒(需容纳最大 100MB 的视频参考)。服务端以User-Agent: Sora2U-Reference-Fetcher/1.0发起下载,请勿在源站按 UA 拦截。源站返回application/octet-stream或缺失Content-Type时会按文件头自动识别类型(识别不出才拒绝)。带 query 参数的临时签名 URL 可用,但请确保有效期覆盖整个生成过程(建议 ≥ 1 小时)——生成引擎可能在渲染阶段再次拉取素材,届时签名过期会导致任务以reference_download_failed失败。逐素材错误归因:下载 / 校验 / 上传失败时返回
400 invalid_reference_url(base64 超限为413 reference_too_large),错误体附failed_reference字段:{ "index": <0 基下标:本接口按同源(url / base64)数组计;火山兼容接口 /v1/contents/generations/tasks 按 content 数组内位置计>, "source": "url" | "base64", "url_host": "<仅源站 host,不回显完整签名 URL>", "http_status": <源站返回的 HTTP 状态码,仅下载失败时>, "reason": <机器可读失败类别,仅下载类失败时:network / timeout / http / invalid_url / unsafe_url / unsupported_type / too_large> },用于定位具体是哪一个素材出了什么问题。
E. 在提示词中精确定位参考素材(<|media:N|> 定位标记)
默认(提示词里不写任何标记)时,引擎会自动安排各参考素材的作用位置——例如音频参考会被自动拼接到句尾生效,多数场景到此即可。
需要把某个素材精确放到句子的某个位置(如"她说完这段音频后转身,背景保持这张图")时,在 prompt 里嵌入定位标记 <|media:N|>:
- N 是该素材在本次提交的参考列表中的 0 基下标——「参考提交顺序 = N」是唯一硬约束。
- 服务端与引擎对
<|media:N|>原样透传、不改写;未写标记的素材仍由引擎自动安排位置。 - 为免混用字段时心算顺序,建议把所有参考放进同一个数组字段(全部用
reference_urls,或全部用references)——此时 N 就是数组下标。混用时合并顺序为:URL 直链类在前(reference_url→reference_urls)、base64 类在后(reference→references)。 - 只有 1 个参考素材时,它就是
<|media:0|>。
# 图片(下标0) + 音频(下标1):人物先说出音频内容,画面锚定参考图
curl -s -X POST https://sora2u.com/api/v1/videos \
-H "Authorization: Bearer $SORA_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "她说 <|media:1|> 然后转身走开,人物与背景保持 <|media:0|> 的样子",
"model": "seedance-2.0",
"reference_urls": ["https://your-cdn.com/char.png", "https://your-cdn.com/voice-10s.mp3"]
}'查询任务状态
GET /api/v1/videos/{id}
只能查询本人的任务。任务处于 pending/processing 时,本接口会顺带触发一次对账,
拉取最新进度。
{
"success": true,
"task": {
"id": "ckxxx",
"status": "completed",
"progress": 100,
"progress_text": "生成完成",
"prompt": "一只柯基在海边奔跑...",
"model": "seedance-2.0",
"mode": "text-to-video",
"image_url": null,
"video_url": "https://.../result.mp4",
"error": null,
"error_code": null,
"retryable": null,
"estimated_credits": 100,
"reserved_credits": 0,
"credits_charged": 96,
"created_at": "2026-06-17T12:00:00.000Z",
"updated_at": "2026-06-17T12:04:10.000Z",
"completed_at": "2026-06-17T12:04:10.000Z"
}
}失败任务示例(error 为脱敏后的安全文案,配合 error_code / retryable 分支处理,详见「错误码 · 任务失败的错误码」):
{
"success": true,
"task": {
"id": "ckxxx",
"status": "failed",
"video_url": null,
"error": "内容审核未通过。此类失败多见于使用纯色 / 合成 / 占位参考图触发的低质检测,请改用真实、有内容的参考图后重试。",
"error_code": "video_audit_rejected",
"retryable": true
}
}202≠ 成功:创建接口返回202只代表任务已受理,必须轮询到status = completed且video_url非空才算成功。status = completed→ 用video_url取成片。status = failed→ 读error(安全文案)/error_code(稳定错误码)/retryable(是否值得原样重试);预扣积分已自动退还。- 所有字段都在
task下(不是顶层);video_url已脱敏,不含任何内部会话参数。
video_url 的有效期
- 正常情况(绝大多数):任务完成时平台会把成片转存到自有对象存储,此时
video_url形如https://.../api/files/videos/<id>.mp4?token=...,长期有效(token 为对该文件的永久签名,不会过期)。 - 兜底情况(极少数):转存瞬时失败时,
video_url会暂时回退为https://.../api/video/<id>形式的引擎中转地址。该地址依赖上游的临时缓存,通常仅数小时内有效;平台会在完成后 48 小时内自动重试转存,成功后同一任务的video_url会更新为持久化地址(旧中转地址也会自动 302 到新地址)。 - 若中转地址在转存成功前就已过期,访问会返回
410与错误码video_expired(对应上游的Video expired (could not be cached in time)),此时成片已无法取回,只能重新发起生成。 - 建议:任务完成后尽快(24 小时内)下载成片或转存到你自己的存储,不要把
video_url当作永久 CDN 直链长期外链使用。
取消任务 · DELETE /api/v1/videos/{id}
仅 pending / processing 可取消,取消后退还预扣积分:
{ "success": true, "refunded": true, "message": "任务已取消,积分已退还。" }其他接口
列出最近任务 · GET /api/v1/videos?limit=20
返回本人最近的任务列表,limit 范围 1–50,默认 20。
{ "success": true, "count": 2, "data": [ { "id": "...", "status": "completed", ... } ] }模型与计费规则 · GET /api/v1/models
响应遵循 OpenAI 兼容的列表外壳:顶层 object: "list" + data 数组,每个模型 object: "model",
其 id 即公开模型名(用作创建任务时的 model 入参),不含内部引擎 id。另附 default_model
与 credit_rule 便于一次取齐上下文。
{
"object": "list",
"data": [
{
"id": "seedance-1.5",
"object": "model",
"created": 1735689600,
"owned_by": "sora2u",
"name": "Seedance 1.5",
"pricing_unit": "second",
"credit_cost_per_second": 10,
"credit_cost": 10,
"durations": [5, 8, 10, 12],
"default_duration": 5,
"aspect_ratios": ["9:16"],
"default_aspect_ratio": "9:16",
"resolutions": ["720p"],
"default_resolution": "720p",
"max_prompt_length": 2000,
"supports_image": true,
"supports_video": false,
"supports_audio": false,
"reference_max_seconds": 15,
"estimated_time": "2-6分钟"
},
{
"id": "seedance-2.0",
"object": "model",
"created": 1735689600,
"owned_by": "sora2u",
"name": "Seedance 2.0",
"credit_cost_per_second": 20,
"supports_image": true,
"supports_video": true,
"supports_audio": true,
"reference_max_seconds": 15,
"...": "..."
}
],
"default_model": "seedance-2.0",
"credit_rule": {
"currency": "GP",
"gp_per_video_second": 10,
"min_charge_gp": 1,
"note": "预扣 = credit_cost_per_second × duration;任务完成后按实际成本结算,多退少补。"
}
}查询余额 · GET /api/v1/credits
{
"success": true,
"balance": 1280,
"currency": "GP",
"daily_free_video": { "eligible": false, "remaining_today": 0, "note": "..." }
}计费与积分扣减
视频生成消耗 GP 积分,全流程与网页端一致:
- 预扣(创建任务时):按
预扣积分 = 模型每秒积分 × 时长(秒)原子扣减余额。 余额不足直接返回402 insufficient_credits,不会创建任务。 并发创建多条任务时扣减是原子的,不会把余额扣成负数。 - 结算(任务完成时):按上游引擎的实际成本换算成积分,与预扣做差额——
实际更贵则补扣,更便宜则退还差额。最终扣费体现在任务的
credits_charged。 - 退款(任务失败 / 取消时):预扣的积分全额退还。
关于每日免费额度:网页端历史付费用户每天有 1 条免费视频额度。 为避免脚本批量调用悄悄吃掉这个名额,开放 API 默认不使用免费额度,始终按积分扣费。
充值积分请前往网页端的购买页面(开放 API 不涉及支付)。
任务状态机
pending ──▶ processing ──▶ completed
│ │
└─────────────┴────────▶ failed (预扣积分自动退还)
│
└──(DELETE)──────────────▶ 取消 (预扣积分自动退还)| 状态 | 含义 | 下一步 |
|---|---|---|
pending | 已创建、排队中 | 轮询 |
processing | 生成中 | 轮询,可读 progress |
completed | 完成 | 取 video_url |
failed | 失败 | 读 error,积分已退 |
progress是阶段里程碑,不是线性百分比:5 = 已创建、20 = 上传参考素材、35 = 正在提交生成引擎、55 = 引擎已受理、70 = 渲染中、100 = 完成。70 会一直保持到任务落终态,渲染期间数分钟不变属正常。失败任务的progress停在失败发生的阶段:35 ≈ 提交阶段失败、70 ≈ 渲染阶段失败,可据此对失败做分层统计。⚠️
pending/processing状态下返回的video_url是提交时预写入的代理地址,不是成品、不可下载;判断成功的唯一依据是status === "completed"(failed状态下video_url恒为null)。
错误码
错误统一为 { "error": { "code", "message", ... } }。
| HTTP | code | 说明 |
|---|---|---|
| 400 | invalid_json | 请求体不是合法 JSON |
| 400 | invalid_prompt | 缺少 prompt 或不足 10 字符 |
| 400 | invalid_model | 模型名不存在 |
| 400 | invalid_param | 参数非法(如 duration 非数字) |
| 400 | invalid_reference | reference 不是合法 base64 |
| 400 | invalid_reference_url | reference_url 非法 / 非 https / 指向内网 / 不可下载(含超时、超限)/ 类型不符 / 视频音频超 15 秒。错误体附 failed_reference 归因(index / url_host / http_status / reason) |
| 400 | unsupported_media | 参考文件类型不支持(仅 image / video / audio) |
| 400 | unsupported_reference | 所选模型不支持该类参考素材(如给 seedance-1.5 传视频 / 音频参考) |
| 400 | too_many_references | 参考素材超过数量上限(视频生成:图片最多 9 张、视频 + 音频合用一个 3 槽池合计 ≤ 3、总数最多 12 个;图片生成:最多 4 张图) |
| 401 | unauthorized | 密钥缺失 / 无效 / 已吊销 / 已过期 |
| 402 | insufficient_credits | 余额不足,附 current_balance、required_credits |
| 404 | not_found | 任务不存在或非本人 |
| 409 | invalid_status | 任务状态变化,无法取消 |
| 413 | reference_too_large | base64 参考超过大小上限(图片 10MB / 视频 100MB / 音频 50MB,平台侧限制);URL 参考超限返回 400 invalid_reference_url,两者均附 failed_reference 归因 |
| 500 | internal_error | 服务端错误,可重试 |
上表为创建 / 查询请求同步返回的错误({ "error": { "code", "message" } })。
远程参考素材下载失败时,invalid_reference_url / invalid_reference 可能带上定位字段:
{
"error": {
"code": "invalid_reference_url",
"message": "下载 reference_url 失败(HTTP 404)",
"failed_reference": {
"index": 1,
"source": "reference_urls",
"url_host": "cdn.example.com",
"http_status": 404,
"reason": "http"
}
}
}index 为对应字段内的 0 基下标;单个 reference_url 的 source 为 reference_url,多 URL 数组为 reference_urls。火山兼容接口会返回 image_url / video_url / audio_url 作为 source。
任务失败的错误码(task.error_code)
任务被受理(202)后若在生成阶段失败,GET /api/v1/videos/{id} 会返回 status = "failed",并把失败原因归一化为稳定字段:error(安全文案)、error_code(下表)、retryable(是否值得原样重试)。原始上游错误串已脱敏,不会外泄内部细节。
error_code | 含义 | retryable |
|---|---|---|
video_audit_rejected | 内容审核未通过。多为纯色 / 合成 / 占位参考图触发的低质检测(错误码 10001305「社区准则」),并非真正违规——换真实有内容的参考图后重试 | ✅ 是 |
input_image_real_person | 参考图疑似包含真实人物,被模型隐私保护拦截。同图重试仍会被拒——请换非真人 / 卡通 / AI 合成参考图,或改用 seedance-2.0-character(人脸素描匿名) | ❌ 否 |
output_audio_copyright | 生成结果的背景音频疑似触发版权 / 敏感检测(自动配乐常见)。可重试,或在提示词中避免特定音乐 | ✅ 是 |
reference_download_failed | 生成引擎拉取参考图 / 视频 / 音频地址失败;请确认链接公网可访问、未过期后重试 | ✅ 是 |
content_policy | 提示词或参考素材命中内容策略,需调整后重试 | ❌ 否 |
too_many_references | 参考素材数量超过上限,需减少 | ❌ 否 |
invalid_reference | 参考素材不合规(视频须宽 ≥ 300px、时长 < 15 秒,须为真实有效文件),需更换 | ❌ 否 |
reference_download_failed | 生成引擎渲染阶段无法拉取你提供的参考素材地址。确认链接可公网访问、未过期(签名 URL 建议有效期 ≥ 1 小时)后重试 | ✅ 是 |
reference_upload_failed | 参考素材上传到生成引擎失败(平台 ↔ 引擎链路问题),重新提交任务即可 | ✅ 是 |
engine_retry_exhausted | 生成引擎内部多次尝试(通常 3 次)仍失败,多为上游临时波动。稍后重试;持续失败请联系平台 | ✅ 是 |
platform_timeout | 平台侧任务超时(排队 / 处理长时间无进展),已自动取消并退还预扣积分。可直接重试;频繁出现请联系平台 | ✅ 是 |
settlement_failed | 生成已完成,但实际费用高于预估且余额不足以补扣积分。请充值后重新发起 | ❌ 否 |
task_cancelled | 任务已被调用方取消,预扣积分已退还;如需继续请重新创建 | — |
engine_error | 其余未归类的引擎处理失败(兜底码) | ✅ 是 |
重试建议:
retryable = true表示同一请求原样重试有望成功(审核抽检、引擎抖动、自动配乐版权等偶发原因),建议指数退避重试 2–3 次;retryable = false表示需先修正请求(换真实参考图、减少数量、调整提示词)再试。任务失败的预扣积分已自动全额退还。
最佳实践
- 轮询节奏:建议每 5–10 秒查询一次状态,轮询直至终态(completed / failed / cancelled),时间预算至少 30 分钟——复杂任务可能超过 20 分钟才完成,请勿按固定 10 分钟窗口提前判失败(提前放弃后任务若最终完成仍会正常扣费)。如需放弃,可先尝试
DELETE /api/v1/videos/{id}取消(当前仅支持排队中 / 尚未提交渲染的任务,成功即退还预扣积分)。GET /api/v1/videos/{id}会顺带触发对账。 - 及时取回成片:
status = completed后尽快下载video_url(建议 24 小时内)并转存到你自己的存储;有效期细节见「查询任务状态 ·video_url的有效期」。 - 先查余额:批量生成前先
GET /api/v1/credits,避免中途402。 - 密钥安全:只放在服务端环境变量里,绝不写进前端代码 / 仓库 / 日志;按用途分多把密钥,泄露即吊销。
- 设过期时间:临时脚本用
expires_in_days创建短期密钥,降低泄露风险。 - 错误重试:仅对
5xx做指数退避重试;4xx属调用方问题,先修参数再试。 - 限流:积分机制本身限制了滥用(无积分即无法生成)。如需对接高频系统,请联系运营协商配额。
完整示例
Node.js(18+,原生 fetch)
const BASE = "https://sora2u.com";
const KEY = process.env.SORA_KEY;
const headers = {
Authorization: `Bearer ${KEY}`,
"Content-Type": "application/json",
};
async function generate(prompt) {
const res = await fetch(`${BASE}/api/v1/videos`, {
method: "POST",
headers,
body: JSON.stringify({ prompt, model: "seedance-2.0", duration: 5 }),
});
if (!res.ok) throw new Error(`创建失败: ${JSON.stringify(await res.json())}`);
const { task } = await res.json();
// 轮询直到完成
for (let i = 0; i < 120; i++) {
await new Promise((r) => setTimeout(r, 5000));
const s = await (
await fetch(`${BASE}/api/v1/videos/${task.id}`, { headers })
).json();
if (s.task.status === "completed") return s.task.video_url;
if (s.task.status === "failed") throw new Error(s.task.error);
}
throw new Error("超时");
}
generate("一只柯基在海边奔跑,电影质感").then(console.log);Python(requests)
import os, time, requests
BASE = "https://sora2u.com"
H = {"Authorization": f"Bearer {os.environ['SORA_KEY']}"}
def generate(prompt: str) -> str:
r = requests.post(f"{BASE}/api/v1/videos", headers=H,
json={"prompt": prompt, "model": "seedance-2.0", "duration": 5})
r.raise_for_status()
task_id = r.json()["task"]["id"]
for _ in range(120):
time.sleep(5)
t = requests.get(f"{BASE}/api/v1/videos/{task_id}", headers=H).json()["task"]
if t["status"] == "completed":
return t["video_url"]
if t["status"] == "failed":
raise RuntimeError(t["error"])
raise TimeoutError()
print(generate("一只柯基在海边奔跑,电影质感"))给 AI 用:MCP 与技能
为方便各类 AI(Claude、Cursor、ChatGPT 等)直接调用本 API,仓库内置了两份现成集成:
- MCP Server:
mcp/sora2u-video-mcp/。一个 stdio MCP 服务器,把本 API 暴露成工具:create_video(支持本地图片/视频/音频做参考)、get_video、list_videos、cancel_video、list_models、get_credits。适用于 Claude Desktop、Cursor、Cline 等 MCP 客户端,配置见该目录 README。 - 技能(Skill):
skills/sora2u-video/SKILL.md。给 Claude「技能」/自定义 Agent 用的说明书,内含完整调用流程与参考素材上传指引。 - OpenAPI:本目录的
openapi.yaml即机器可读规范,可直接导入 ChatGPT「Actions」/ 自定义 GPT、Apifox、Postman 作为函数/工具定义。
三者都覆盖图片 / 视频 / 音频参考素材的上传细节(base64 与 reference_url 两种方式)。
给维护者:上线步骤
本次新增了 ApiKey 数据表与一组 /api/v1/*、/api/api-keys 接口。部署前需:
# 1. 安装依赖并生成 Prisma Client(已包含新的 ApiKey 模型)
npm install
npx prisma generate
# 2. 应用数据库迁移(新增 ApiKey 表)
npx prisma migrate deploy # 生产
# 或本地:npx prisma migrate dev
# 3. 正常构建启动
npm run build && npm start涉及代码:
- 数据模型:
prisma/schema.prisma(ApiKey)+ 迁移prisma/migrations/20260617120000_add_api_keys - 密钥工具:
lib/auth/api-key.ts(生成 / 哈希 / 鉴权) - 生成服务:
lib/ai/video-generation-service.ts(站内与开放 API 共用的扣费链路) - 开放接口:
app/api/v1/{videos,videos/[id],models,credits}/route.ts - 密钥管理:
app/api/api-keys/route.ts、app/api/api-keys/[id]/route.ts - 响应工具:
lib/api/public-api.ts
鉴权所需环境变量与现有视频生成一致(
DATABASE_URL、视频引擎相关配置等),开放 API 未引入新的必填环境变量。
