transcript.imDocs
DocumentationAPI Reference

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:

  1. Treat 200 as a completed transcript and 202 as accepted work.
  2. Wait before the first poll, then use bounded exponential backoff.
  3. Stop when the job reports success or failed.
  4. 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 file part 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 202 job (or a 200 if it finishes inside waitMs).

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: true means caption text was served. false means the extractor confirmed the video has no caption track. null means unproven — a requested-language miss, an upstream fault, or metadata that could not be trusted.
  • requiresAsync: true means speech recognition is required. It is set for an asr-only request, for a confirmed absence of caption tracks (with captionAvailable: false), and for a confirmed miss of every requested caption language (with captionAvailable: null). It is never inferred from an upstream fault or the wait budget.
  • trackMetadataStatus reports how trustworthy the Google track listing is: complete, unavailable (no client or the 2s metadata budget elapsed), or error; trackMetadataError / trackMetadataErrorCode carry 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" with captionAvailable: null and does not invent a job id. To actually run speech recognition, call POST /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.

On this page