Transcript workflows
Handle language selection, asynchronous transcription, batches, and cursor pagination.
Inspect before extracting
Use GET /v1/transcript/info?url=... to resolve a source, read its metadata, and see which languages can be served. This is useful when you want to show a language picker or decide whether speech recognition is necessary before extraction.
Language priority
The language field on POST /v1/transcript is a comma-separated priority list:
{
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"language": "de,en,asr"
}Entries may be BCP 47 language tags or asr / asr-<code> selectors. The API chooses the first source it can serve. Reaching an ASR selector starts an asynchronous speech-to-text job. When caption tracks exist only in other languages, the request also continues to ASR instead of failing; a 404 is reserved for a genuinely missing asset or an unavailable language when ASR is not permitted.
An asr / asr-<code> request always runs speech recognition — it never reuses a previously cached or captioned transcript, and it is billed as ASR minutes. Existing caption transcripts are never overwritten by a lower-quality ASR result: if this run's output would land on the same (platform, video, language) identity as an existing non-ASR transcript, it is stored under a distinct address instead — language in the response comes back as <lang>-x-asr (a valid BCP 47 private-use tag) rather than plain <lang>, and transcript.source is "asr". This address behaves like any other language value in Library, batch item status, and exports; it never collides with a language you supply, since -x- private-use tags are rejected by request validation. When there is no colliding transcript, the ASR result is stored under the plain detected language as usual.
Output formats
Set format to json for structured segments or text for readable plain text. With JSON, includeTimestamp: false removes start and duration from segments. With text, it removes the timestamp prefix from each line.
Use sendMetadata: true when the result also needs title, creator, thumbnail, publication time, or duration information.
Asynchronous jobs
Caption-less media never holds the extraction request open. A 202 response returns a jobId and a stage (for example asr_queued); poll GET /v1/transcript/job/{id} until it reaches a terminal state.
Recommended client behavior:
- Treat
200as a completed transcript and202as accepted work. - Wait before the first poll, then use bounded exponential backoff.
- Stop when the job reports
successorfailed. - Preserve the structured terminal error for logging and user feedback.
Instead of polling, you can subscribe to GET /v1/transcript/job/{id}/events (Server-Sent Events). It always sends a snapshot first — so a job that finished before you subscribed is not missed — then stage updates and a terminal completed or failed. Reconnect on a dropped stream; polling remains a valid fallback.
A guest that submitted anonymously can poll its own job with the same guest credential: send the tgs cookie (browsers) or the x-guest-token header (non-cookie clients) to GET /v1/transcript/job/{id} or its /events stream. Without an account credential or a guest token those reads return 401.
Uploading local media
POST /v1/transcript also accepts multipart/form-data as an alternative to a URL, for audio or video you already have:
curl -X POST 'https://api.transcript.im/v1/transcript' \
-H 'Authorization: Bearer tr_your_api_key' \
-F 'file=@./sample.mp3;type=audio/mpeg' \
-F 'language=en' \
-F 'format=json' \
-F 'waitMs=5000'- The body carries exactly one
filepart plus the same options as the JSON request (language,format,includeTimestamp,sendMetadata,waitMs). - Uploads require an account credential (a session or a paid API key); there is no guest upload path, and a credential-less upload returns
401 SIGN_IN_REQUIRED. - The size cap is per plan: 500 MiB on Free, 5 GiB on Pro. Exceeding it returns
413 MEDIA_TOO_LARGE, and the server aborts the stream without leaving a partial object. - The declared media type is verified against the file's magic bytes; a mismatch, a missing file part, or an interrupted body returns
400 UPLOAD_INVALID. - An uploaded file always goes to speech recognition — captions are not attempted — so the response is a
202job (or a200if it finishes insidewaitMs).
Batch jobs
POST /v1/batch selects its mode from the request body. There is no mode field.
{ "urls": ["https://youtu.be/…", "https://www.tiktok.com/@creator/video/…"] }{ "playlistId": "PL…" }{ "channelId": "@TED" }A playlist or channel may also be supplied as a full URL with playlist or channel. The create call returns 202 with the batch id and counts.
Read status with GET /v1/batch/{batchId}. Items use cursor pagination: pass the returned nextPageToken as pageToken, and choose a maxResults value when you need a different page size.
Finished batches can be exported as txt, csv, json, srt, vtt, or zip. Export requires a paid plan; the file is returned directly in the response, not as a redirect to a separate download URL.
YouTube discovery
The /v1/youtube/* endpoints use familiar YouTube resource names and cursor fields such as q, id, videoId, channelId, playlistId, pageToken, and nextPageToken. Use them to find sources, enumerate a channel or playlist, then pass selected URLs into transcript or batch requests.
Pagination scope: search, channels/{channelId}/videos, and playlists/{playlistId}/items accept pageToken and return nextPageToken. videos and channels do not paginate — videos accepts pageToken for schema compatibility but ignores it, so do not rely on it for a second page.
Caption metadata and text
GET /v1/youtube/captions?videoId=… returns both the best-effort YouTube track metadata and the transcript text served by the same caption service as POST /v1/transcript. It requires the transcripts scope and never creates an ASR job. Read the fields carefully:
captionAvailable: truemeans caption text was served.falsemeans the extractor confirmed the video has no caption track.nullmeans unproven — a requested-language miss, an upstream fault, or metadata that could not be trusted.requiresAsync: truemeans speech recognition is required. It is set for anasr-only request, for a confirmed absence of caption tracks (withcaptionAvailable: false), and for a confirmed miss of every requested caption language (withcaptionAvailable: null). It is never inferred from an upstream fault or the wait budget.trackMetadataStatusreports how trustworthy the Google track listing is:complete,unavailable(no client or the 2s metadata budget elapsed), orerror;trackMetadataError/trackMetadataErrorCodecarry the reason.- The endpoint waits only a short budget (about 2 seconds) for the transcript. If it is still running, the response is
status: "unavailable"withcaptionAvailable: nulland does not invent a job id. To actually run speech recognition, callPOST /v1/transcript.
Saved content
/v1/transcript submits extraction requests; /v1/transcript/info reads source metadata, and /v1/transcript/job/{id} tracks extraction jobs.
Use GET /v1/library/transcripts to page through saved account content with server-side search, platform and language filters, and sorting. Each item is a transcript, a batch record, or a failed history row — a terminal failure with no transcript body, carrying the source identity and an error code such as VIDEO_TOO_LONG. Failure history comes only from your own durable jobs, adds no billing, and disappears once the same asset succeeds. GET /v1/library/transcripts/{platform}/{externalId} reads one saved item; its /download and /related routes export saved content and list related saved records. DELETE /v1/library/transcripts/{platform}/{externalId} removes saved content or a failed-history row permanently, so a dismissed failure never reappears. Reads require ownership, including when the underlying transcript exists in the shared cache. Without language, the API selects your most recently saved language.
Batch creation, status, retry, and export remain under /v1/batch. Deleting a saved batch uses DELETE /v1/library/batches/{batchId}. The previous content-read and content-delete paths under /v1/transcript, and DELETE /v1/batch/{batchId}, are removed.
Library lists include queued or processing submissions (kind: pending, jobStatus), failures (kind: failed, errorCode), saved transcripts, and collections. Resume a submission through its jobId; reading history does not submit it again. A collection with status: completed and failedCount > 0 finished with errors. Removing a Library entry preserves its job and billing records while hiding it from the account.