API v1 参考
使用 VideoLingo API 创建并查询 YouTube 视频翻译任务。
概览
VideoLingo API v1 允许专业计划用户从服务端代码创建 YouTube 视频翻译任务并查询进度。API 任务与网页任务使用同一套处理流程、账户余额、计费规则和任务列表;未填写名称时,任务名以 API · 开头。
| 项目 | 说明 |
|---|---|
| API 基址 | https://videolingo.io/api/v1 |
| 鉴权方式 | Authorization: Bearer <API_KEY> |
| 请求与响应格式 | JSON |
| 当前接口 | POST /tasks、GET /tasks/{id} |
| 计划要求 | 有效的专业计划;付费期结束后包含 1 天宽限期 |
| 单个视频最长时长 | 4 小时(14400 秒) |
| 同时处理上限 | 每个账户 3 个排队中或处理中的任务,网页任务也计入 |
API v1 目前只接受 YouTube 链接,暂不提供任务列表、取消任务或 webhook 回调接口。所有响应都带 Cache-Control: no-store,且不返回 CORS 头。请从服务端调用,不要从浏览器前端直接调用;这也能避免把 API Key 暴露给用户。
获取 API Key
登录后打开任务台 → 账户 → API,也可以从右上角头像菜单进入 API Key。有效的专业计划账户会得到一把以 vl_live_ 开头的 Key,可以在该页面显示、复制或重新生成。
API Key 等同密码。不要放进浏览器端代码、不要提交到代码仓库、不要拼进 URL、不要写入日志,也不要分享包含 Key 的截图。请保存在服务端密钥管理服务或环境变量中。
下文示例使用环境变量:
export VL_API_KEY="vl_live_替换成你的Key"鉴权
每个请求都要带:
Authorization: Bearer vl_live_xxxxxxxxxxxxxxxx接口不读取 Cookie,也不接受 URL 查询参数里的 Key。缺失、格式错误、已被重新生成或不存在的 Key 返回 401 invalid_api_key;Key 正确但专业计划无效时返回 403 subscription_inactive。
创建任务
POST /tasks请求头
| 请求头 | 是否必填 | 值与行为 |
|---|---|---|
Authorization | 是 | Bearer <API_KEY>。必须带 Bearer,不能只发送 Key。 |
Content-Type | 是 | application/json。请求体必须是合法 JSON。 |
Idempotency-Key | 强烈建议 | 8–128 个字符;对这一次预期创建的任务保持唯一且稳定,例如订单号或 UUID。 |
幂等重试
每个新视频任务使用一个新的 Idempotency-Key。如果网络超时或 5xx 导致结果不确定,请使用同一个 Key 和完全相同的 JSON 请求体重试。重复请求会指向同一个任务;任务开始后不会再次创建或重复扣费。
- Key 对应的任务已经删除时,返回
422 invalid_request,此时需要换新 Key。 - Key 少于 8 个或超过 128 个字符时,返回
422 invalid_request。 - 不要用同一个 Key 提交另一个视频或另一组参数。API v1 并不保证在所有“尚未派发”的状态下都检测请求体冲突。
- 不带 Key 时,每次重试都可能创建一个独立任务并单独计费。
请求体
请求体是一个扁平 JSON 对象。只有 youtube_url 必填。未列出的字段都会被拒绝,包括 user_plan、watermark、prod、模型名称等由服务端决定的字段。
| 字段 | 是否必填 | JSON 类型 | 默认值 | 详细说明 |
|---|---|---|---|---|
youtube_url | 是 | string | — | YouTube 视频的 HTTPS 链接,最长 2048 字符。允许的主机:youtube.com、www.youtube.com、m.youtube.com、youtu.be。支持 /watch?v=…、youtu.be/…、/shorts/…、/embed/…、/live/…、/v/…。带用户名密码、自定义端口、非 HTTPS、只有播放列表没有单个视频 ID、或非 YouTube 主机的链接都会被拒绝。 |
name | 否 | string | API · <视频 ID> | 任务台中显示的任务名称。服务端会去掉首尾空格,剩余内容必须为 1–200 个字符。 |
target_language | 否 | string 枚举 | 简体中文 | 译文字幕和配音的输出语言。必须传下方目标语言表中的精确值;它不是跟随当前页面语言变化的显示文案。 |
source_language | 否 | string 枚举 | en | 源视频里实际说的语言,用于语音识别。必须传下方源语言表中的精确代码。这里不会自动检测;源视频不是英语时请主动设置。 |
dubbing | 否 | boolean | false | false 只生成翻译字幕;true 还会生成配音音轨。必须传 JSON 布尔值,不能传字符串 "true" 或 "false"。 |
tts_method | 否 | string 枚举 | 当前 target_language 的第一款预设音色 | 配音音色。fish_voice_… 必须属于所选目标语言;fish_clone 使用系统从视频中提取的参考声音。只有 dubbing 为 true 时才影响成品音频。 |
target_height | 否 | string 枚举 | "1080" | 下载 YouTube 视频时的最高分辨率:"480"、"720"、"1080"。必须是 JSON 字符串,不能传数字 1080。如果源视频没有对应清晰度,实际文件可能更低。 |
demucs | 否 | boolean | true | 是否在转写和配音前分离人声与背景音。对音乐或背景声较大的视频通常更有帮助,但也会增加处理工作量。 |
glossary | 否 | string | 不传 | 用于人名、品牌名和专业术语的自由文本提示,最多 250 个字符。只放与当前视频有关的简短术语说明。 |
target_language 可选值
必须使用下列精确值之一:
| 语言 | target_language 精确值 |
|---|---|
| 英语 | English |
| 法语 | Français |
| 德语 | Deutsch |
| 意大利语 | Italiano |
| 西班牙语 | Español |
| 俄语 | Русский |
| 韩语 | 한국어 |
| 日语 | 日本語 |
| 简体中文 | 简体中文 |
| 繁体中文 | 繁體中文 |
| 粤语 | 粤语 |
source_language 可选值
必须使用下列精确值之一:
| 语言 | source_language 精确代码 |
|---|---|
| 英语 | en |
| 简体中文 | zh |
| 繁体中文 | zh-TW |
| 法语 | fr |
| 德语 | de |
| 意大利语 | it |
| 西班牙语 | es |
| 俄语 | ru |
| 日语 | ja |
配音音色取值
当 dubbing 为 true 时,可以省略 tts_method(自动使用该目标语言的第一款预设音色)、选择下表中与目标语言匹配的 fish_voice_<模型 ID>,或使用 fish_clone 克隆视频中提取到的参考声音。
预设音色和 target_language 必须属于同一语言。比如法语音色配 target_language: "日本語"、不存在的模型 ID、以及已经停用的 openai_tts 都会返回 422 invalid_request。
| target_language 精确值 | 音色 | 性别 | tts_method 精确值 |
|---|---|---|---|
English | Sarah | 女声 | fish_voice_933563129e564b19a115bedd57b7406a |
English | Selene | 女声 | fish_voice_b347db033a6549378b48d00acb0d06cd |
English | Adrian | 男声 | fish_voice_bf322df2096a46f18c579d0baa36f41d |
English | Ethan | 男声 | fish_voice_536d3a5e000945adb7038665781a4aca |
Français | Manon | 女声 | fish_voice_10a3a20742114a4ea6dd441e7591850f |
Français | Thérèse | 女声 | fish_voice_94702e20bb3e44d39c64ba74bb480dfc |
Français | Lucas | 男声 | fish_voice_f69bca092b674168a8d02d61ca20943c |
Français | Julien | 男声 | fish_voice_9af982ac4fbc4877af9d33b06e694fb9 |
Deutsch | Lena | 女声 | fish_voice_3937b44b604249f18b94913daef94043 |
Deutsch | Melanie | 女声 | fish_voice_d979e92f9c8a489fa581f55f6b9e48da |
Deutsch | Leon | 男声 | fish_voice_7b7c88bf825a4ab3bca27d55c26ddf16 |
Deutsch | Jonas | 男声 | fish_voice_44f1ec96a51e429f8b87776cbc55a997 |
Italiano | Chiara | 女声 | fish_voice_ef02f46e6a7540759f9d767d7e22c2e5 |
Italiano | Giulia | 女声 | fish_voice_0e36e185d0654d5bac3d24331562a0cc |
Italiano | Lorenzo | 男声 | fish_voice_59ac2b0ad66641b5ab4666d3ed89e5d4 |
Italiano | Marco | 男声 | fish_voice_f6cbad8b89254774a495c088aeb7822e |
Español | Carmen | 女声 | fish_voice_3f065d660212478a9c697fa965b4d098 |
Español | Elena | 女声 | fish_voice_566359628f6a4d5fabf902f6f64ecec1 |
Español | Pablo | 男声 | fish_voice_1bd666ea8ada44c789e0fec21cf78f33 |
Español | Andrés | 男声 | fish_voice_d85aae3c4f4d4c70860997c27554f533 |
Русский | Полина Polina | 女声 | fish_voice_bf75c2e80b0e441485eeec20330a75fa |
Русский | Maria | 女声 | fish_voice_7adab54c80d34cba8c11463ab1502c9a |
Русский | Артём Artyom | 男声 | fish_voice_b871e425b6c0442baa5cf73f6c336bbe |
Русский | Dmitri | 男声 | fish_voice_2911dfe1c9d54419b24ed8c4a1489355 |
한국어 | 하윤 Hayoon | 女声 | fish_voice_96ed4e3f47334430b990df471d0a0268 |
한국어 | Yuna | 女声 | fish_voice_3d31499f0e13438bbce8dcce7b7c4298 |
한국어 | 지훈 Jihoon | 男声 | fish_voice_1faa840246094164bf4b0b0b94ae1675 |
한국어 | Minjun | 男声 | fish_voice_82703151e4b043dbbd98f082d4841b71 |
日本語 | きょうこ(カスタマーサポート) | 女声 | fish_voice_b2d9d8db057042688a5e318b8f405bc2 |
日本語 | しおり(ナレーション) | 女声 | fish_voice_5da7f24e9e274f91b2b677669c818ce9 |
日本語 | さとる(ナレーション) | 男声 | fish_voice_297a6fd278df47c3b9da9bfdf55ac89a |
日本語 | じん(ラジオパーソナリティ) | 男声 | fish_voice_d738c70d206149e69f8e3b6fc8af9d96 |
简体中文 | 语彤 Yutong | 女声 | fish_voice_74c6aba5cbf94a15bbdc547ffce5cb38 |
简体中文 | 冰冰 Bingbing | 女声 | fish_voice_e98fa6cdad6946bf8d9bb8f9cb8c2532 |
简体中文 | 梓轩 Zixuan | 男声 | fish_voice_5d29a99739c14d4ca3e4fe42193105b2 |
简体中文 | 浩然 Haoran | 男声 | fish_voice_d675c275d1d44e57b4ef3840c5a23209 |
繁體中文 | 詩涵 Shihan(台灣) | 女声 | fish_voice_91ec588cf8ef443a9c0d5b21d0c1fa36 |
繁體中文 | Ya-Ting | 女声 | fish_voice_49066cb45f0a427484ade915a4474534 |
繁體中文 | 承翰 Chenghan(台灣) | 男声 | fish_voice_1e85fd1e0d3e4cc2b79fbca800e7e3fe |
繁體中文 | Chia-Hao | 男声 | fish_voice_e7af89da8abc4fbdacd9a10ab9881933 |
粤语 | 詠詩 Wing Sze | 女声 | fish_voice_50c586f7c811466cac751c28c3e4a671 |
粤语 | VideoLingo 粵語女聲 | 女声 | fish_voice_da2d934b77df4561ac51f8ed3edfaa2b |
粤语 | 俊傑 Chun Kit | 男声 | fish_voice_fc917a4b147145258519982fe306b2cf |
粤语 | VideoLingo 粵語男聲 | 男声 | fish_voice_a197cea20d84442d8462253a7ddf08a3 |
最小字幕任务示例
curl --request POST 'https://videolingo.io/api/v1/tasks' \
--header "Authorization: Bearer $VL_API_KEY" \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: order-8fa1a2b3' \
--data '{
"youtube_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"source_language": "en",
"target_language": "简体中文",
"dubbing": false
}'指定音色的配音任务示例
curl --request POST 'https://videolingo.io/api/v1/tasks' \
--header "Authorization: Bearer $VL_API_KEY" \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: order-4c5d6e7f' \
--data '{
"youtube_url": "https://youtu.be/dQw4w9WgXcQ",
"name": "日语配音发布视频",
"source_language": "en",
"target_language": "日本語",
"dubbing": true,
"tts_method": "fish_voice_b2d9d8db057042688a5e318b8f405bc2",
"target_height": "1080",
"demucs": true,
"glossary": "VideoLingo:产品名保留拉丁字母,不翻译"
}'接受任务前会发生什么
- 接口验证 Key 和完整 JSON 请求体;未知字段或不匹配的值会在处理前失败。
- 接口读取公开视频时长,这一步最多约 20 秒。私有或不可访问的视频返回
422 duration_unavailable;超过 4 小时返回422 invalid_request。 - 接口检查共享并发名额和可用余额,预留任务费用,然后派发处理。
成功时返回 202 Accepted 和任务对象;初始 status 通常为 queued 或 processing。
查询任务
GET /tasks/{id}使用 POST /tasks 返回的 UUID:
curl 'https://videolingo.io/api/v1/tasks/TASK_ID' \
--header "Authorization: Bearer $VL_API_KEY"同一接口也能查询当前账户在网页上创建的任务。非法 UUID、不存在、已删除或属于其他用户的任务都会返回同一种 404 not_found,避免泄露任务归属。
建议每 15–30 秒查询一次,直到 status 变成 completed、failed 或 cancelled。查询处理中的任务时,接口也会刷新最新处理状态。
任务对象
两个成功接口返回相同结构:
{
"id": "3f2c7a1e-5b8d-4c6f-9a0b-1d2e3f4a5b6c",
"name": "API · dQw4w9WgXcQ",
"status": "processing",
"youtube_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"video_duration_seconds": 212,
"cost_usd": 0.294444,
"step": "Translating text",
"step_index": 6,
"error": null,
"result": null,
"created_at": "2026-09-24T08:00:00.000Z",
"updated_at": "2026-09-24T08:03:12.000Z"
}| 字段 | 类型 | 何时出现、应该怎么用 |
|---|---|---|
id | string (UUID) | 稳定的任务 ID。请保存下来,用于后续查询。 |
name | string | 任务台显示的任务名称。 |
status | string 枚举 | 生命周期状态。业务逻辑应判断此字段,取值见下表。 |
youtube_url | string | null | 源视频链接。网页上传的本地文件任务为 null。 |
video_duration_seconds | number | null | 已知时返回源视频秒数。 |
cost_usd | number | null | 预留的美元费用:视频时长 × 每小时 5 美元。仅字幕与配音任务使用相同费率;时长未知时为 null。 |
step | string | null | 处理中的可读流程名称。可以展示给用户,但不要依赖这段文案编写业务逻辑。 |
step_index | number | null | 适合进度 UI 使用的数字阶段,完整映射见下表。 |
error | string | null | 当 status 为 failed 时的可读失败原因;它与 HTTP 错误响应里的 error 对象不是一回事。 |
result | object | null | completed 时包含下载链接,其他状态为 null。 |
created_at | string | ISO 8601 格式的 UTC 创建时间。 |
updated_at | string | 任务最后一次持久化更新的 ISO 8601 UTC 时间。 |
status 取值
| 值 | 是否终态 | 含义与客户端动作 |
|---|---|---|
draft | 否 | 尚未派发。可能是网页草稿,或创建接口返回过 503 dispatch_failed;后一种情况请用同一幂等键重试创建请求。 |
queued | 否 | 已接受,正在等待处理资源。继续轮询。 |
processing | 否 | 正在处理。可展示 step / step_index,继续轮询。 |
completed | 是 | 成功完成,从 result 读取下载链接。 |
failed | 是 | 处理失败,从 error 读取原因。除非原创建请求本身返回 503,再次提交应视为新任务并使用新的幂等键。 |
cancelled | 是 | 已停止,不会产生结果。 |
step 与 step_index
step_index | 仅字幕任务 | 配音任务 |
|---|---|---|
| 0 | Preparing input video | Preparing input video |
| 1 | Extracting audio using Demucs | Extracting audio using Demucs |
| 2 | Transcribing using Whisper | Transcribing using Whisper |
| 3 | Splitting sentences using spaCy | Splitting sentences using spaCy |
| 4 | Splitting sentences by meaning | Splitting sentences by meaning |
| 5 | Summarizing content | Summarizing content |
| 6 | Translating text | Translating text |
| 7 | Splitting for subtitles | Splitting for subtitles |
| 8 | Aligning timestamps | Aligning timestamps |
| 9 | Merging subtitles | 保留 / 跳过 |
| 10 | Uploading results | Generating audio tasks |
| 11 | — | Extracting reference audio |
| 12 | — | Generating audio using TTS |
| 13 | — | Merging full audio |
| 14 | — | Merging dub audio |
| 15 | — | Uploading results |
较早开始的配音任务可能短暂上报旧版第 9 步;用于展示时,如果同时有 step,优先显示它。
result
任务完成后,result 包含 4 个可直接下载的链接:
| 字段 | 文件 |
|---|---|
video | 最终 MP4 视频。 |
subtitles_zip | 包含全部字幕文件的 ZIP。 |
translated_srt | 译文 SRT 字幕。 |
source_srt | 原文 SRT 字幕。 |
{
"result": {
"video": "https://cloud.videolingo.io/tasks/3f2c7a1e-…/output.mp4",
"subtitles_zip": "https://cloud.videolingo.io/tasks/3f2c7a1e-…/srt.zip",
"translated_srt": "https://cloud.videolingo.io/tasks/3f2c7a1e-…/trans.srt",
"source_srt": "https://cloud.videolingo.io/tasks/3f2c7a1e-…/src.srt"
}
}下载 URL 本身不能代替完成信号;必须先等到 status: "completed" 再使用。
错误与重试策略
HTTP 错误统一使用下列结构。程序应判断稳定的 error.code;error.message 是给人看的说明,未来可能调整措辞。
{
"error": {
"code": "concurrency_limit",
"message": "At most 3 tasks can be queued or processing at once",
"active": 3,
"limit": 3
}
}| HTTP | error.code | 含义 | 客户端应该怎么做 |
|---|---|---|---|
| 401 | invalid_api_key | Key 缺失、格式错误、已重新生成或不存在。 | 不要原样重试;修正请求头或部署当前 Key。 |
| 403 | subscription_inactive | 不是有效专业计划,或付费期结束已超过 1 天。 | 续费/升级后再试;原 Key 会自动恢复。 |
| 404 | not_found | ID 非法、任务不存在、已删除或属于其他账户。 | 检查 ID 和账户,不要无休止重试。 |
| 422 | invalid_request | JSON/字段/值不合法、未知字段、YouTube 链接不合法、视频超过 4 小时、幂等键非法或对应已删除任务。 | 修正请求。只有 Key 对应已删除任务或任务意图改变时才换新 Key。 |
| 422 | duration_unavailable | 视频为私有、不存在、受限或暂时不可访问,导致无法读取时长。 | 先确认视频可公开访问;URL 不变时可以稍后用同一 Key 重试。 |
| 402 | insufficient_balance | 可用余额低于任务费用;附加字段为美元 balance、cost。 | 充值后用同一 Key 重试。本次拒绝不会创建任务或扣费。 |
| 429 | concurrency_limit | 已有 3 个任务排队或处理中;附加字段为 active、limit。 | 等待名额释放,再用同一 Key 重试。本次拒绝不会创建任务或扣费。 |
| 503 | dispatch_failed | 处理服务暂时拒绝接收;附加字段为 task_id。 | 指数退避,并使用同一 Key 和请求体重试;任务保留为 draft。 |
| 500 | internal_error | 未预期的服务端错误。 | 因结果可能不确定,请指数退避并使用同一 Key 和请求体重试。 |
建议瞬时错误退避间隔:2 秒、5 秒、10 秒、30 秒,之后停止并把错误显示给用户。未来如果响应带 Retry-After,应优先遵守。401、403、404 和一般 422 错误在根因未改变前不要自动重试。
限制与计费
- **并发:**每个账户最多 3 个
queued或processing任务,网页上创建的任务也占用同一名额。 - **时长:**单个视频最长 4 小时(
14400秒)。 - **计费:**字幕翻译与配音均按视频时长以每小时 5 美元计费。任务开始时从账户余额预留最终的
cost_usd。 - **轮询:**目前没有每分钟请求配额,但应保持 15–30 秒一次的合理频率。长视频可以进一步延长间隔,进入终态后立即停止。
- 异步语义:
202 Accepted只表示任务已接受处理,不表示已经完成;计费和成品都属于这个异步任务。
完整轮询示例
下面的 Bash 脚本需要 curl 和 jq。它会保留幂等键、在 HTTP 错误时失败,并且只在任务进入终态后停止。
#!/usr/bin/env bash
set -euo pipefail
: "${VL_API_KEY:?请先设置 VL_API_KEY}"
API='https://videolingo.io/api/v1'
IDEMPOTENCY_KEY="video-$(date +%s)-$$"
RESPONSE=$(curl --silent --show-error --fail-with-body \
--request POST "$API/tasks" \
--header "Authorization: Bearer $VL_API_KEY" \
--header 'Content-Type: application/json' \
--header "Idempotency-Key: $IDEMPOTENCY_KEY" \
--data '{
"youtube_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"source_language": "en",
"target_language": "简体中文",
"dubbing": false
}')
TASK_ID=$(jq --raw-output '.id // empty' <<<"$RESPONSE")
test -n "$TASK_ID" || { printf '响应里没有任务 ID:%s\n' "$RESPONSE" >&2; exit 1; }
printf '任务 ID:%s\n' "$TASK_ID"
while true; do
RESPONSE=$(curl --silent --show-error --fail-with-body \
"$API/tasks/$TASK_ID" \
--header "Authorization: Bearer $VL_API_KEY")
STATUS=$(jq --raw-output '.status' <<<"$RESPONSE")
STEP=$(jq --raw-output '.step // ""' <<<"$RESPONSE")
printf '%s %s\n' "$STATUS" "$STEP"
case "$STATUS" in
completed) jq '.result' <<<"$RESPONSE"; break ;;
failed|cancelled) jq '{status, error}' <<<"$RESPONSE" >&2; exit 1 ;;
queued|processing|draft) sleep 30 ;;
*) printf '未知状态:%s\n' "$STATUS" >&2; exit 1 ;;
esac
done轮换 API Key
- 在任务台 → 账户 → API点击重新生成 Key。
- 旧 Key 会立即失效并返回
401;正在处理的任务不会中断。 - 删除自己保存的旧 Key 前,先更新所有服务器、CI 密钥和集成环境。
- 怀疑泄露时立即重新生成。每个账户同一时间只有一把有效 Key。
- 专业计划过期时 Key 返回
403;续费后原 Key 会恢复,因此日常续费不需要轮换。