API 参考
接口、字段、响应、点数、限制和错误。
更新于
工作原理#
每份文稿都来自一个任务。你提交一个来源,任务在后台运行,成功后它会指向一份文稿,你可以随时读取或导出,次数不限。基础 URL:https://www.transcriptdock.com。所有请求和响应都是 JSON。
# 1. Submitcurl -X POST https://www.transcriptdock.com/v1/jobs \-H "Authorization: Bearer td_live_YOUR_KEY" \-H "Idempotency-Key: video-7680721699171601694" \-H "Content-Type: application/json" \-d '{ "source": { "url": "https://www.tiktok.com/@tiktok/video/7680721699171601694" }, "mode": "captions_only" }'# 2. Wait (returns as soon as the job finishes, up to 25 s per call)curl "https://www.transcriptdock.com/v1/jobs/11111111-1111-4111-8111-111111111111?wait=25" \-H "Authorization: Bearer td_live_YOUR_KEY"# 3. Exportcurl "https://www.transcriptdock.com/v1/transcripts/22222222-2222-4222-8222-222222222222/export?format=srt" \-H "Authorization: Bearer td_live_YOUR_KEY"
身份验证#
在 API 密钥 页面创建密钥。密钥只显示一次,形如 td_live_...,并放在每个请求的 Authorization 请求头中。密钥带有创建时选择的权限范围:jobs:read、 jobs:write、transcripts:read、 uploads:write。建议每个集成使用一个单独的密钥,这样就可以单独撤销它。
Authorization: Bearer td_live_YOUR_KEY
Idempotency-Key#
以下接口必须带上它:POST /v1/jobs、/v1/batches、 /v1/jobs/{id}/retry、/v1/video-discoveries 和 /v1/language-discoveries: 可以是任意 8 到 128 个可打印的 ASCII 字符。在 48 小时内用同一个 Idempotency-Key 和同样的请求体再次发送,会返回原来的对象,因此重试的请求不会重复创建,也不会重复扣费。同一个 Idempotency-Key 搭配不同的请求体,会返回 409 IDEMPOTENCY_CONFLICT。好的 Idempotency-Key 就是你自己为要转录的内容设定的 ID。
X-Request-Id#
每个响应都会带有一个。联系支持时请提供它。
接口#
| 方法 | 路径 | 作用 | 点数 |
|---|---|---|---|
| POST | /v1/jobs | 转录一个视频、文件或链接 | 每份字幕文稿 1 个,AI 转录每分钟 2 个 |
| GET | /v1/jobs/{id} | 任务状态,最多等待 25 秒 | 免费 |
| GET | /v1/jobs | 任务历史 | 免费 |
| POST | /v1/jobs/{id}/cancel | 取消排队中的任务 | 免费 |
| POST | /v1/jobs/{id}/retry | 重试失败的任务 | 按新任务计费 |
| POST | /v1/batches | 转录多个视频(受套餐批量大小限制) | 按每一项计,同上 |
| GET | /v1/batches/{id} | 批量任务状态 | 免费 |
| GET | /v1/transcripts/{id} | 文稿 JSON | 免费 |
| GET | /v1/transcripts/{id}/export | txt、srt、vtt 或 json 文件 | 免费 |
| DELETE | /v1/transcripts/{id} | 删除文稿 | 免费 |
| POST | /v1/uploads | 获取用于上传文件的 URL | 免费 |
| POST | /v1/uploads/{id}/complete | 标记上传已完成 | 免费 |
| POST | /v1/video-discoveries | 搜索 YouTube,列出频道、播放列表或 TikTok 个人主页的视频 | 每页 1 个 |
| GET | /v1/video-discoveries/{id} | 视频发现结果 | 免费 |
| POST | /v1/language-discoveries | 列出 YouTube 视频的字幕语言 | 1 个 |
| GET | /v1/language-discoveries/{id} | 语言列表 | 免费 |
| POST | /v1/webhook-endpoints | 注册 Webhook URL(需要已登录的会话) | 免费 |
| GET | /v1/webhook-endpoints | 列出 Webhook URL | 免费 |
| DELETE | /v1/webhook-endpoints/{id} | 停用 Webhook URL(需要已登录的会话) | 免费 |
| GET | /v1/usage | 点数和套餐 | 免费 |
| GET | /v1/capabilities | 你的密钥能做什么 | 免费 |
创建任务#
/v1/jobs返回 202 和任务。如果你的工作区已经有同一个视频、同样选项的文稿,则免费返回 200 和一个已成功的任务 (billing.kind: "cached")。
{"id": "11111111-1111-4111-8111-111111111111","status": "queued","stage": "resolve","result_id": null,"error": null,"billing": { "credits_reserved": 2, "credits_charged": 0, "kind": "captions" },"source": { "platform": "tiktok", "media_id": "7680721699171601694", "canonical_url": "https://www.tiktok.com/@tiktok/video/7680721699171601694", "title": null, "upload_id": null },"options": { "mode": "auto", "language": null, "caption_preference": "prefer_creator" },"created_at": "2026-09-16T10:00:00.000Z"}
任务对象#
获取任务#
/v1/jobs/{id}?wait=25返回任务。带上 wait(0 到 25 秒)时,请求会保持打开,并在任务进入最终状态时立即返回,因此一次调用就可以取代轮询循环。读取请求有单独的限制:每分钟 120 次。
列出任务#
/v1/jobs?limit=20&cursor=按从新到旧排序,limit 取 1 到 100。把响应中的 next_cursor 作为 cursor 传回,即可获取下一页。 同时加上 platform 和 source_id(任务的 source.media_id),就只会看到某一个视频的任务,不论使用哪种模式。客户端就是这样在再次提交之前,检查工作区是否已经拥有该文稿的。
取消任务#
/v1/jobs/{id}/cancel取消尚未开始 AI 转录的任务,预留的点数会被释放。如果已经开始,则返回 409 CANCELLATION_NOT_ALLOWED,任务会继续完成。
重试任务#
/v1/jobs/{id}/retry基于错误为 retryable 的失败任务,创建一个新任务。需要一个新的 Idempotency-Key;请求体可以选填 { "max_credits": 40 }。 按新任务计价。属于最终失败的情况(私密视频、没有字幕)会返回 409 JOB_NOT_RETRYABLE:请修正输入后重新提交。
文稿#
/v1/transcripts/{id}{"id": "22222222-2222-4222-8222-222222222222","source": { "platform": "youtube", "media_id": "dQw4w9WgXcQ", "canonical_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "title": "Never Gonna Give You Up" },"source_origin": "creator_captions","language": "en","text": "Never gonna give you up. Never gonna let you down.","segments": [{ "start": 0, "end": 2.4, "text": "Never gonna give you up" },{ "start": 2.4, "end": 4.9, "text": "Never gonna let you down" }],"words": null,"timing_granularity": "segment","duration_seconds": 212.0,"recognition": null,"extraction_version": "worker-0.1.0","created_at": "2026-09-16T10:00:14.000Z"}
文稿会按你所在套餐的保存期限保留(试用套餐 7 天,Starter 30 天,Pro 和 Scale 90 天)。DELETE /v1/transcripts/{id} 可以提前删除某份文稿。
导出文稿#
/v1/transcripts/{id}/export?format=srt| format | 你得到 |
|---|---|
| txt | 纯文本,每行一个分段。 |
| srt | SubRip 字幕。 |
| vtt | WebVTT 字幕。 |
| json | 上面的文稿对象。 |
免费,不限次数。srt 和 vtt 需要时间信息;没有时间信息的文稿会返回 422 TIMESTAMPS_UNAVAILABLE。
批量任务#
/v1/batches一个请求,多个视频。每一项的字段与 创建任务相同;整个批量任务会被一起接受或一起拒绝。每个批量任务的项数:试用套餐 1 个,Starter 10 个,Pro 25 个,Scale 50 个。
curl -X POST https://www.transcriptdock.com/v1/batches \-H "Authorization: Bearer td_live_YOUR_KEY" \-H "Idempotency-Key: playlist-2026-09-16" \-H "Content-Type: application/json" \-d '{ "items": [{ "source": { "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ" }, "mode": "captions_only" },{ "source": { "url": "https://www.tiktok.com/@tiktok/video/7680721699171601694" }, "mode": "captions_only" }] }'
批量任务对象包含 status(queued、 processing、succeeded、 partial_success、failed、 cancelled)、计数(total_items、 succeeded_items、failed_items、 cancelled_items)和 items,每一项都有自己的 job_id。使用 GET /v1/batches/{id} 读取;每一项都是一个普通任务。
上传#
你自己的音频或视频(mp3、wav、m4a、ogg、aac、mp4、webm;最大 250 MB,最长 2 小时)。先预留一个上传位置,用 PUT 把文件传到签名 URL,标记为完成,然后用 source.upload_id 把它作为任务提交。上传的文件始终使用 AI 转录。
# 1. Reservecurl -X POST https://www.transcriptdock.com/v1/uploads \-H "Authorization: Bearer td_live_YOUR_KEY" \-H "Content-Type: application/json" \-d '{ "filename": "interview.mp3", "content_type": "audio/mpeg", "bytes": 48213920 }'# -> { "id": "33333333-...", "signed_upload_url": "https://...", "expires_at": "...", "status": "pending" }# 2. Upload the bytes (same Content-Type you declared)curl -X PUT "SIGNED_UPLOAD_URL" -H "Content-Type: audio/mpeg" --data-binary @interview.mp3# 3. Completecurl -X POST https://www.transcriptdock.com/v1/uploads/33333333-3333-4333-8333-333333333333/complete \-H "Authorization: Bearer td_live_YOUR_KEY"# 4. Transcribe itcurl -X POST https://www.transcriptdock.com/v1/jobs \-H "Authorization: Bearer td_live_YOUR_KEY" \-H "Idempotency-Key: interview-01" \-H "Content-Type: application/json" \-d '{ "source": { "upload_id": "33333333-3333-4333-8333-333333333333" }, "mode": "transcribe" }'
签名 URL 的有效期为 24 小时,过期后请重新预留一个上传位置。
Webhook#
/v1/webhook-endpoints注册一个 https URL,提交任务时把它的 id 作为 webhook_endpoint_id 传入。任务或批量任务完成时,我们会向它 POST 一个事件。响应只会包含一次用于签名的 secret。注册和停用端点需要已登录的会话,所以请在控制台的 Webhook 页面中操作;使用 API 密钥在这里会得到 403 FORBIDDEN,但可以列出端点。
事件#
{"event_id": "44444444-4444-4444-8444-444444444444","type": "job.succeeded","created_at": "2026-09-16T10:00:14.000Z","job_id": "11111111-1111-4111-8111-111111111111","batch_id": null,"result_id": "22222222-2222-4222-8222-222222222222","metadata": { "order": "8812" },"error": null}
job.succeeded:获取或导出result_id。job.failed:error中带有错误码。batch.completed:所有项都已进入最终状态;读取该批量任务即可得到每一项的结果。
请在几秒内返回任意 2xx 响应。投递失败时会按退避策略重试,也可以在控制台中重新发送。同一个事件可能会收到多次:请根据 event_id 去重。
验证签名#
请求头为 X-TranscriptDock-Event-Id、X-TranscriptDock-Timestamp 和 X-TranscriptDock-Signature。签名是用你的 secret 对 {event_id}.{timestamp}.{raw_body} 计算出的 HMAC-SHA256(十六进制)。请拒绝任何超过五分钟的请求。
import crypto from "node:crypto";export function verify(eventId: string, timestamp: string, rawBody: string, signature: string, secret: string): boolean {if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;const expected = crypto.createHmac("sha256", secret).update(`${eventId}.${timestamp}.${rawBody}`).digest("hex");return expected.length === signature.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));}
查找视频#
/v1/video-discoveries搜索 YouTube,或列出某个频道、播放列表或 TikTok 个人主页的视频,然后把得到的 URL 交给任务处理。视频发现和任务一样在后台运行:用 GET /v1/video-discoveries/{id} 读取,直到 status 变为 succeeded。每页 1 个点数;结果保留 24 小时。
curl -X POST https://www.transcriptdock.com/v1/video-discoveries \-H "Authorization: Bearer td_live_YOUR_KEY" \-H "Idempotency-Key: search-async-rust-1" \-H "Content-Type: application/json" \-d '{ "kind": "youtube_search", "query": "async rust", "limit": 5 }'
这个 POST 请求会返回 202 和 status: "queued"。已完成的 GET /v1/video-discoveries/{id} 响应如下:
{"id": "55555555-5555-4555-8555-555555555555","kind": "youtube_search","status": "succeeded","request": { "kind": "youtube_search", "query": "async rust", "limit": 5 },"videos": [{"platform": "youtube","video_id": "wXtngLBkK4Q","url": "https://www.youtube.com/watch?v=wXtngLBkK4Q","title": "Async Rust explained in 20 minutes","channel_id": "UCSp-OaMpsO8K0KkOqyBl7_w","channel_title": "Let's Get Rusty","duration_s": 1155,"published_at": null,"published_text": "5 months ago","view_count": 83635,"view_count_text": "83,635 views","thumbnail_url": "https://i.ytimg.com/vi/wXtngLBkK4Q/hq720.jpg","tiktok": null}],"profile": null,"next_cursor": "opaque-token","error": null,"created_at": "2026-09-16T12:00:00.000Z","updated_at": "2026-09-16T12:00:03.000Z","expires_at": "2026-09-17T12:00:00.000Z"}
视频的字幕语言#
/v1/language-discoveries请求体为 { "source": { "url": "https://www.youtube.com/watch?v=..." } },并带上 Idempotency-Key(仅限 YouTube,1 个点数)。读取 GET /v1/language-discoveries/{id} 可以得到字幕轨道列表,包括各自的语言代码、是创作者字幕还是自动字幕,以及默认轨道。请把这些代码用在 caption_languages 中。
用量和功能#
/v1/usage{"plan": "pro","period_end": "2026-10-16T00:00:00.000Z","credits_remaining": 5840,"credits_total": 6000,"credits_reserved": 4,"prepaid_credits": 0,"transcribe_credits_per_minute": 2,"minimum_billable_seconds": 60,"subscription": {"status": "active","interval": "yearly","cancel_at_period_end": false,"current_period_end": "2027-09-16T00:00:00.000Z"}}
credits_remaining 已经扣除了 credits_reserved (运行中任务预留的点数)。prepaid_credits 是购买的点数,永不过期。
subscription 是付费套餐背后的 Stripe 订阅,没有订阅时为 null,包含:status、计费周期 interval(monthly 或 yearly)、 cancel_at_period_end,以及 current_period_end(套餐续订或结束的时间)。 period_end 是每月点数刷新的时间,年付套餐也是每月刷新。
/v1/capabilities你的密钥当前能做什么:针对每个来源(youtube、 tiktok、instagram、upload、 direct)有哪些 模式可用,以及限制和价格。请读取它,而不要硬编码;例如试用套餐会把 auto 和 transcribe 报告为 false。
{"youtube": { "enabled": true, "captions_only": true, "auto": true, "transcribe": true, "social_acquisition_fee": true },"tiktok": { "enabled": true, "captions_only": true, "auto": true, "transcribe": true, "social_acquisition_fee": true },"instagram": { "enabled": false, "captions_only": false, "auto": false, "transcribe": false, "social_acquisition_fee": true, "native_captions": "unverified_disabled" },"upload": { "enabled": true, "captions_only": false, "auto": true, "transcribe": true, "social_acquisition_fee": false },"direct": { "enabled": true, "captions_only": false, "auto": true, "transcribe": true, "social_acquisition_fee": false },"max_duration_ms": 7200000,"max_media_bytes": 262144000,"export_formats": ["txt", "json", "srt", "vtt"],"recognition_profiles": ["standard"],"transcribe_credits_per_minute": 2,"minimum_billable_seconds": 60,"price_version": "2026-09-14","asr_enabled": true,"instagram_enabled": false,"free_caption_credits": 50,"free_caption_credits_period": "month"}
速率限制#
| 套餐 | 每分钟提交数 | 进行中的任务 | 每分钟读取数 |
|---|---|---|---|
| 试用 | 10 | 10 | 120 |
| Starter | 60 | 100 | 120 |
| Pro | 120 | 500 | 120 |
| Scale | 300 | 2,000 | 120 |
超出限制时,你会收到 429 RATE_LIMITED,其中带有 retry-after 响应头(单位为秒),以及指明具体限制的 details.detail。不会扣除任何点数。 使用 Webhook 和 ?wait=25 可以让你远离读取限制。
错误#
所有错误的结构都相同。retryable: true 表示同一个请求稍后可能成功:如果有 retry-after,就等待相应的秒数,否则退避几秒再试,并复用同一个 Idempotency-Key,这样就不会产生重复。其他错误则需要你这边做出修改。
{"error": {"code": "INSUFFICIENT_BALANCE","message": "Not enough credits. Buy credits or upgrade your plan to continue.","retryable": false,"details": { "detail": "This job needs more credits than you have left. Buy credits or upgrade your plan." },"doc_url": "https://www.transcriptdock.com/docs/api-reference#error-codes"},"request_id": "66666666-6666-4666-8666-666666666666"}
任务被接受之后才失败的情况,GET /v1/jobs/{id} 仍然返回 200,其中 status: "failed",error 中是同样的错误对象。失败的任务不会扣除任何点数。
错误码#
| 错误码 | HTTP | 重试 | 含义和处理方式 |
|---|---|---|---|
INVALID_REQUEST | 422 | 否 | 缺少某个字段,或字段格式不对。details.detail 会指明是哪个字段。 |
INVALID_URL | 422 | 否 | 该 URL 不是有效的 https 链接(最多 2048 个字符)。 |
UNSUPPORTED_SOURCE | 422 | 否 | 该链接不是 YouTube 或 TikTok 视频的 URL。 |
UNSAFE_URL | 422 | 否 | 该直接媒体链接指向私有或被屏蔽的网络地址。 |
INVALID_CURSOR | 422 | 否 | 视频发现的 cursor 已过期或格式不正确。请从第一页重新开始。任务列表的 cursor 有误时,返回的是 INVALID_REQUEST。 |
CAPABILITY_UNAVAILABLE | 422 | 否 | 该来源不支持这个模式。GET /v1/capabilities 会显示哪些可用。 |
IDEMPOTENCY_CONFLICT | 409 | 否 | 这个 Idempotency-Key 已经和不同的请求体一起使用过。请换一个新的 Idempotency-Key。 |
UNAUTHENTICATED | 401 | 否 | Authorization 请求头中没有有效的 API 密钥或 OAuth 令牌。 |
FORBIDDEN | 403 | 否 | 该密钥没有访问此接口所需的权限范围,或者工作区已被停用。 |
EMAIL_UNVERIFIED | 403 | 否 | 请先验证账户邮箱,再使用 API。 |
NOT_FOUND | 404 | 否 | 此工作区中没有这个对象。 |
INSUFFICIENT_BALANCE | 402 | 否 | 点数不足,无法完成此任务。请购买点数或升级套餐。 |
PLAN_REQUIRED | 402 | 否 | 这需要付费套餐(AI 转录、更大的批量),或者试用点数已经用完。 |
BUDGET_EXCEEDED | 402 | 否 | 测得的费用超过了 max_credits。没有扣除任何点数;请提高上限,或跳过这个媒体。 |
JOB_NOT_RETRYABLE | 409 | 否 | 这次失败是最终结果(私密视频、没有字幕)。请修正输入,然后提交新任务。 |
CANCELLATION_NOT_ALLOWED | 409 | 否 | AI 转录已经开始,任务会继续完成。 |
RATE_LIMITED | 429 | 是 | 超过了速率或队列限制。请等待 retry-after 指定的秒数;details.detail 会说明是哪项限制。 |
SOURCE_NOT_FOUND | 422 | 否 | 视频不存在或已被删除。 |
SOURCE_PRIVATE | 422 | 否 | 视频是私密的。只有公开视频可以使用。 |
SOURCE_AUTH_REQUIRED | 422 | 否 | 观看该视频需要登录、年龄验证或会员资格。 |
SOURCE_REGION_RESTRICTED | 422 | 否 | 该视频在我们获取内容的地区被屏蔽。 |
NO_CAPTIONS | 422 | 否 | 这个视频没有字幕,而 mode 是 captions_only。请改用 auto 或 transcribe。 |
LANGUAGE_UNAVAILABLE | 422 | 否 | 这个视频没有 caption_languages 中的任何一种语言。去掉这个列表,即可使用默认轨道。 |
LANGUAGE_UNSUPPORTED | 422 | 否 | AI 转录不支持所请求的语言。 |
NO_SPEECH | 422 | 否 | AI 转录没有在音频中检测到语音。 |
NO_AUDIO | 422 | 否 | 该媒体没有音轨。 |
INVALID_MEDIA | 422 | 否 | 无法把该文件当作音频或视频读取。 |
UNSUPPORTED_MEDIA | 422 | 否 | 不是音频或视频文件类型。 |
DURATION_LIMIT_EXCEEDED | 413 | 否 | 时长超过 2 小时。 |
FILE_TOO_LARGE | 413 | 否 | 文件超过 250 MB。 |
TIMESTAMPS_UNAVAILABLE | 422 | 否 | 这份文稿没有时间信息,因此无法导出 srt 和 vtt。请使用 txt 或 json。 |
PROVIDER_REJECTED | 422 | 否 | AI 转录模型无法处理这段音频。 |
SOURCE_RATE_LIMITED | 503 | 是 | 来源平台正在限制我们的请求。任务会自动重试。 |
SOURCE_BLOCKED | 503 | 是 | 来源平台拦截了这次获取。任务会自动重试。 |
SOURCE_TIMEOUT | 503 | 是 | 来源平台响应超时。任务会自动重试。 |
SOURCE_CHANGED | 503 | 是 | 我们读取时来源发生了变化。任务会自动重试。 |
PROVIDER_UNAVAILABLE | 503 | 是 | AI 转录暂时不可用。任务会自动重试。 |
STORAGE_UNAVAILABLE | 503 | 是 | 文件存储暂时不可用。请重试这个请求。 |
INTERNAL_ERROR | 500 | 是 | 这是我们的问题。请使用同一个 Idempotency-Key 重试;如果问题持续,请附上 X-Request-Id 联系我们。 |