DocsAPI reference
API reference
Every /v1 endpoint with parameters and examples. Download the OpenAPI spec as JSON or YAML.
Introduction
https://api.transcript1.com/v1The base URL is https://api.transcript1.com/v1.
- Request and response bodies are JSON with
snake_casefield names. - Timestamps are ISO 8601 in UTC, for example
2026-10-13T09:30:00.000Z. - Durations and transcript times are in seconds.
- Every error uses the same structure. See Errors.
- Every response to an authenticated request carries
X-RateLimit-LimitandX-RateLimit-Remaining. See Billing and limits. source=autoprefers usable original-language captions on YouTube, X, TikTok, Bluesky, Dailymotion and LinkedIn, otherwise speech-to-text. Not every video has captions.source=captionsnever downloads audio or falls back to STT; no usable track meansno_captions.source=sttand speaker labels bypass captions. See the source capability matrix for retrieval restrictions and segment versus word timing.
Download the machine-readable OpenAPI spec:
Authentication
curl https://api.transcript1.com/v1/account \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY"Send a credential in the Authorization header: Authorization: Bearer <token>.
- An API key starts with
t1_. Create one on the API keys page. API keys can call every endpoint.POST /v1/topupscharges the saved card only for a key with Can buy top-ups switched on; it is off by default. - An OAuth access token starts with
t1o_. MCP clients such as Claude and ChatGPT receive one when you connect them. Its scopes aretranscriptions:readandtranscriptions:write, plusbilling:writeonly when you allow it.
| Scope | Allows |
|---|---|
transcriptions:read | GET /v1/transcriptions, GET /v1/transcriptions/:id, GET /v1/transcriptions/:id/upload and GET /v1/account |
transcriptions:write | POST /v1/transcriptions, DELETE /v1/transcriptions/:id, POST /v1/transcriptions/:id/upload/complete and POST /v1/topups |
billing:write | Lets POST /v1/topups charge the saved card, up to the monthly agent limit. Never granted by default: a client must ask for it, the consent screen shows it unchecked, and you can switch it on or off later with Can buy top-ups on the Connected apps page. Without it, POST /v1/topups returns 402 topup_needs_user. |
- A token without the required scope returns
403 insufficient_scope. - A missing or invalid credential returns
401 unauthorized.
For chunk uploads, authenticate PUT /v1/transcriptions/:id/upload/chunks/:index with X-Upload-Token. You receive this token when you create the upload. See Uploads.
Idempotency
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"}'To retry POST /v1/transcriptions safely, send an Idempotency-Key request header.
The value must be 1 to 255 characters. Send it as a header, not a query parameter.
- The same key and parameters return the original transcription without another charge.
- The same key with different parameters returns
409 idempotency_conflict. - For chunked uploads, repeating the create request with the same key issues a fresh upload token.
Errors
{
"error": {
"code": "insufficient_minutes",
"message": "Not enough minutes",
"retryable": false,
"action_url": "https://transcript1.com/dashboard/billing",
"minutes_needed": 20,
"minutes_remaining": 3
}
}- A request error means the API refused the call.
- A job failure means the API accepted the transcription, then it failed. Its
statusisfailed.
The error envelope
Every /v1 request error contains an error object with code, message and retryable.
- Handle errors by
code. - Display
messageto the user. Its wording may change. - When
retryableistrue, the same request may succeed later.
Some errors add fields next to retryable:
| Field | On which errors | Meaning |
|---|---|---|
action_url | insufficient_minutes, no_active_plan, topup_needs_user | Where to buy a plan or a top-up. |
minutes_needed, minutes_remaining | insufficient_minutes | Minutes the request needs (null when it is not known yet) and minutes you have. |
reason, packs | topup_needs_user | Why the card was not charged, and the packs that were requested. |
supported_platforms | unsupported_platform | The platforms you can send: ["youtube", "x", "tiktok", "instagram", "facebook", "bluesky", "twitch", "dailymotion", "pinterest", "linkedin", "snapchat"]. |
missing_chunks | upload_incomplete | Indexes of the chunks that did not arrive. |
max_bytes | media_too_large | The largest file size allowed for you, in bytes. |
params | invalid_request | For body and query validation errors, an object mapping each field to what is wrong with it. |
Request errors
| HTTP status | code | Retryable | When |
|---|---|---|---|
| 400, 422 | invalid_request | No | A parameter or body field is missing or invalid, for example diarize together with source: "captions", or reuse_recent with webhook_url. Also returned when the request body is not valid JSON. |
| 422 | invalid_url | No | Use a YouTube, X, TikTok, Instagram, Facebook or Bluesky link, or another platform listed in Sources and options. Also returned when platform does not match the link. |
| 422 | unsupported_platform | No | platform is not supported. |
| 422 | unsupported_media | No | The uploaded file does not have a supported extension. |
| 422 | unsupported_translation | No | The translate_to language of a live session is not supported, or live translation is not available. |
| 401 | unauthorized | No | The credential is missing, invalid, expired or revoked, or the upload token is invalid or expired. |
| 403 | insufficient_scope | No | An OAuth token lacks transcriptions:read or transcriptions:write. |
| 403 | email_not_verified | No | Verify your email address before creating transcriptions. |
| 402 | insufficient_minutes | No | Your usable minutes are 0 or fewer than the transcription needs. When auto top-up is on, transcript1 tries to top up first. Buy minutes at action_url, or let an agent buy them with POST /v1/topups or the MCP tool top_up. |
| 402 | no_active_plan | No | You tried to buy a top-up without an active paid plan. POST /v1/topups and the dashboard's billing endpoints return this, not the transcription endpoints. |
| 402 | topup_needs_user | Only for pending and in_progress | POST /v1/topups did not charge the card. reason says why: not_allowed (the credential cannot buy top-ups, or its user is not a team admin), cap_reached (the monthly agent limit has no room), card_declined, authentication_required or no_card. The user can buy the packs at action_url. For pending (the payment is not confirmed yet) and in_progress (another top-up is running), the charge may still complete: retry later with the same Idempotency-Key instead of sending the user to buy. |
| 404 | not_found | No | The transcription does not exist or belongs to another team. |
| 409 | upload_incomplete | No | Some chunks are missing. Send them and complete again. |
| 409 | invalid_state | No | The transcription is not awaiting an upload, so chunks can no longer be sent or completed. |
| 409 | idempotency_conflict | No | The Idempotency-Key was already used with different parameters. |
| 409 | session_ended | No | A live session has ended, so POST /v1/live/sessions/:id/token cannot issue a token. Also sent as a WebSocket error event. |
| 413 | media_too_large | No | The file is larger than your limit. See Uploads. |
| 413 | invalid_request | No | A JSON body over 1 MB. Files use media_too_large instead. |
| 429 | rate_limited | Yes | Too many requests per minute. Wait for Retry-After seconds. |
| 429 | too_many_active_jobs | Yes | Too many transcriptions running, or too many open uploads. Wait for Retry-After seconds. |
| 429 | too_many_live_sessions | Yes | Your plan's simultaneous live sessions are in use. Wait for Retry-After seconds (30). |
| 500 | internal_error | Yes | Something went wrong on our side. Retry shortly. |
| 503 | service_unavailable | Yes | transcript1 is temporarily unavailable. Retry shortly. |
| 503 | live_capacity | Yes | All live capacity is in use. Wait for Retry-After seconds (30) and create the session again. |
| 503 | live_unavailable | No | Live transcription is not available right now. |
Browser requests authenticated with a login cookie also have their origin checked. A request from another site returns 403 forbidden_origin.
API calls with an Authorization header are not affected.
Every 429 response includes a Retry-After header. Its value is the number of seconds to wait. See Billing and limits for the limits.
Retry a request
- Retry only when
retryableistrue:rate_limited,too_many_active_jobs,internal_error,service_unavailable, andtopup_needs_userwithreasonpendingorin_progress. - Follow
Retry-Afterwhen present. Otherwise, increase the wait between attempts. - When retrying
POST /v1/transcriptionsorPOST /v1/topups, send the sameIdempotency-Keyheader. This avoids a second charge if the original request succeeded. - For every other code, fix the request before sending it again.
Job failures
A transcription with status set to failed includes an error object with code and message.
Failures can happen after the request returns. Read error when you poll or receive a transcription.failed webhook. A failed translation sends a transcription.translation.failed webhook and never fails the transcript.
error.code | Meaning | Try again? |
|---|---|---|
media_unavailable | The video does not exist or is no longer available. | No. Check the URL. |
media_private | The video is private. | No. Use a public post or a public or unlisted YouTube video. |
login_required | The platform requires a logged-in account. | No. Permanent failure; not retried. |
no_audio | The video has no sound track. | No. Permanent failure; not retried. |
age_restricted | The video is age-restricted. | No. Upload the file instead. |
live_stream | The video is a live stream, or a stream that has not started yet. | After the stream has ended. |
no_captions | source was captions and the video has no usable caption track. | Use source: "auto" or "stt". |
media_too_long | Speech-to-text accepts up to 5 hours of media. | No. Split the media. |
unsupported_media | The file could not be read or has no audio track. | No. Send a supported file. |
no_speech | No speech was detected. | No. |
unsupported_language | The language of this media is not supported. | No. Set language to a supported code or "auto". |
insufficient_minutes | Your minutes ran out before the transcription could be charged, and auto top-up, if on, could not add more. | After you top up. |
fetch_failed | Fetching the media failed after several attempts. | Yes, later. |
transcription_failed | Speech-to-text failed after several attempts. | Yes, later. |
Failed and canceled transcriptions are never charged. Any minutes already charged are refunded when the transcription fails or is canceled.
The Transcription object
{
"group_id": "tg_4fHq8ZbN2xLm0PqRsTuVwX",
"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."
}
]
}
}Attributes
- group_idstringrequirednullable
Public transcription group ID, or null for legacy ungrouped work.
- idstringrequired
- objectstringrequired
Allowed values
transcription - statusstringrequired
Allowed values
awaiting_uploadqueuedfetchingtranscribingcompletedfailedcanceledlive - sourceobjectrequired
Show child attributesHide child attributes
With
url- platformstringrequired
- urlstringrequirednullable
- media_idstringrequirednullable
With
filename- platformstringrequired
Allowed values
upload - filenamestringrequirednullable
- sizenumberrequirednullable
With
session_id- platformstringrequired
Allowed values
live - session_idstringrequirednullable
- mediaobjectrequirednullable
Show child attributesHide child attributes
- titlestringoptionalnullable
- authorstringoptionalnullable
- author_urlstringoptionalnullable
- thumbnail_urlstringoptionalnullable
- filenamestringoptional
- mime_typestringoptional
- sizenumberoptional
- duration_secondsnumberoptionalnullable
- optionsobjectrequired
Show child attributesHide child attributes
- clipobjectoptionalnullable
Show child attributesHide child attributes
- startnumberrequired
- endnumberrequired
- requested_endnumberoptional
- chunkobjectoptional
Show child attributesHide child attributes
- audio_startnumberrequired
- sourcestringrequired
Allowed values
autocaptionsstt - languagestringrequired
- diarizebooleanrequired
- translate_tostringoptional
- transcript_sourcestringrequirednullable
Allowed values
youtube_captionsx_captionstiktok_captionsbluesky_captionsdailymotion_captionslinkedin_captionsspeech_to_textlive - caption_kindstringrequirednullable
Null until resolved, for speech-to-text, or when native caption metadata does not establish authorship. Unknown does not mean manual.
Allowed values
manualauto_generated - languagestringrequirednullable
- billed_minutesintegerrequirednullable
- cachedbooleanrequired
- errorobjectrequirednullable
Show child attributesHide child attributes
- codestringrequired
- messagestringrequirednullable
- created_atstring · date-timerequirednullable
- completed_atstring · date-timerequirednullable
- deleted_atstring · date-timerequirednullable
When the transcription was deleted. Always
nullunless the request includesinclude_deleted=true. - translationobjectoptionalnullable
The translation this read applies: the requested
translation_language, else thetranslate_tolanguage, else the most recent one, ornull. Included inGETresponses.Show child attributesHide child attributes
- languagestringrequired
- statusstringrequired
Allowed values
processingcompletedfailed - errorstringoptional
- same_as_sourcebooleanoptional
trueon a completed translation into the language the transcript is already in. Nothing was translated: its text is the original text, and text, SRT and WebVTT show each line once.
- translationsarray of objectsoptional
Every translation of the transcription, oldest first, including a pending
translate_tolanguage. Included inGETand list responses.Show child attributesHide child attributes
- languagestringrequired
- statusstringrequired
Allowed values
processingcompletedfailed - errorstringoptional
- same_as_sourcebooleanoptional
trueon a completed translation into the language the transcript is already in. Nothing was translated: its text is the original text, and text, SRT and WebVTT show each line once.
- transcriptobjectoptional
Included in
GETresponses for completed transcriptions.Show child attributesHide child attributes
- textstringrequired
- speakersarray of stringsrequired
- segmentsarray of objectsrequired
Show child attributesHide child attributes
- startnumberrequired
- endnumberrequired
- speakerstringrequirednullable
- languagestringrequirednullable
- textstringrequired
- translationstringoptional
Translated text, present with
translation=bothonce a translation is completed. - wordsarray of objectsoptionalnullable
Show child attributesHide child attributes
- startnumberrequired
- endnumberrequired
- textstringrequired
- confidencenumberoptionalnullable
- speakerstringrequirednullable
Transcriptions
Create, read, list and delete transcriptions of video links and uploaded files.
Create a transcription
POST /v1/transcriptionsTranscribes 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 available on YouTube, X, TikTok, Bluesky, Dailymotion and LinkedIn when a usable native track exists. Dailymotion supports direct SRT, not caption playlists; Bluesky captions require a supported trusted PDS; LinkedIn supports public native posts, not Learning. Other platforms use speech-to-text. Caption kind is null when authorship is unknown. 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"}'{
"group_id": "tg_4fHq8ZbN2xLm0PqRsTuVwX",
"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
}Query
- waitintegeroptionalnullableDefault: 0
Seconds to wait for a final status before responding, up to 60. Defaults to 0.
- reuse_recentstringoptionalDefault: false
For platform URLs only. Cannot be combined with
webhook_url. Returns the team's most recent transcription of the same video with the samesource,languageanddiarize. It must have been created in the last 24 hours and must not befailedorcanceled. Reuses that transcription without creating or charging for a new one.Allowed values
truefalse
Headers
- Idempotency-Keystringoptional
Returns the original transcription when you repeat a request with the same key and parameters.
Body
With url
- platformstringoptional
Optional; detected from the link. If supplied, must match the link.
Allowed values
youtubextiktokinstagramfacebookblueskytwitchdailymotionpinterestlinkedinsnapchat - urlstringrequired
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.
- sourcestringoptional
"auto" uses native captions when available, otherwise speech-to-text. "captions" never falls back to speech-to-text and cannot be combined with diarize or uploads. "stt" always uses speech-to-text; diarize with auto skips captions. Captions are available on YouTube, X, TikTok, Bluesky, Dailymotion and LinkedIn when a usable native track exists. Dailymotion supports direct SRT, not caption playlists; Bluesky captions require a supported trusted PDS; LinkedIn supports public native posts, not Learning. Other platforms use speech-to-text. Caption kind is null when authorship is unknown.
Allowed values
autocaptionsstt - languagestringoptional
- diarizebooleanoptional
- translate_tostringoptional
Two-letter language code. Translates the transcript once it completes. Translation is free.
- webhook_urlstring · urioptional
With upload
- uploadobjectrequired
Show child attributesHide child attributes
- filenamestringrequired
- sizeintegerrequired
- mime_typestringoptional
- sourcestringoptional
Allowed values
autostt - languagestringoptional
- diarizebooleanoptional
- translate_tostringoptional
Two-letter language code. Translates the transcript once it completes. Translation is free.
- webhook_urlstring · urioptional
multipart/form-data
- filestring · binaryrequired
- sourcestringoptional
Allowed values
autostt - languagestringoptional
- diarizebooleanoptional
- translate_tostringoptional
Two-letter language code. Translates the transcript once it completes. Translation is free.
- webhook_urlstring · urioptional
Returns
200 OK— The transcription reached a final status201 Created— The transcription is awaiting a chunked upload.uploaddescribes where to send each chunk.202 Accepted— The transcription is processing
Other statuses
400 Bad Request— The JSON request body is malformed401 Unauthorized— Missing or invalid credential402 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 plan409 Conflict—Idempotency-Keywas reused with different parameters.413 Content Too Large— The upload or the JSON request body is too large422 Unprocessable Content— Invalid request, URL, platform or file type429 Too Many Requests— Too many requests or too many active transcriptions. Wait for the number of seconds inRetry-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"{
"group_id": "tg_4fHq8ZbN2xLm0PqRsTuVwX",
"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."
}
]
}
}Path
- idstringrequired
Query
- waitintegeroptionalnullableDefault: 0
Seconds to wait for a final status before responding, up to 60. Defaults to 0.
- wait_forstringoptionalDefault: translation
What
waitwaits for.translationalso waits for a requested translation to finish, unlesstranslation=none.transcriptreturns as soon as the transcription itself finishes: a completed transcript may then come withtranslation.statusprocessingand no translated text yet; text, SRT and WebVTT then show only the original. Defaults totranslation.Allowed values
translationtranscript - formatstringoptionalDefault: json
Allowed values
jsontextsrtvtt - includestringoptional
Allowed values
words - translationstringoptionalDefault: both
How to include a completed translation.
bothadds atranslationfield to each segment, or a second line in text, SRT and WebVTT.onlyreplaces the text with the translation.noneignores it. Without a completed translation this has no effect. Defaults toboth.Allowed values
bothonlynone - translation_languagestringoptional
Two-letter code of the translation to read. Defaults to the
translate_tolanguage, then the most recently created translation. Unknown languages return404 translation_not_found. - timestampsstringoptionalDefault: false
Allowed values
truefalse - include_deletedstringoptionalDefault: false
Also returns deleted transcriptions as read-only records without a transcript. Their
deleted_atis set. Defaults tofalse.Allowed values
truefalse
Returns
200 OK— The transcription, or the rendered transcript202 Accepted— A rendered format was requested while the transcription is still processing
Other statuses
401 Unauthorized— Missing or invalid credential403 Forbidden— Insufficient scope or a single-member team plan404 Not Found— Transcription not found422 Unprocessable Content— Invalid query parameters429 Too Many Requests— Too many requests. Wait for the number of seconds inRetry-After.
List transcriptions
GET /v1/transcriptionsLists 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": [
{
"group_id": "tg_4fHq8ZbN2xLm0PqRsTuVwX",
"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"
}Query
- limitintegeroptionalDefault: 20
- cursorstringoptional
- statusstringoptional
Allowed values
awaiting_uploadqueuedfetchingtranscribingcompletedfailedcanceledlive - platformstringoptional
- include_deletedstringoptionalDefault: false
Also returns deleted transcriptions as read-only records without a transcript. Their
deleted_atis set. Defaults tofalse.Allowed values
truefalse
Returns
200 OK— A page of transcriptions
Other statuses
401 Unauthorized— Missing or invalid credential403 Forbidden— Insufficient scope or a single-member team plan422 Unprocessable Content— Invalid query parameters or cursor429 Too Many Requests— Too many requests. Wait for the number of seconds inRetry-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.
A live transcription cannot be deleted until its live session has ended; before that the request returns 409 invalid_state.
curl -X DELETE https://api.transcript1.com/v1/transcriptions/tr_4fHq8ZbN2xLm0PqRsTuVwX \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY"No response body
Path
- idstringrequired
Returns
204 No Content— Canceled or deleted
Other statuses
401 Unauthorized— Missing or invalid credential403 Forbidden— Insufficient scope, cross-site request or a single-member team plan404 Not Found— Transcription not found409 Conflict— The transcription belongs to a live session that has not ended429 Too Many Requests— Too many requests. Wait for the number of seconds inRetry-After.
Transcribe a buffered part of a video
POST /v1/transcriptions/chunksUploads up to 8 MiB of audio for a 1–120 second window. Times are absolute media seconds. Payment settles actual completed coverage; admission does not reserve balance.
curl -X POST https://api.transcript1.com/v1/transcriptions/chunks \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY"{
"group_id": "tg_4fHq8ZbN2xLm0PqRsTuVwX",
"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
}Headers
- Idempotency-Keystringoptional
Body
- audiostring · binaryrequired
- urlstringrequired
- keystringoptional
- titlestringoptional
- durationstringoptional
- startstringrequired
- endstringrequired
- audio_startstringrequired
- languagestringoptional
- translate_tostringoptional
Returns
200 OK— Idempotent replay201 Created— Created, queued or completed
Other statuses
402 Payment Required— Insufficient minutes409 Conflict— Idempotency conflict413 Content Too Large— Audio exceeds 8 MiB422 Unprocessable Content— Invalid media or window429 Too Many Requests— Too many requests. Wait for the number of seconds inRetry-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
}Path
- idstringrequired
- indexintegeroptionalnullable
Zero-based chunk number from the upload plan
Headers
- X-Upload-Tokenstringrequired
Body
- (raw body)string · binaryrequired
Returns
200 OK— The chunk was stored
Other statuses
401 Unauthorized— Missing, invalid or expired upload token404 Not Found— Transcription not found409 Conflict— The transcription is no longer awaiting an upload422 Unprocessable Content— Index outside the chunk plan or wrong body size
Get upload progress
GET /v1/transcriptions/{id}/uploadLists 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"
}Path
- idstringrequired
Returns
200 OK— Upload progress
Other statuses
401 Unauthorized— Missing or invalid credential403 Forbidden— Insufficient scope or a single-member team plan404 Not Found— Transcription or upload not found429 Too Many Requests— Too many requests. Wait for the number of seconds inRetry-After.
Complete a chunked upload
POST /v1/transcriptions/{id}/upload/completeChecks 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"{
"group_id": "tg_4fHq8ZbN2xLm0PqRsTuVwX",
"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
}Path
- idstringrequired
Returns
202 Accepted— The transcription is queued
Other statuses
401 Unauthorized— Missing or invalid credential402 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 plan404 Not Found— Transcription or upload not found409 Conflict—upload_incompletemeans chunks are missing.missing_chunkslists them.invalid_statemeans 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 inRetry-After.
Account
Your plan, remaining minutes, rate limits and top-ups.
Get your account
GET /v1/accountReturns the authenticated user, current plan, remaining transcription minutes and top-up packs. topup.can_charge tells whether POST /topups with this credential charges right away.
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
},
"rates": {
"batch": 1,
"live": 1
},
"topup": {
"pack_minutes": 1000,
"pack_price": 600,
"currency": "usd",
"can_charge": true,
"auto": {
"enabled": true,
"threshold_minutes": 100,
"packs": 1,
"paused_reason": null
}
}
}Returns
200 OK— The authenticated user's account
Other statuses
401 Unauthorized— Missing or invalid credential403 Forbidden— Insufficient scope or a single-member team plan404 Not Found— No account found429 Too Many Requests— Too many requests. Wait for the number of seconds inRetry-After.
Buy top-up minutes
POST /v1/topupsCharges the card on file for top-up packs and adds their minutes to the balance. Pack size and price depend on the plan: read topup from GET /account first.
Charging needs an API key with "Can buy top-ups" switched on, or an OAuth token with billing:write, whose user is a team admin. Agent purchases are limited by the monthly agent limit set on the billing page.
When the card is not charged, 402 topup_needs_user gives the reason. For pending and in_progress the charge may still complete: retry later with the same Idempotency-Key. For the other reasons the user can buy the packs at action_url. Repeat a request with the same Idempotency-Key to get the original top-up without paying again.
curl -X POST https://api.transcript1.com/v1/topups \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY" \
-H "Content-Type: application/json" \
-d '{"packs":2}'{
"object": "topup",
"status": "succeeded",
"packs": 2,
"minutes": 2000,
"amount": 1200,
"currency": "usd",
"card": "visa ****4242",
"minutes_remaining": 2150
}Headers
- Idempotency-Keystringoptional
Returns the original top-up when you repeat a request with the same key and packs.
Body
- packsintegeroptionalDefault: 1
Packs to buy.
GET /accountshows the pack size and price.
Returns
201 Created— The card was charged and the minutes were added
Other statuses
400 Bad Request— The JSON request body is malformed401 Unauthorized— Missing or invalid credential402 Payment Required—topup_needs_usergives areason.pending(Stripe has not confirmed the payment yet) andin_progress(another top-up is running) are retryable: the charge may still complete, so do not send the user to buy. Retry later with the sameIdempotency-Key, or checkGET /account. Fornot_allowed(the credential cannot buy top-ups, or its user is not a team admin),cap_reached(the monthly agent limit has no room),card_declined,authentication_requiredandno_card, nothing was charged and the user can buy the packs ataction_url.no_active_planmeans the team is on the Free plan; the user chooses a plan ataction_url.403 Forbidden— Insufficient scope, cross-site request or a single-member team plan409 Conflict—Idempotency-Keywas reused with different packs422 Unprocessable Content— Invalid packs or Idempotency-Key429 Too Many Requests— Too many requests. Wait for the number of seconds inRetry-After.
The Account object
{
"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
},
"rates": {
"batch": 1,
"live": 1
},
"topup": {
"pack_minutes": 1000,
"pack_price": 600,
"currency": "usd",
"can_charge": true,
"auto": {
"enabled": true,
"threshold_minutes": 100,
"packs": 1,
"paused_reason": null
}
}
}Attributes
- objectstringrequired
Allowed values
account - userobjectrequired
Show child attributesHide child attributes
- idstringrequired
- emailstringrequired
- planobjectrequirednullable
Show child attributesHide child attributes
- idstringrequired
- namestringrequired
- minutesobjectrequired
Show child attributesHide child attributes
- remainingnumberrequired
- cyclenumberrequired
- topupnumberrequired
- topup_usablebooleanrequired
- resets_atstring · date-timerequirednullable
- renews_atstring · date-timerequirednullable
- limitsobjectrequired
Show child attributesHide child attributes
- requests_per_minutenumberrequired
- max_active_jobsnumberrequired
- ratesobjectrequired
Show child attributesHide child attributes
- batchnumberrequired
- livenumberrequired
- topupobjectrequirednullable
Top-up packs on the current paid plan.
nullwithout a paid plan.Show child attributesHide child attributes
- pack_minutesintegerrequired
Minutes in one top-up pack
- pack_priceintegerrequired
Price of one pack in the smallest currency unit (cents)
- currencystringrequired
Lowercase ISO currency code
- can_chargebooleanrequired
Whether
POST /topupswith this credential charges the card on file right away: the credential may buy top-ups, its user is a team admin, a card is on file and the monthly agent limit has room for one pack. - autoobjectrequired
Show child attributesHide child attributes
- enabledbooleanrequired
- threshold_minutesintegerrequired
Auto top-up buys packs when the balance drops below this
- packsintegerrequired
Packs each auto top-up buys
- paused_reasonstringrequirednullable
Why auto top-up is paused,
nullwhile it is not. The user resumes it on the billing page.Allowed values
card_declinedauthentication_requiredno_card
Live
Transcribe audio as it happens. Create a session, then stream PCM audio over a WebSocket with the single-use token and read subtitle lines back.
Create a live session
POST /v1/live/sessionsStarts a live transcription session and returns a single-use WebSocket token. Connect with new WebSocket(websocket_url, ["t1-live", token]) within 60 seconds, then send PCM audio.
Live sessions are billed by connected time: each started minute costs rate balance minutes. Creating a session needs enough balance for the first minute. Read GET /live/capabilities for the languages, translation targets and limits of the active provider.
curl -X POST https://api.transcript1.com/v1/live/sessions \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY" \
-H "Content-Type: application/json" \
-d '{"language":"auto","translate_to":"es","diarize":false,"audio_format":"pcm_s16le","sample_rate":16000,"save":true}'{
"group_id": "tg_4fHq8ZbN2xLm0PqRsTuVwX",
"id": "lv_7Kd2mQp9XvTn4RbWc1HsYe",
"status": "waiting",
"transcription_id": "tr_4fHq8ZbN2xLm0PqRsTuVwX",
"websocket_url": "wss://api.transcript1.com/v1/live/sessions/lv_7Kd2mQp9XvTn4RbWc1HsYe/stream",
"token": "Q2xhdWRlLXRva2VuLWV4YW1wbGUtMzItYnl0ZXMtYmFzZTY0dXJs",
"token_expires_at": "2026-11-01T12:01:00.000Z",
"rate": 1
}Body
- languagestringoptionalDefault: auto
autoor a two-letter code the active provider supports. SeeGET /live/capabilities. - translate_tostringoptional
Two-letter code. Must be a valid target (and pair) for the active provider.
- diarizebooleanoptionalDefault: false
Label speakers. Rejected where the provider cannot diarize in that mode.
- audio_formatstringoptionalDefault: pcm_s16le
Mono signed 16-bit little-endian PCM. The only supported format.
Allowed values
pcm_s16le - sample_rateintegeroptionalDefault: 16000
Sample rate of the audio you send, in Hz.
- savebooleanoptionalDefault: true
Save the session as a transcription.
falsekeeps no transcript after the session ends.
Returns
201 Created— The session was created. Connect with the token before it expires.
Other statuses
400 Bad Request— The JSON request body is malformed401 Unauthorized— Missing or invalid credential402 Payment Required— The usable balance is below the first minute.403 Forbidden— Insufficient scope, unverified email, cross-site request or a single-member team plan422 Unprocessable Content— Invalid request, language or translation429 Too Many Requests— Too many requests or too many live sessions. Wait for the number of seconds inRetry-After.503 Service Unavailable— Live transcription is at capacity (live_capacity, retryable) or not configured (live_unavailable, not retryable).Retry-Afteris sent forlive_capacityonly.
Get a new WebSocket token
POST /v1/live/sessions/{id}/tokenReturns a new single-use token for a new connection: to resume after a dropped connection, or to take over from a socket the server still thinks is open. Fails with 409 session_ended once the session has ended.
curl -X POST https://api.transcript1.com/v1/live/sessions/lv_7Kd2mQp9XvTn4RbWc1HsYe/token \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY"{
"token": "Q2xhdWRlLXRva2VuLWV4YW1wbGUtMzItYnl0ZXMtYmFzZTY0dXJs",
"token_expires_at": "2026-11-01T12:01:00.000Z",
"websocket_url": "wss://api.transcript1.com/v1/live/sessions/lv_7Kd2mQp9XvTn4RbWc1HsYe/stream"
}Path
- idstringrequired
Returns
200 OK— A new token for the session
Other statuses
401 Unauthorized— Missing or invalid credential403 Forbidden— Insufficient scope, cross-site request or a single-member team plan404 Not Found— Live session not found409 Conflict— The session has ended429 Too Many Requests— Too many requests. Wait for the number of seconds inRetry-After.
Get a live session
GET /v1/live/sessions/{id}Returns the status, options, connected time and minutes charged of a live session of your team.
curl https://api.transcript1.com/v1/live/sessions/lv_7Kd2mQp9XvTn4RbWc1HsYe \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY"{
"id": "lv_7Kd2mQp9XvTn4RbWc1HsYe",
"status": "connected",
"end_reason": null,
"transcription_id": "tr_4fHq8ZbN2xLm0PqRsTuVwX",
"options": {
"language": "auto",
"translate_to": "es",
"diarize": false,
"audio_format": "pcm_s16le",
"sample_rate": 16000,
"save": true
},
"connected_ms": 93400,
"audio_ms": 91000,
"minutes_charged": 2,
"rate": 1,
"created_at": "2026-11-01T12:00:00.000Z",
"ended_at": null
}Path
- idstringrequired
Returns
200 OK— The live session
Other statuses
401 Unauthorized— Missing or invalid credential403 Forbidden— Insufficient scope or a single-member team plan404 Not Found— Live session not found429 Too Many Requests— Too many requests. Wait for the number of seconds inRetry-After.
Get live capabilities
GET /v1/live/capabilitiesDescribes the active live provider: supported languages, translation targets, diarization, audio format, sample rates and the rate. Returns { "available": false, "rate": 1 } when live transcription is not configured.
curl https://api.transcript1.com/v1/live/capabilities \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY"{
"available": true,
"auto_language": true,
"languages": [
"en",
"es",
"de",
"fr"
],
"translation": {
"targets": [
"en",
"es",
"de",
"fr"
],
"pairs": "any",
"requires_language": false
},
"diarize": true,
"audio_formats": [
"pcm_s16le"
],
"sample_rate": {
"min": 8000,
"max": 48000
},
"max_session_minutes": 300,
"rate": 1
}Returns
200 OK— The active provider's capabilities
Other statuses
401 Unauthorized— Missing or invalid credential403 Forbidden— Insufficient scope or a single-member team plan429 Too Many Requests— Too many requests. Wait for the number of seconds inRetry-After.
The LiveSession object
{
"id": "lv_7Kd2mQp9XvTn4RbWc1HsYe",
"status": "connected",
"end_reason": null,
"transcription_id": "tr_4fHq8ZbN2xLm0PqRsTuVwX",
"options": {
"language": "auto",
"translate_to": "es",
"diarize": false,
"audio_format": "pcm_s16le",
"sample_rate": 16000,
"save": true
},
"connected_ms": 93400,
"audio_ms": 91000,
"minutes_charged": 2,
"rate": 1,
"created_at": "2026-11-01T12:00:00.000Z",
"ended_at": null
}Attributes
- idstringrequired
- statusstringrequired
Allowed values
waitingconnecteddisconnectedended - end_reasonstringrequirednullable
Allowed values
client_endedbalance_exhaustedidle_timeoutmax_durationdisconnectedupstream_errorinternal_errornot_connected - transcription_idstringrequirednullable
- optionsobjectrequired
Show child attributesHide child attributes
- languagestringrequired
- translate_tostringrequirednullable
- diarizebooleanrequired
- audio_formatstringrequired
Allowed values
pcm_s16le - sample_rateintegerrequired
- savebooleanrequired
- connected_msintegerrequired
- audio_msintegerrequired
- minutes_chargedintegerrequired
Balance minutes charged so far, after the rate.
- ratenumberrequired
Balance minutes charged per started minute of the session.
- created_atstring · date-timerequirednullable
- ended_atstring · date-timerequirednullable
The LiveCapabilities object
Groups
Group transcriptions by media, read merged coverage, and manage their lifecycle.
List transcription groups
GET /v1/groupsReturns team-scoped groups, newest update first, using cursor pagination.
curl https://api.transcript1.com/v1/groups \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY"{
"data": [
{
"id": "tg_4fHq8ZbN2xLm0PqRsTuVwX",
"title": "Example video",
"url": "https://youtu.be/dQw4w9WgXcQ",
"platform": "youtube",
"media_id": "dQw4w9WgXcQ",
"duration_seconds": 600,
"covered": [
[
0,
60
]
],
"covered_seconds": 60,
"sources": [
"download"
],
"languages": [
"en"
],
"translations": [],
"members_count": 1,
"active": false,
"billed_minutes": 1,
"created_at": "2026-11-08T10:00:00.000Z",
"updated_at": "2026-11-08T10:00:00.000Z"
}
],
"next_cursor": null
}Query
- limitintegeroptionalDefault: 20
- cursorstringoptional
Returns
200 OK— Successful response
Other statuses
429 Too Many Requests— Too many requests. Wait for the number of seconds inRetry-After.
Find a group by media URL
GET /v1/groups/lookupResolves the URL and optional page key within your team.
curl "https://api.transcript1.com/v1/groups/lookup?url=%3Curl%3E" \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY"{
"id": "tg_4fHq8ZbN2xLm0PqRsTuVwX",
"title": "Example video",
"url": "https://youtu.be/dQw4w9WgXcQ",
"platform": "youtube",
"media_id": "dQw4w9WgXcQ",
"duration_seconds": 600,
"covered": [
[
0,
60
]
],
"covered_seconds": 60,
"sources": [
"download"
],
"languages": [
"en"
],
"translations": [],
"members_count": 1,
"active": false,
"billed_minutes": 1,
"created_at": "2026-11-08T10:00:00.000Z",
"updated_at": "2026-11-08T10:00:00.000Z"
}Query
- urlstringrequired
- keystringoptional
Returns
200 OK— Successful response
Other statuses
429 Too Many Requests— Too many requests. Wait for the number of seconds inRetry-After.
Read a group and up to 500 members
GET /v1/groups/{id}Returns metadata and the newest members with a truncation flag.
curl https://api.transcript1.com/v1/groups/tg_4fHq8ZbN2xLm0PqRsTuVwX \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY"{
"id": "tg_4fHq8ZbN2xLm0PqRsTuVwX",
"title": "Example video",
"url": "https://youtu.be/dQw4w9WgXcQ",
"platform": "youtube",
"media_id": "dQw4w9WgXcQ",
"duration_seconds": 600,
"covered": [
[
0,
60
]
],
"covered_seconds": 60,
"sources": [
"download"
],
"languages": [
"en"
],
"translations": [],
"members_count": 1,
"active": false,
"billed_minutes": 1,
"created_at": "2026-11-08T10:00:00.000Z",
"updated_at": "2026-11-08T10:00:00.000Z",
"members": [],
"members_truncated": false
}Path
- idstringrequired
Returns
200 OK— Successful response
Other statuses
429 Too Many Requests— Too many requests. Wait for the number of seconds inRetry-After.
Cancel active work and delete a group
DELETE /v1/groups/{id}Ends live sessions, cancels active batch work and soft-deletes members. Prior paid coverage is retained.
curl -X DELETE https://api.transcript1.com/v1/groups/tg_4fHq8ZbN2xLm0PqRsTuVwX \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY"No response body
Path
- idstringrequired
Returns
204 No Content— Deleted; settled payments and allocations are retained
Other statuses
409 Conflict— New active work joined during deletion; retry429 Too Many Requests— Too many requests. Wait for the number of seconds inRetry-After.
Read the merged transcript
GET /v1/groups/{id}/transcriptMerges completed members at absolute media timestamps, optionally restricted to a time window.
curl https://api.transcript1.com/v1/groups/tg_4fHq8ZbN2xLm0PqRsTuVwX/transcript \
-H "Authorization: Bearer $TRANSCRIPT1_API_KEY"{
"group_id": "tg_4fHq8ZbN2xLm0PqRsTuVwX",
"language": null,
"covered": [
[
0,
60
]
],
"segments": []
}Path
- idstringrequired
Query
- fromnumberoptionalnullable
- tonumberoptional
- translation_languagestringoptional
- formatstringoptionalDefault: json
Allowed values
jsontextsrtvtt
Returns
200 OK— Merged transcript in the requested format
Other statuses
429 Too Many Requests— Too many requests. Wait for the number of seconds inRetry-After.
Webhooks
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));
};{
"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
}
}Pass webhook_url when you create a transcription. transcript1 sends a signed POST to that URL when the transcription completes or fails.
Webhooks are part of the REST API only. The MCP server has no webhook parameter.
Set a webhook URL
Send webhook_url with POST /v1/transcriptions:
- For a supported video link or chunked upload reservation, add it to the JSON body.
- For a one-shot upload, use a form field.
The URL must:
- Use HTTPS.
- Contain no user name or password.
- Resolve to a public address.
Private, loopback, link-local and other non-public addresses return 422 invalid_request. The address is checked when you create the transcription and at every delivery.
webhook_url cannot be combined with reuse_recent=true.
Events
type | Sent when |
|---|---|
transcription.completed | The transcription reached completed. |
transcription.failed | The transcription reached failed. data.error has the code and message. |
transcription.translation.completed | A requested translation reached completed. |
transcription.translation.failed | A requested translation reached failed. |
A canceled transcription sends no webhook. For the two translation events, data also has translation: { language, status, error }. The error is set only when the translation failed. same_as_source: true marks a translation into the language the transcript is already in: nothing was translated, and the event is sent at once. transcription.completed still fires when the transcript is done, and translation events are sent separately when the translation finishes. Deleted transcriptions send no translation events.
Payload
The request body is JSON:
created_atis when the event was created.datais the transcription object withouttranscript.
To read the transcript, call GET /v1/transcriptions/:id with the id from data.
The request includes these headers:
Content-Type: application/jsonUser-Agent: transcript1-webhooks/1.0X-Transcript1-Signature, described below.
Verify the signature
Every delivery carries the header X-Transcript1-Signature with the value t=<unix seconds>,v1=<hex digest>.
tis the Unix time in seconds when the request was signed.v1is the hex-encoded HMAC-SHA256 of"<t>.<raw body>". This string contains the value oft, a dot and the request body exactly as received. Use your full signing secret as the key, including thewhsec_prefix.
Verify the raw body bytes before parsing JSON. Parsing and serializing can change whitespace or key order, invalidating the signature.
- Compare signatures in constant time.
- Reject requests whose
tis too far from the current time. The examples allow 5 minutes. - With Express, read the body as raw text so it is not parsed first.
- With Flask, pass
request.get_data(as_text=True)asraw_body.
Your signing secret
Your team's signing secret starts with whsec_. It signs webhooks for every transcription the team creates.
Reveal or rotate it on the Webhooks page.
Rotation replaces the secret immediately. All webhooks sent afterwards use the new secret.
Delivery rules
- Any
2xxresponse counts as delivered. - Any other status code, connection error or no response within the 10-second timeout counts as a failed attempt.
- Failed deliveries are retried with exponential backoff, up to 6 attempts in total. The waits are 2, 4, 8, 16 and 32 minutes. The last attempt is about an hour after the first.
- Use the final URL. transcript1 does not follow redirects, and a
3xxresponse counts as a failed attempt. - Deliveries can arrive more than once. This can happen if your server responds after the timeout. Use
data.idandtypeto ignore duplicates.