API Reference

API Reference

Send a YouTube URL, get a transcript. Authenticated with a bearer API key.

Prefer to call this from Claude, Cursor, Cline, or Windsurf? Use the MCP server — same key, same tools, no HTTP plumbing.
On Claude Code? Install the Agent Skill so the assistant calls this API with your key: /plugin marketplace add dorik/youtube-transcript-skill then /plugin install youtube-transcript@dorik. Or copy the skills/youtube-transcript/ folder from the GitHub repo into your .claude/skills/.

Authentication

Every request must include your API key in the Authorization header using the Bearer scheme:

Authorization: Bearer yt_live_YOUR_KEY

Create an API key from the dashboard. Keys are shown once; store them securely.

POST /v1/transcript

Enqueues a transcript request for a YouTube video. The API is asynchronous: this call always returns a queued request (HTTP 202), and you poll GET /v1/transcript/:id until its status is completed (or failed). Requests already in our cache are resolved by the worker almost immediately, so polling typically completes on the first or second try.

Request body (JSON)

ParameterTypeRequiredDescription
urlstringrequiredAny standard YouTube URL or a bare 11-character video id.
formatenumoptionaljson (default), text, text-timestamps, srt, vtt.
languagestringoptionalPreferred caption language — an ISO-639 code (en, es, fr…) or "auto".
native_onlybooleanoptionalWhen true, skip the Whisper fallback and fail if no native captions exist.
translate_tostringoptionalTarget language for translation — an ISO-639 code, or "none" to skip.
include_summarybooleanoptionalWhen true, attach an AI summary (TL;DR + key points) to result.summary. Free — adds no credits.

Example

# 1. Enqueue a transcript request curl -X POST 'https://api.youtubetranscripts.co/v1/transcript' \ -H 'Authorization: Bearer yt_live_YOUR_KEY' \ -H 'Content-Type: application/json' \ -d '{"url":"https://youtu.be/dQw4w9WgXcQ","format":"json"}' # Returns 202 with a queued request. Poll until status is "completed": curl 'https://api.youtubetranscripts.co/v1/transcript/REQUEST_ID' \ -H 'Authorization: Bearer yt_live_YOUR_KEY'

Example response (a completed request)

{ "id": "a1b2c3d4-5e6f-7890-abcd-ef1234567890", "user_id": "21bb5891-f61b-40c0-b81a-28f90dece908", "source": "api", "status": "completed", "request": { "url": "https://youtu.be/dQw4w9WgXcQ", "format": "json" }, "video_id": "dQw4w9WgXcQ", "title": "Rick Astley - Never Gonna Give You Up (Official Video) (4K Remaster)", "channel": "Rick Astley", "duration_seconds": 212, "thumbnail_url": "https://img.youtube.com/vi/dQw4w9WgXcQ/mqdefault.jpg", "bullmq_job_id": "1843", "attempts": 1, "result": { "video_id": "dQw4w9WgXcQ", "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "title": "Rick Astley - Never Gonna Give You Up (Official Video) (4K Remaster)", "channel": "Rick Astley", "duration": 212, "language": "en", "original_language": "en", "translated_to": null, "source": "native_captions", "format": "json", "transcript": "[♪♪♪] ♪ We're no strangers to love ♪ ...", "segments": [ { "start": 1.36, "duration": 1.68, "text": "[♪♪♪]" } ], "credits_used": 1, "credits_remaining": 99, "cached": false, "fetched_at": "2026-05-09T08:14:07.359Z" }, "credits_used": 1, "error_code": null, "error_message": null, "batch_id": null, "batch_position": null, "created_at": "2026-05-09T08:14:05.001Z", "started_at": "2026-05-09T08:14:05.123Z", "completed_at": "2026-05-09T08:14:07.412Z" }

While the worker runs, status is queued or processing and result is null. Once completed, result holds the transcript — including original_language and translated_to (non-null only when a translation was applied). When a translation is applied, JSON responses also include original_transcript and original_segments (the pre-translation text), and result.summary is present when include_summary was set.

Response headers

  • X-RateLimit-Limit / -Remaining / -Reset: current rate-limit budget.

AI summary

Add "include_summary": true to any POST /v1/transcript request to get an AI-generated summary alongside the transcript. The summary is written in the transcript's display language (so it respects translate_to) and lands on result.summary:

"summary": { "tldr": "A 2-3 sentence plain-language summary of the whole video.", "key_points": [ "First key point in one sentence.", "Second key point …5-8 total." ], "model": "gpt-4o-mini", "generated_at": "2026-06-07T10:00:00.000Z" }
  • tldr — a 2–3 sentence overview of the whole video.
  • key_points — 5–8 one-sentence bullet points.
  • model — the model that produced the summary.
  • generated_at — ISO timestamp.

Summaries are free — only the transcript's normal credit applies (1 native / per-minute Whisper). They are cached, so re-summarizing the same video and language is instant and costs nothing. If summary generation fails, the transcript is still returned with summary: null — a summary failure never fails the transcript.

POST /v1/summarize

A convenience wrapper that always sets include_summary and format: json. Same async contract as /v1/transcript: it returns a queued request you poll at GET /v1/transcript/:id.

ParameterTypeRequiredDescription
urlstringrequiredAny standard YouTube URL or a bare 11-character video id.
languagestringoptionalPreferred caption language — an ISO-639 code (en, es, fr…) or "auto".
translate_tostringoptionalSummarize in this language — an ISO-639 code, or "none" to skip translation.

Browse endpoints

The same bearer key unlocks lightweight YouTube discovery endpoints and the bulk-transcript queue. Discovery (list-only) endpoints charge a flat one credit per call — zero if the call returns no results. /v1/channel/latest returns the single most-recent upload and is free. The bulk endpoint charges one credit per transcript delivered (cache hits charge the same as a fresh fetch). Single-video /v1/video/metadata is free.

ParameterTypeRequiredDescription
GET /v1/searchq, type, limitrequiredSearch YouTube videos, channels, or playlists.
GET /v1/channel/searchchannel, q, limitrequiredSearch videos inside a channel URL, ID, or @handle.
GET /v1/channel/videoschannel, limitrequiredList videos from a channel.
GET /v1/channel/latestchannelrequiredReturn the single most-recent upload on a channel. Free — no credits charged.
GET /v1/playlist/videosplaylist, limitrequiredExpand a YouTube playlist into video records.
GET /v1/video/metadataurl or video_idrequiredReturn title, channel, duration, view count, and thumbnail for one video. Free — no credits charged.
POST /v1/transcripts/bulkplaylist | channel | urls, format, language, native_only, translate_to, limitrequiredEnqueue a batch of transcripts from a playlist, channel, or URL list. One credit per transcript delivered.
GET /v1/transcripts/batches/:id(batch id in path)requiredPoll a bulk batch — its progress counts and per-video request rows.

GET /v1/search accepts a type of video (the default), channel, playlist, or all. limit defaults to 10. The cap varies by endpoint: /v1/search allows up to 50; the channel and playlist listing endpoints allow up to 200; the bulk endpoint caps at 100. Values above the cap are rejected with a VALIDATION_ERROR.

On /v1/search and /v1/channel/search, q is the canonical search-query parameter, and query is accepted as an alias. Send one or the other — if both are present, q takes precedence.

Output formats

Pass format in the request body to control how the transcript is rendered. Every response is the JSON envelope shown above (Content-Type: application/json) — format changes the string inside result.transcript, not the response type. Write that string to a file yourself if you need a standalone subtitle file.

  • jsonresult.transcript is plain text; a structured segments array is included alongside it.
  • text — plain text only, no timestamps.
  • text-timestamps — plain text prefixed with [mm:ss] per line.
  • srtresult.transcript holds SubRip-formatted subtitles.
  • vttresult.transcript holds WebVTT-formatted subtitles.

Credits

One credit per native YouTube transcript regardless of length. Whisper fallback costs one credit per minute of audio (rounded up). Cached requests are free.

Your remaining balance is in credits_remaining on every JSON response.

Error codes

CodeHTTPMeaning
MISSING_API_KEY401Authorization header is missing.
INVALID_API_KEY401Bearer token is malformed, revoked, or unknown.
INVALID_AUTH_SCHEME401Header is present but not the Bearer scheme.
VALIDATION_ERROR400Request body or query parameters failed schema validation.
METHOD_NOT_ALLOWED405The path exists but not for that HTTP method (see the Allow header).
INSUFFICIENT_CREDITS402Account is out of credits for this billing cycle.
NO_TRANSCRIPTVideo has no captions and the Whisper fallback also produced nothing. Delivered as the error_code on a failed transcript request (HTTP 200), not as an HTTP error.
VIDEO_NOT_FOUND404Video does not exist or is private/removed.
CHANNEL_NOT_FOUND404Channel URL, ID, or @handle could not be resolved.
PLAYLIST_NOT_FOUND404Playlist does not exist or is private.
UPGRADE_REQUIRED402This feature (AI fallback, batch, or MCP) requires a higher plan tier.
NOT_FOUND404The transcript request or batch id does not exist (or is not yours).
ROUTE_NOT_FOUND404The URL path does not match any API endpoint.
RATE_LIMIT_EXCEEDED429You exceeded your per-minute request limit.
UPSTREAM_BLOCKED503YouTube is temporarily blocking our servers. Retry shortly.

A code with no HTTP status () is not an HTTP error: the request returns 200, then its status becomes failed and error_code holds the code.

All errors share the envelope { error, code, message, request_id, ...details }. Quote the request_id when reporting a failure so it can be traced in the logs.