文档API接口参考

API接口参考

查阅所有/v1接口的参数和调用示例,或下载JSON、YAML格式的OpenAPI规范。

概览

API地址
https://api.transcript1.com/v1

API统一使用https://api.transcript1.com/v1作为基础地址。

  • 请求体和响应体采用JSON,字段名采用snake_case。
  • 时间戳统一为UTC,按ISO 8601格式返回,例如2026-10-13T09:30:00.000Z。
  • 音视频时长、转录文本中的时间都以秒计。
  • 错误响应的结构一致,处理方法见错误说明。
  • 通过认证的请求,响应头都会包含X-RateLimit-Limit和X-RateLimit-Remaining。各套餐的请求上限见计费规则与用量限制。
  • 设置source=auto时,优先提取YouTube、X、TikTok、Bluesky、Dailymotion和LinkedIn上与视频原语言一致的可用字幕,没有可用字幕时再转录音频。并不是每个视频都有字幕。source=captions使用captions(仅字幕)模式,不会下载音频,也不会改用语音转文字;找不到可用字幕轨道就返回no_captions。设置source=stt或开启说话人分离时,会跳过字幕。各平台的获取限制,以及分段和逐词时间戳的支持情况,见来源支持表。

需要供程序读取的接口定义,可下载OpenAPI规范:

身份验证

curl https://api.transcript1.com/v1/account \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY"

调用API时,用Authorization请求头提供凭据,格式为Authorization: Bearer <token>。

  • API密钥以t1_开头,可调用所有接口。在“API keys”页面创建即可。
  • OAuth访问Token以t1o_开头。连接Claude、ChatGPT等MCP客户端时,客户端会取得该Token,权限范围为transcriptions:read和transcriptions:write。
权限范围可调用的接口
transcriptions:readGET /v1/transcriptions、GET /v1/transcriptions/:id、GET /v1/transcriptions/:id/upload和GET /v1/account
transcriptions:writePOST /v1/transcriptions、DELETE /v1/transcriptions/:id和POST /v1/transcriptions/:id/upload/complete
  • Token没有接口所需的权限时,返回403 insufficient_scope。
  • 没有提供凭据,或凭据无效时,返回401 unauthorized。

上传分块的PUT /v1/transcriptions/:id/upload/chunks/:index接口使用X-Upload-Token认证,Token在创建上传时返回。具体流程见文件上传。

幂等性

curl -X POST https://api.transcript1.com/v1/transcriptions \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY" \
  -H "Idempotency-Key: 5f1c2b0e-8d4a-4c3e-9b7a-2e6f1d0c9a8b" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://www.youtube.com/watch?v=dQw4w9WgXcQ"}'

重试POST /v1/transcriptions时,带上Idempotency-Key请求头,避免重复创建和扣费。

该值需为1到255个字符,只能放在请求头中,不能作为查询参数传入。

  • 重试时保持键值和参数不变,API就会返回原来的转录,不再扣费。
  • 沿用同一个键值却修改参数,会返回409 idempotency_conflict。
  • 创建分块上传时,用同一个键值重发创建请求,会获得新的上传Token。

错误

402 Payment Required
{
  "error": {
    "code": "insufficient_minutes",
    "message": "Not enough minutes",
    "retryable": false,
    "action_url": "https://transcript1.com/dashboard/billing",
    "minutes_needed": 20,
    "minutes_remaining": 3
  }
}
  • 请求错误:这次调用没有通过API校验或未被接受。
  • 任务失败:API已经接受转录,但后续处理没有成功,任务的status变为failed。

读取错误响应

/v1接口拒绝请求时,会在error对象中返回code、message和retryable:

  • 用code判断错误类型并编写处理逻辑。
  • message供用户阅读,不要依赖其中的具体措辞,文案可能调整。
  • retryable为true时,可以稍后重试,同样的请求有可能成功。

部分错误还会在同一层级附上其他字段:

字段对应错误码说明
action_urlinsufficient_minutes打开后可购买套餐或充值。
minutes_needed、minutes_remaininginsufficient_minutes分别为本次请求需要的分钟数和当前余额;所需分钟数尚未确定时为null。
supported_platformsunsupported_platform支持的平台值列表:["youtube", "x", "tiktok", "instagram", "facebook", "bluesky", "twitch", "dailymotion", "pinterest", "linkedin", "snapchat"]。
missing_chunksupload_incomplete服务端尚未收到的分块索引。
max_bytesmedia_too_large当前允许上传的文件大小上限,单位为字节。
paramsinvalid_request按字段列出请求体或查询参数的校验问题。对象中的字段名对应具体错误说明。

请求被拒绝时的错误码

HTTP状态码code可否重试原因和处理方法
400、422invalid_request否参数或请求体字段缺失、值无效,或选项不能组合使用,例如diarize与source: "captions",以及reuse_recent与webhook_url。请求体不是合法JSON时,也返回此错误。
422invalid_url否链接需来自YouTube、X、TikTok、Instagram、Facebook、Bluesky,或来源与选项列出的其他平台。指定的platform与链接不符时,也返回此错误。
422unsupported_platform否platform的值不在支持范围内。
422unsupported_media否文件扩展名不在支持范围内。
401unauthorized否没有提供凭据,或凭据无效、过期、已撤销;上传Token无效或过期时也会出现。
403insufficient_scope否OAuth Token没有所需的transcriptions:read或transcriptions:write权限。
403email_not_verified否邮箱还未验证,先完成验证再创建转录。
402insufficient_minutes否可用分钟数为0,或不足以支付本次转录。
402no_active_plan否没有有效的付费套餐,却尝试充值。此错误只由控制台的计费接口返回,转录接口不会返回。
404not_found否找不到该转录,或转录属于其他团队。
409upload_incomplete否还有分块未上传。补传后,再调用完成上传接口。
409invalid_state否任务已不在等待上传的状态,不能继续发送分块,也不能再完成上传。
409idempotency_conflict否这个Idempotency-Key已被使用,但本次参数与之前不同。
413media_too_large否文件大小超过当前上限,具体限制见文件上传。
413invalid_request否JSON请求体超过1MB。文件过大时返回的则是media_too_large。
429rate_limited是本分钟请求次数已超限,按Retry-After给出的秒数等待后再试。
429too_many_active_jobs是活跃转录任务或未完成的上传数量已达上限,按Retry-After给出的秒数等待后再试。
500internal_error是服务端发生错误,稍后重试。
503service_unavailable是transcript1暂时无法提供服务,稍后重试。

浏览器使用登录Cookie认证时,还必须通过来源检查。请求来自其他网站时,返回403 forbidden_origin。

通过Authorization请求头认证的API调用,不受这项来源检查影响。

所有429响应都会带上Retry-After响应头,告诉调用方需要等待多少秒。各项上限见计费规则与用量限制。

什么时候应该重试

  • 只自动重试retryable为true的错误:rate_limited、too_many_active_jobs、internal_error和service_unavailable。
  • 响应包含Retry-After时,按它指定的秒数等待;没有时,逐步增加重试间隔。
  • 重试POST /v1/transcriptions必须沿用原来的Idempotency-Key请求头。即使首个请求其实已经成功,也不会重复扣费。
  • 遇到其他错误码,先修正请求,再重新提交。

请求成功后任务仍可能失败

转录任务变为failed后,返回的error对象会包含code和message。

请求返回不代表转录已经成功,后续处理仍可能失败。轮询任务或收到transcription.failedWebhook时,都应检查error。

error.code失败原因是否重试及处理方法
media_unavailable视频不存在,或现在已经无法访问。不要直接重试,先检查URL。
media_private视频设为私有。不要直接重试,改用公开帖子;YouTube还可使用不公开但能凭链接访问的视频。
login_required平台要求登录后才能访问。不重试,这是永久性失败。
no_audio视频没有音轨。不重试,这是永久性失败。
age_restricted视频受到年龄限制。不要直接重试,改为上传文件。
live_stream视频正在直播,或直播还没开始。等直播结束后再提交。
no_captions选择了source为captions,但视频没有可用字幕轨道。改用source: "auto"或"stt"。
media_too_long语音转文字支持的音视频时长上限为5小时。不要直接重试,先拆分音视频。
unsupported_media无法读取文件,或文件没有音轨。不要直接重试,换成支持的文件格式。
no_speech未检测到语音。不重试。
unsupported_language音视频使用的语言不受支持。不要直接重试,将language改为受支持的语言代码或"auto"。
insufficient_minutes结算本次转录前,分钟余额已用完。充值后可以重试。
fetch_failed重试多次后,仍未能获取音视频。可以稍后重试。
transcription_failed重试多次后,语音转文字仍未成功。可以稍后重试。

如果转录失败或被取消,不会扣费;已经扣除的分钟数会退还。

Transcription对象

Transcription对象
{
  "id": "tr_4fHq8ZbN2xLm0PqRsTuVwX",
  "object": "transcription",
  "status": "completed",
  "source": {
    "platform": "youtube",
    "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "media_id": "dQw4w9WgXcQ"
  },
  "media": {
    "title": "Example video",
    "author": "Example channel",
    "author_url": "https://www.youtube.com/@examplechannel",
    "thumbnail_url": "https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg",
    "duration_seconds": 212
  },
  "options": {
    "source": "auto",
    "language": "auto",
    "diarize": false
  },
  "transcript_source": "youtube_captions",
  "caption_kind": "auto_generated",
  "language": "en",
  "billed_minutes": 4,
  "cached": false,
  "error": null,
  "created_at": "2026-10-13T09:30:00.000Z",
  "completed_at": "2026-10-13T09:30:04.000Z",
  "deleted_at": null,
  "transcript": {
    "text": "Welcome back to the channel. Today we are looking at how transcripts are made.",
    "speakers": [],
    "segments": [
      {
        "start": 0,
        "end": 4.2,
        "speaker": null,
        "language": "en",
        "text": "Welcome back to the channel."
      },
      {
        "start": 4.2,
        "end": 9.8,
        "speaker": null,
        "language": "en",
        "text": "Today we are looking at how transcripts are made."
      }
    ]
  }
}

字段

  • idstring必填
  • objectstring必填

    可选值transcription

  • statusstring必填

    可选值awaiting_uploadqueuedfetchingtranscribingcompletedfailedcanceled

  • sourceobject必填
    展开子字段
    With url
    • platformstring必填
    • urlstring必填允许为null
    • media_idstring必填允许为null
    With filename
    • platformstring必填

      可选值upload

    • filenamestring必填允许为null
    • sizenumber必填允许为null
  • mediaobject必填允许为null
    展开子字段
    • titlestring可选允许为null
    • authorstring可选允许为null
    • author_urlstring可选允许为null
    • thumbnail_urlstring可选允许为null
    • filenamestring可选
    • mime_typestring可选
    • sizenumber可选
    • duration_secondsnumber可选允许为null
  • optionsobject必填
    展开子字段
    • sourcestring必填

      可选值autocaptionsstt

    • languagestring必填
    • diarizeboolean必填
  • transcript_sourcestring必填允许为null

    可选值youtube_captionsx_captionsspeech_to_text

  • caption_kindstring必填允许为null

    可选值manualauto_generated

  • languagestring必填允许为null
  • billed_minutesinteger必填允许为null
  • cachedboolean必填
  • errorobject必填允许为null
    展开子字段
    • codestring必填
    • messagestring必填允许为null
  • created_atstring · date-time必填允许为null
  • completed_atstring · date-time必填允许为null
  • deleted_atstring · date-time必填允许为null

    When the transcription was deleted. Always null unless the request includes include_deleted=true.

  • transcriptobject可选

    Included in GET responses for completed transcriptions.

    展开子字段
    • textstring必填
    • speakersarray of strings必填
    • segmentsarray of objects必填
      展开子字段
      • startnumber必填
      • endnumber必填
      • speakerstring必填允许为null
      • languagestring必填允许为null
      • textstring必填
      • wordsarray of objects可选允许为null
        展开子字段
        • startnumber必填
        • endnumber必填
        • textstring必填
        • confidencenumber可选允许为null
        • speakerstring必填允许为null

Transcriptions

Create, read, list and delete transcriptions of video links and uploaded files.

Create a transcription

POST /v1/transcriptions

Transcribes an uploaded file or a YouTube, X, TikTok, Instagram, Facebook, Bluesky, Twitch, Dailymotion, Pinterest, LinkedIn, Snapchat link. Social posts must be public; only the first video is used.

Twitch accepts clips and completed VODs; Pinterest accepts native video pins; LinkedIn accepts public video posts; Snapchat accepts Spotlight. New platforms require public recorded native media without supplied account cookies; no live streams or external embeds.

platform is optional and detected from the link. Captions are YouTube/X-only. All sources use the same per-minute price.

Returns 200 for a final result, 201 when awaiting an upload and 202 while processing. Pass wait to wait up to 60 seconds for a final status.

Pass reuse_recent=true with a platform URL to reuse a transcription of the same video and options from the last 24 hours. The response includes reused: true, with no additional charge. Short links may not be matched. An Idempotency-Key replay takes precedence.

curl -X POST https://api.transcript1.com/v1/transcriptions \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://www.youtube.com/watch?v=dQw4w9WgXcQ"}'
{
  "id": "tr_4fHq8ZbN2xLm0PqRsTuVwX",
  "object": "transcription",
  "status": "queued",
  "source": {
    "platform": "youtube",
    "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "media_id": "dQw4w9WgXcQ"
  },
  "media": null,
  "options": {
    "source": "auto",
    "language": "auto",
    "diarize": false
  },
  "transcript_source": null,
  "caption_kind": null,
  "language": null,
  "billed_minutes": null,
  "cached": false,
  "error": null,
  "created_at": "2026-10-13T09:30:00.000Z",
  "completed_at": null,
  "deleted_at": null,
  "reused": false
}

查询参数

  • waitinteger可选允许为null默认值:0

    Seconds to wait for a final status before responding, up to 60. Defaults to 0.

  • reuse_recentstring可选默认值:false

    For platform URLs only. Cannot be combined with webhook_url. Returns the team's most recent transcription of the same video with the same source, language and diarize. It must have been created in the last 24 hours and must not be failed or canceled. Reuses that transcription without creating or charging for a new one.

    可选值truefalse

请求头

  • Idempotency-Keystring可选

    Returns the original transcription when you repeat a request with the same key and parameters.

请求体

With url
  • platformstring可选

    Optional; detected from the link. If supplied, must match the link.

    可选值youtubextiktokinstagramfacebookblueskytwitchdailymotionpinterestlinkedinsnapchat

  • urlstring必填

    YouTube, X, TikTok, Instagram, Facebook, Bluesky, Twitch, Dailymotion, Pinterest, LinkedIn, Snapchat video link. Social posts must be public; only the first video is used. New platforms support public recorded native video without supplied account cookies, not live streams or external embeds.

  • sourcestring可选

    "auto" uses YouTube or X captions when available, otherwise speech-to-text. "captions" is YouTube/X-only. "stt" always uses speech-to-text.

    可选值autocaptionsstt

  • languagestring可选
  • diarizeboolean可选
  • webhook_urlstring · uri可选
With upload
  • uploadobject必填
    展开子字段
    • filenamestring必填
    • sizeinteger必填
    • mime_typestring可选
  • sourcestring可选

    可选值autostt

  • languagestring可选
  • diarizeboolean可选
  • webhook_urlstring · uri可选
multipart/form-data
  • filestring · binary必填
  • sourcestring可选

    可选值autostt

  • languagestring可选
  • diarizeboolean可选
  • webhook_urlstring · uri可选

返回内容

  • 200 OK — The transcription reached a final status
  • 201 Created — The transcription is awaiting a chunked upload. upload describes where to send each chunk.
  • 202 Accepted — The transcription is processing

其他响应状态

  • 400 Bad Request — The JSON request body is malformed
  • 401 Unauthorized — Missing or invalid credential
  • 402 Payment Required — Not enough minutes. Applies to every request body format.
  • 403 Forbidden — Insufficient scope, unverified email, cross-site request or a single-member team plan
  • 409 Conflict — Idempotency-Key was reused with different parameters.
  • 413 Content Too Large — The upload or the JSON request body is too large
  • 422 Unprocessable Content — Invalid request, URL, platform or file type
  • 429 Too Many Requests — Too many requests or too many active transcriptions. Wait for the number of seconds in Retry-After.

Get a transcription

GET /v1/transcriptions/{id}

Returns the transcription object. Completed transcriptions include the transcript. Use format to render it as text, SRT or WebVTT.

If you request a rendered format before completion, the response is JSON. It returns 202 while processing or 200 if the transcription failed or was canceled.

With include_deleted=true, a deleted transcription is returned as a record without a transcript. For deleted records, wait is ignored and rendered formats return 404.

curl https://api.transcript1.com/v1/transcriptions/tr_4fHq8ZbN2xLm0PqRsTuVwX \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY"
{
  "id": "tr_4fHq8ZbN2xLm0PqRsTuVwX",
  "object": "transcription",
  "status": "completed",
  "source": {
    "platform": "youtube",
    "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "media_id": "dQw4w9WgXcQ"
  },
  "media": {
    "title": "Example video",
    "author": "Example channel",
    "author_url": "https://www.youtube.com/@examplechannel",
    "thumbnail_url": "https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg",
    "duration_seconds": 212
  },
  "options": {
    "source": "auto",
    "language": "auto",
    "diarize": false
  },
  "transcript_source": "youtube_captions",
  "caption_kind": "auto_generated",
  "language": "en",
  "billed_minutes": 4,
  "cached": false,
  "error": null,
  "created_at": "2026-10-13T09:30:00.000Z",
  "completed_at": "2026-10-13T09:30:04.000Z",
  "deleted_at": null,
  "transcript": {
    "text": "Welcome back to the channel. Today we are looking at how transcripts are made.",
    "speakers": [],
    "segments": [
      {
        "start": 0,
        "end": 4.2,
        "speaker": null,
        "language": "en",
        "text": "Welcome back to the channel."
      },
      {
        "start": 4.2,
        "end": 9.8,
        "speaker": null,
        "language": "en",
        "text": "Today we are looking at how transcripts are made."
      }
    ]
  }
}

路径参数

  • idstring必填

查询参数

  • waitinteger可选允许为null默认值:0

    Seconds to wait for a final status before responding, up to 60. Defaults to 0.

  • formatstring可选默认值:json

    可选值jsontextsrtvtt

  • includestring可选

    可选值words

  • timestampsstring可选默认值:false

    可选值truefalse

  • include_deletedstring可选默认值:false

    Also returns deleted transcriptions as read-only records without a transcript. Their deleted_at is set. Defaults to false.

    可选值truefalse

返回内容

  • 200 OK — The transcription, or the rendered transcript
  • 202 Accepted — A rendered format was requested while the transcription is still processing

其他响应状态

  • 401 Unauthorized — Missing or invalid credential
  • 403 Forbidden — Insufficient scope or a single-member team plan
  • 404 Not Found — Transcription not found
  • 422 Unprocessable Content — Invalid query parameters
  • 429 Too Many Requests — Too many requests. Wait for the number of seconds in Retry-After.

List transcriptions

GET /v1/transcriptions

Lists your team's transcriptions, newest first, without transcripts. Pass next_cursor as cursor to get the next page.

Deleted transcriptions are hidden unless you pass include_deleted=true.

curl https://api.transcript1.com/v1/transcriptions \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY"
{
  "object": "list",
  "data": [
    {
      "id": "tr_4fHq8ZbN2xLm0PqRsTuVwX",
      "object": "transcription",
      "status": "completed",
      "source": {
        "platform": "youtube",
        "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
        "media_id": "dQw4w9WgXcQ"
      },
      "media": {
        "title": "Example video",
        "author": "Example channel",
        "author_url": "https://www.youtube.com/@examplechannel",
        "thumbnail_url": "https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg",
        "duration_seconds": 212
      },
      "options": {
        "source": "auto",
        "language": "auto",
        "diarize": false
      },
      "transcript_source": "youtube_captions",
      "caption_kind": "auto_generated",
      "language": "en",
      "billed_minutes": 4,
      "cached": false,
      "error": null,
      "created_at": "2026-10-13T09:30:00.000Z",
      "completed_at": "2026-10-13T09:30:04.000Z",
      "deleted_at": null
    }
  ],
  "next_cursor": "eyJjcmVhdGVkQXQiOiIyMDI2LTEwLTEzVDA5OjMwOjAwLjAwMFoiLCJpZCI6IjViMGU2YzFjLTBkNmYtNGMxZi05YjVlLThmM2Y0ZDBhN2ExMSJ9"
}

查询参数

  • limitinteger可选默认值:20
  • cursorstring可选
  • statusstring可选

    可选值awaiting_uploadqueuedfetchingtranscribingcompletedfailedcanceled

  • platformstring可选
  • include_deletedstring可选默认值:false

    Also returns deleted transcriptions as read-only records without a transcript. Their deleted_at is set. Defaults to false.

    可选值truefalse

返回内容

  • 200 OK — A page of transcriptions

其他响应状态

  • 401 Unauthorized — Missing or invalid credential
  • 403 Forbidden — Insufficient scope or a single-member team plan
  • 422 Unprocessable Content — Invalid query parameters or cursor
  • 429 Too Many Requests — Too many requests. Wait for the number of seconds in Retry-After.

Cancel or delete a transcription

DELETE /v1/transcriptions/{id}

Cancels an active transcription or one awaiting an upload. Any charged minutes are refunded.

Deletes a completed, failed or canceled transcription together with its stored transcript.

curl -X DELETE https://api.transcript1.com/v1/transcriptions/tr_4fHq8ZbN2xLm0PqRsTuVwX \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY"

不返回响应体

路径参数

  • idstring必填

返回内容

  • 204 No Content — Canceled or deleted

其他响应状态

  • 401 Unauthorized — Missing or invalid credential
  • 403 Forbidden — Insufficient scope, cross-site request or a single-member team plan
  • 404 Not Found — Transcription not found
  • 429 Too Many Requests — Too many requests. Wait for the number of seconds in Retry-After.

Uploads

Send a large file in chunks. Start with Create a transcription using an upload body. Send each chunk, then complete the upload.

Upload one chunk

PUT /v1/transcriptions/{id}/upload/chunks/{index}

Sends one chunk of a chunked upload as application/octet-stream.

Authenticate with the X-Upload-Token from the create response. This endpoint does not use a bearer credential.

The body must match the planned chunk size exactly. Sending a chunk again replaces it.

curl -X PUT https://api.transcript1.com/v1/transcriptions/tr_4fHq8ZbN2xLm0PqRsTuVwX/upload/chunks/0 \
  -H "X-Upload-Token: t1u_Zk3vYb8Nw1ZrT5xLpD0aHcE7sUj9GfMiO2yXtVnR4Bw" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @chunk-0.bin
{
  "object": "upload_chunk",
  "index": 0,
  "size": 838860
}

路径参数

  • idstring必填
  • indexinteger可选允许为null

    Zero-based chunk number from the upload plan

请求头

  • X-Upload-Tokenstring必填

请求体

  • (原始请求体)string · binary必填

返回内容

  • 200 OK — The chunk was stored

其他响应状态

  • 401 Unauthorized — Missing, invalid or expired upload token
  • 404 Not Found — Transcription not found
  • 409 Conflict — The transcription is no longer awaiting an upload
  • 422 Unprocessable Content — Index outside the chunk plan or wrong body size

Get upload progress

GET /v1/transcriptions/{id}/upload

Lists the received chunks of a chunked upload so an interrupted upload can resume.

curl https://api.transcript1.com/v1/transcriptions/tr_4fHq8ZbN2xLm0PqRsTuVwX/upload \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY"
{
  "object": "upload",
  "status": "awaiting_upload",
  "total_bytes": 48210334,
  "uploaded_bytes": 4194300,
  "received_chunks": [
    0,
    1,
    2,
    3,
    4
  ],
  "chunks": [
    {
      "index": 0,
      "offset": 0,
      "size": 838860
    },
    {
      "index": 1,
      "offset": 838860,
      "size": 838860
    },
    {
      "index": 2,
      "offset": 1677720,
      "size": 838860
    },
    {
      "index": 3,
      "offset": 2516580,
      "size": 838860
    },
    {
      "index": 4,
      "offset": 3355440,
      "size": 838860
    },
    {
      "index": 5,
      "offset": 4194300,
      "size": 8388608
    },
    {
      "index": 6,
      "offset": 12582908,
      "size": 8388608
    },
    {
      "index": 7,
      "offset": 20971516,
      "size": 8388608
    },
    {
      "index": 8,
      "offset": 29360124,
      "size": 8388608
    },
    {
      "index": 9,
      "offset": 37748732,
      "size": 8388608
    },
    {
      "index": 10,
      "offset": 46137340,
      "size": 2072994
    }
  ],
  "expires_at": "2026-10-13T10:30:00.000Z"
}

路径参数

  • idstring必填

返回内容

  • 200 OK — Upload progress

其他响应状态

  • 401 Unauthorized — Missing or invalid credential
  • 403 Forbidden — Insufficient scope or a single-member team plan
  • 404 Not Found — Transcription or upload not found
  • 429 Too Many Requests — Too many requests. Wait for the number of seconds in Retry-After.

Complete a chunked upload

POST /v1/transcriptions/{id}/upload/complete

Checks that every planned chunk arrived with the planned size. Then assembles the file and queues the transcription.

curl -X POST https://api.transcript1.com/v1/transcriptions/tr_4fHq8ZbN2xLm0PqRsTuVwX/upload/complete \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY"
{
  "id": "tr_4fHq8ZbN2xLm0PqRsTuVwX",
  "object": "transcription",
  "status": "queued",
  "source": {
    "platform": "upload",
    "filename": "interview.m4a",
    "size": 48210334
  },
  "media": {
    "filename": "interview.m4a",
    "mime_type": "audio/mp4",
    "size": 48210334
  },
  "options": {
    "source": "auto",
    "language": "auto",
    "diarize": false
  },
  "transcript_source": null,
  "caption_kind": null,
  "language": null,
  "billed_minutes": null,
  "cached": false,
  "error": null,
  "created_at": "2026-10-13T09:30:00.000Z",
  "completed_at": null,
  "deleted_at": null
}

路径参数

  • idstring必填

返回内容

  • 202 Accepted — The transcription is queued

其他响应状态

  • 401 Unauthorized — Missing or invalid credential
  • 402 Payment Required — Not enough minutes to queue the transcription. The upload stays awaiting completion.
  • 403 Forbidden — Insufficient scope, cross-site request or a single-member team plan
  • 404 Not Found — Transcription or upload not found
  • 409 Conflict — upload_incomplete means chunks are missing. missing_chunks lists them. invalid_state means the transcription is not awaiting an upload.
  • 429 Too Many Requests — Too many requests or too many active transcriptions. Wait for the number of seconds in Retry-After.

Account

Your plan, remaining minutes and rate limits.

Get your account

GET /v1/account

Returns the authenticated user, current plan and remaining transcription minutes.

curl https://api.transcript1.com/v1/account \
  -H "Authorization: Bearer $TRANSCRIPT1_API_KEY"
{
  "object": "account",
  "user": {
    "id": "5b0e6c1c-0d6f-4c1f-9b5e-8f3f4d0a7a11",
    "email": "[email protected]"
  },
  "plan": {
    "id": "9d4e0c0a-2b8a-4f0c-8d63-6a8d3c8a1f22",
    "name": "Monthly"
  },
  "minutes": {
    "remaining": 1180,
    "cycle": 980,
    "topup": 200,
    "topup_usable": true,
    "resets_at": "2026-11-13T09:30:00.000Z"
  },
  "renews_at": "2026-11-13T09:30:00.000Z",
  "limits": {
    "requests_per_minute": 200,
    "max_active_jobs": 10
  }
}

返回内容

  • 200 OK — The authenticated user's account

其他响应状态

  • 401 Unauthorized — Missing or invalid credential
  • 403 Forbidden — Insufficient scope or a single-member team plan
  • 404 Not Found — No account found
  • 429 Too Many Requests — Too many requests. Wait for the number of seconds in Retry-After.

Account对象

Account对象
{
  "object": "account",
  "user": {
    "id": "5b0e6c1c-0d6f-4c1f-9b5e-8f3f4d0a7a11",
    "email": "[email protected]"
  },
  "plan": {
    "id": "9d4e0c0a-2b8a-4f0c-8d63-6a8d3c8a1f22",
    "name": "Monthly"
  },
  "minutes": {
    "remaining": 1180,
    "cycle": 980,
    "topup": 200,
    "topup_usable": true,
    "resets_at": "2026-11-13T09:30:00.000Z"
  },
  "renews_at": "2026-11-13T09:30:00.000Z",
  "limits": {
    "requests_per_minute": 200,
    "max_active_jobs": 10
  }
}

字段

  • objectstring必填

    可选值account

  • userobject必填
    展开子字段
    • idstring必填
    • emailstring必填
  • planobject必填允许为null
    展开子字段
    • idstring必填
    • namestring必填
  • minutesobject必填
    展开子字段
    • remainingnumber必填
    • cyclenumber必填
    • topupnumber必填
    • topup_usableboolean必填
    • resets_atstring · date-time必填允许为null
  • renews_atstring · date-time必填允许为null
  • limitsobject必填
    展开子字段
    • requests_per_minutenumber必填
    • max_active_jobsnumber必填

Webhook

import { Buffer } from 'node:buffer';
import crypto from 'node:crypto';

// header: X-Transcript1-Signature, rawBody: the request body exactly as received,
// secret: your full signing secret including the whsec_ prefix.
export const verifySignature = (secret, header, rawBody, nowSeconds = Math.floor(Date.now() / 1000)) => {
  const fields = Object.fromEntries(header.split(',').map(part => part.trim().split('=')));
  const t = Number(fields.t);

  if (!Number.isInteger(t) || !fields.v1 || Math.abs(nowSeconds - t) > 300) {
    return false;
  }

  const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');

  return fields.v1.length === expected.length
    && crypto.timingSafeEqual(Buffer.from(fields.v1), Buffer.from(expected));
};
transcription.completed
{
  "type": "transcription.completed",
  "created_at": "2026-10-13T09:30:04.000Z",
  "data": {
    "id": "tr_8fK2Lm0PqRsTuVwXyZaBcD",
    "object": "transcription",
    "status": "completed",
    "transcript_source": "youtube_captions",
    "billed_minutes": 4,
    "error": null
  }
}

创建转录时指定webhook_url,就能在转录完成或失败时收到通知。transcript1会向该地址发送带签名的POST请求。

只有REST API支持Webhook,MCP服务器不提供Webhook参数。

配置接收通知的地址

调用POST /v1/transcriptions时,按上传方式传入webhook_url:

  • 提交受支持的视频链接或创建分块上传时,放进JSON请求体。
  • 用单次请求上传文件时,放进表单字段。

接收地址需满足以下条件:

  • 使用HTTPS协议。
  • URL中不能包含用户名或密码。
  • 解析后的地址必须是公网地址。

私有地址、环回地址、链路本地地址以及其他非公网地址,都会导致422 invalid_request。创建转录时会检查地址,每次发送通知前也会重新检查。

不要同时设置webhook_url和reuse_recent=true,两者不能合用。

哪些状态会触发通知

type触发条件
transcription.completed转录状态变为completed。
transcription.failed转录状态变为failed,错误码code和错误说明message在data.error中返回。

取消转录不会触发Webhook。

通知中包含什么

Webhook请求体采用JSON:

  • created_at记录事件的创建时间。
  • data包含转录对象,但不含transcript字段。

收到通知后,从data中取出id,再调用GET /v1/transcriptions/:id获取转录文本。

每次请求还会携带以下请求头:

  • Content-Type: application/json
  • User-Agent: transcript1-webhooks/1.0
  • X-Transcript1-Signature,用于下文的签名校验。

校验通知签名

每次通知的X-Transcript1-Signature请求头都采用t=<unix seconds>,v1=<hex digest>格式:

  • t表示签名时的Unix时间戳,单位为秒。
  • v1是用HMAC-SHA256计算后转成十六进制的签名。待签名内容为"<t>.<raw body>":将t的值、英文句点和原始请求体依次拼接。计算时使用完整的签名密钥,包括whsec_前缀。

务必先用收到的原始请求体字节校验签名,再解析JSON。不要先解析再序列化:空白字符或字段顺序一旦改变,签名就无法匹配。

  • 比较签名时使用恒定时间比较。
  • 检查t与当前时间的差值,超出允许范围就拒绝请求。示例允许相差5分钟。
  • Express中要读取请求体原文,不要先交给JSON解析器。
  • Flask中用request.get_data(as_text=True)读取,再作为raw_body传入。

管理团队签名密钥

签名密钥属于团队,以whsec_开头。团队创建的所有转录,都使用这份密钥签署Webhook。

到“Webhooks”页面可以查看或轮换密钥。

轮换后旧密钥立即被替换,之后发送的Webhook全部使用新密钥。

发送失败后如何重试

  • 返回任意2xx状态码,就视为接收成功。
  • 返回其他状态码、连接出错,或超过10秒还未响应,都算本次发送失败。
  • 失败后按指数退避重试,总共最多尝试6次。重试前分别等待2、4、8、16和32分钟,最后1次大约在首次发送的1小时后。
  • 请直接填写最终URL。transcript1不会跟随重定向,收到3xx同样算发送失败。
  • 同一条通知可能重复送达,例如服务器超过超时时间才响应。请按data.id和type识别并忽略重复通知。