API Reference
Send a YouTube URL, get a transcript. Authenticated with a bearer API 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:
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)
| Parameter | Type | Required | Description |
|---|---|---|---|
| url | string | required | Any standard YouTube URL or a bare 11-character video id. |
| format | enum | optional | json (default), text, text-timestamps, srt, vtt. |
| language | string | optional | Preferred caption language — an ISO-639 code (en, es, fr…) or "auto". |
| native_only | boolean | optional | When true, skip the Whisper fallback and fail if no native captions exist. |
| translate_to | string | optional | Target language for translation — an ISO-639 code, or "none" to skip. |
| include_summary | boolean | optional | When true, attach an AI summary (TL;DR + key points) to result.summary. Free — adds no credits. |
Example
Example response (a completed request)
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:
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.
| Parameter | Type | Required | Description |
|---|---|---|---|
| url | string | required | Any standard YouTube URL or a bare 11-character video id. |
| language | string | optional | Preferred caption language — an ISO-639 code (en, es, fr…) or "auto". |
| translate_to | string | optional | Summarize 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.
| Parameter | Type | Required | Description |
|---|---|---|---|
| GET /v1/search | q, type, limit | required | Search YouTube videos, channels, or playlists. |
| GET /v1/channel/search | channel, q, limit | required | Search videos inside a channel URL, ID, or @handle. |
| GET /v1/channel/videos | channel, limit | required | List videos from a channel. |
| GET /v1/channel/latest | channel | required | Return the single most-recent upload on a channel. Free — no credits charged. |
| GET /v1/playlist/videos | playlist, limit | required | Expand a YouTube playlist into video records. |
| GET /v1/video/metadata | url or video_id | required | Return title, channel, duration, view count, and thumbnail for one video. Free — no credits charged. |
| POST /v1/transcripts/bulk | playlist | channel | urls, format, language, native_only, translate_to, limit | required | Enqueue 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) | required | Poll 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.
json—result.transcriptis plain text; a structuredsegmentsarray is included alongside it.text— plain text only, no timestamps.text-timestamps— plain text prefixed with[mm:ss]per line.srt—result.transcriptholds SubRip-formatted subtitles.vtt—result.transcriptholds 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
| Code | HTTP | Meaning |
|---|---|---|
MISSING_API_KEY | 401 | Authorization header is missing. |
INVALID_API_KEY | 401 | Bearer token is malformed, revoked, or unknown. |
INVALID_AUTH_SCHEME | 401 | Header is present but not the Bearer scheme. |
VALIDATION_ERROR | 400 | Request body or query parameters failed schema validation. |
METHOD_NOT_ALLOWED | 405 | The path exists but not for that HTTP method (see the Allow header). |
INSUFFICIENT_CREDITS | 402 | Account is out of credits for this billing cycle. |
NO_TRANSCRIPT | — | Video 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_FOUND | 404 | Video does not exist or is private/removed. |
CHANNEL_NOT_FOUND | 404 | Channel URL, ID, or @handle could not be resolved. |
PLAYLIST_NOT_FOUND | 404 | Playlist does not exist or is private. |
UPGRADE_REQUIRED | 402 | This feature (AI fallback, batch, or MCP) requires a higher plan tier. |
NOT_FOUND | 404 | The transcript request or batch id does not exist (or is not yours). |
ROUTE_NOT_FOUND | 404 | The URL path does not match any API endpoint. |
RATE_LIMIT_EXCEEDED | 429 | You exceeded your per-minute request limit. |
UPSTREAM_BLOCKED | 503 | YouTube 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.