# YouTube Transcript API (V2)

_Version 2.1.0_

V2 API with a simplified, user-owned transcript model.
Fast, single-call lookups with consistent response shapes.

**Capabilities**
- Single-video and batch transcription (captions + optional AI ASR)
- Direct audio/video file uploads transcribed through the ASR pipeline
- Rich ASR configuration (asr_options): language, translation, speaker
  diarization, speech understanding, and formatting
- ASR intelligence in results (asr_intelligence): summary, topics,
  content safety, sentiment, entities, highlights, chapters
- Transparent credit estimates before you commit (base + add-ons)
- Transcript management: list, fetch, delete, translate, languages, themes
- Playlist and channel resolution + batch history

**Authentication**
All endpoints (except /ping) require an API key:
`Authorization: Bearer yt_sk_live_...`

**Credit costs**
- Native captions: 1 credit
- Translation: ~1 credit / 2,500 chars
- ASR base: 1 credit / 120 seconds (5 credits per 10 minutes, ~30 credits/hour)
- ASR add-ons: prorated per hour (see asr_options), e.g. diarization +6,
  sentiment +6, profanity +3, summarization/custom-formatting +8,
  translation +16, PII redaction +22, topic detection / content
  moderation +40 credits per hour

**Rate limits**
- Free: 1 req/s · Basic: 25 req/s · Pro: 50 req/s · Business: 100 req/s

**Batch limits**
- Free: 10 · Basic: 500 · Pro: 1,500 · Business: 3,000 videos per batch

**Base URL:** https://www.youtubetranscript.dev/api/v2

## Endpoints

### POST /transcribe
Transcribe a single YouTube video

**Request body:**
- `video` (string): YouTube URL or 11-char ID. Required unless upload_id is set.
- `upload_id` (string): Id of a finalized media upload (see POST /uploads). Forces the ASR path: source must be omitted or "asr" (mode omitted or "speech_to_text_only"), and webhook_url is required. The upload id is returned as video_id in results.
- `language` (string): Preferred transcript language (e.g. en, es). If unavailable, captions in another language may be returned; transcript.language reports the actual language.
- `mode` (TranscriptMode)
- `source` (string): Legacy
- `allow_asr` (boolean): Legacy
- `format` (FormatOptions)
- `webhook_url` (string): Where ASR results are POSTed. Required for legacy ASR (source="asr" or upload_id) unless run from the dashboard. Optional with mode=auto or speech_to_text_only: poll GET /jobs/{job_id} instead. Ignored by captions_only and human_captions_only.
- `asr_options` (AsrOptions)

**Responses:** 200, 202, 400, 401, 402, 404, 429

### POST /transcribe/estimate
Estimate credit cost (dry run)

Returns the ASR base cost plus any add-on surcharge for the requested asr_options. Does not create a job or charge credits.

**Request body:**
- `video` (string, required)
- `mode` (TranscriptMode)
- `source` (string): Legacy
- `asr_options` (AsrOptions)

**Responses:** 200, 400, 401

### POST /uploads
Register a media file upload

Step 1 of transcribing your own audio/video file. Registers the file
and returns a signed URL; PUT the raw file bytes to that URL with the
file's Content-Type header (max 2GB, max 8 hours).

Full flow:
1. POST /uploads with the file metadata -> signed_url + upload_id
2. PUT the file bytes to signed_url (Content-Type = the file's mime type)
3. POST /uploads/{upload_id}/complete to verify the upload
4. POST /transcribe with upload_id (+ webhook_url) and source="asr"

**Request body:**
- `filename` (string, required): Original filename (extension is kept for content sniffing)
- `mime_type` (string, required): Must start with audio/ or video/
- `size_bytes` (integer, required): File size in bytes (max 2147483648 = 2GB)
- `duration_sec` (integer, required): Media duration in seconds (max 28800 = 8 hours). Used for the ASR credit estimate.

**Responses:** 200, 400, 401, 429

### POST /uploads/{upload_id}/complete
Finalize a media file upload

Verifies the file landed in storage and marks the upload ready for transcription. Call after the PUT to signed_url succeeds.

**Parameters:**
- `upload_id` (path, string, required): Upload id returned by POST /uploads

**Responses:** 200, 400, 401, 404, 409

### POST /batch
Transcribe multiple videos

**Request body:**
- `video_ids` (array<string>, required)
- `language` (string): Preferred language for every video
- `mode` (TranscriptMode)
- `source` (string): Legacy
- `allow_asr` (boolean): Legacy
- `format` (FormatOptions)
- `webhook_url` (string): Required when mode is auto or speech_to_text_only (legacy - source="asr" or allow_asr=true)
- `asr_options` (AsrOptions)

**Responses:** 200, 202, 400, 402

### GET /batch/{batch_id}
Poll a V2 batch job

**Parameters:**
- `batch_id` (path, string, required)

**Responses:** 200, 202, 404

### GET /jobs
List transcription jobs

**Parameters:**
- `page` (query, integer)
- `limit` (query, integer)
- `status` (query, string)

**Responses:** 200

### GET /jobs/{job_id}
Poll a V2 transcription job

**Parameters:**
- `job_id` (path, string, required)
- `include_segments` (query, boolean)
- `include_paragraphs` (query, boolean)
- `include_words` (query, boolean)

**Responses:** 200, 202, 404

### GET /transcripts
List stored transcripts

**Parameters:**
- `page` (query, integer)
- `limit` (query, integer)
- `search` (query, string)
- `status` (query, string)
- `language` (query, string)
- `date_from` (query, string)
- `date_to` (query, string)
- `include_segments` (query, boolean)

**Responses:** 200

### GET /transcripts/{video_id}
Fetch a stored transcript

Returns the best owned transcript for a video, including asr_intelligence, summary, and mind_map when available.

**Parameters:**
- `video_id` (path, string, required)
- `id` (query, string): Specific transcript/job id
- `language` (query, string)
- `source` (query, string)
- `include_timestamps` (query, boolean)

**Responses:** 200, 403, 404

### DELETE /transcripts/{video_id}
Delete a stored transcript

Deletes owned transcript(s) for a video. Optional language/source narrow the deletion; otherwise all languages/sources are removed.

**Parameters:**
- `video_id` (path, string, required)
- `language` (query, string)
- `source` (query, string)

**Responses:** 200, 404

### GET /transcripts/{video_id}/languages
List available languages for a video

Languages the caller owns plus available YouTube translation targets.

**Parameters:**
- `video_id` (path, string, required)
- `include_youtube_default` (query, boolean)

**Responses:** 200

### GET /transcripts/{video_id}/themes
Read unlocked key moments, themes & keywords (free)

**Parameters:**
- `video_id` (path, string, required)
- `id` (query, string)
- `language` (query, string)
- `source` (query, string)

**Responses:** 200, 202

### POST /transcripts/{video_id}/themes
Unlock key moments, themes & keywords (1 credit, once per transcript)

**Parameters:**
- `video_id` (path, string, required)
- `id` (query, string)
- `language` (query, string)
- `source` (query, string)

**Responses:** 200, 202, 402

### POST /transcripts/{video_id}/translate
Translate a stored transcript

**Parameters:**
- `video_id` (path, string, required)

**Request body:**
- `target_language` (string, required)
- `source_language` (string)
- `provider` (string)
- `allow_ai_fallback` (boolean)

**Responses:** 200, 400, 402

### POST /transcripts/bulk-delete
Delete transcripts by id

**Request body:**
- `ids` (array<string>, required)

**Responses:** 200, 403

### GET /credits
Get credit balance and plan

**Responses:** 200

### GET /ping
Health check

**Responses:** 200

### GET /folders
List your folders with video counts (free)

**Responses:** 200

### POST /folders
Create a folder (free)

**Request body:**
- `name` (string, required)

**Responses:** 201, 409

### GET /folders/{folder_id}
Get one folder (free)

**Parameters:**
- `folder_id` (path, string, required)

**Responses:** 200, 404

### PATCH /folders/{folder_id}
Rename a folder (free)

**Parameters:**
- `folder_id` (path, string, required)

**Request body:**
- `name` (string, required)

**Responses:** 200, 409

### DELETE /folders/{folder_id}
Delete a folder; its videos and transcripts stay (free)

**Parameters:**
- `folder_id` (path, string, required)

**Responses:** 200

### GET /folders/{folder_id}/videos
List a folder's videos (same shape as GET /videos) (free)

**Parameters:**
- `folder_id` (path, string, required)
- `search` (query, string)
- `page` (query, integer)
- `limit` (query, integer)

**Responses:** 200

### POST /folders/{folder_id}/videos
Add videos to a folder (free)

**Parameters:**
- `folder_id` (path, string, required)

**Request body:**
- `video_ids` (array<string>, required)

**Responses:** 200

### DELETE /folders/{folder_id}/videos
Remove videos from a folder; transcripts are kept (free)

**Parameters:**
- `folder_id` (path, string, required)

**Request body:**
- `video_ids` (array<string>, required)

**Responses:** 200

### GET /videos
List your videos with their folders and existing outputs (free)

**Parameters:**
- `folder_id` (query, string)
- `unfiled` (query, boolean): Only videos in no folder
- `channel` (query, string): One channel's videos
- `sort` (query, string)
- `search` (query, string)
- `page` (query, integer)
- `limit` (query, integer)

**Responses:** 200

### GET /videos/channels
The channels your videos come from, most videos first (free)

**Responses:** 200

### GET /videos/{video_id}/folders
The folders a video is in (free)

**Parameters:**
- `video_id` (path, string, required)

**Responses:** 200

### PUT /videos/{video_id}/folders
Put a video in exactly these folders (free)

**Parameters:**
- `video_id` (path, string, required)

**Request body:**
- `folder_ids` (array<string>, required)

**Responses:** 200, 404

### POST /social/transcribe
Transcribe an Instagram Reel (1 credit with captions, else 1 per 2 minutes of audio)

**Request body:**
- `url` (string, required): Instagram Reel or video post link
- `language` (string): ISO 639-1

**Responses:** 200, 402, 422

### POST /library/search
Find every mention of keywords across your saved transcripts (free)

**Request body:**
- `keywords` (array<string>): Required unless keyword_set_id is set
- `keyword_set_id` (string)
- `exclude` (array<string>): Leave out transcripts that mention any of these
- `whole_word` (boolean)
- `video_ids` (array<string>): Default is your 1
- `limit` (integer)

**Responses:** 200

### POST /library/keywords/suggest
The same keywords in other languages (free)

**Request body:**
- `keywords` (array<string>, required)
- `languages` (array<string>)

**Responses:** 200

### GET /library/keyword-sets
Your saved keyword sets (free)

**Responses:** 200

### POST /library/keyword-sets
Save a keyword set (free)

**Request body:**
- `name` (string, required)
- `keywords` (array<string>, required)

**Responses:** 201

### PATCH /library/keyword-sets/{set_id}
Rename a keyword set or replace its keywords (free)

**Parameters:**
- `set_id` (path, string, required)

**Request body:**
- `name` (string)
- `keywords` (array<string>)

**Responses:** 200, 404

### DELETE /library/keyword-sets/{set_id}
Delete a keyword set (free)

**Parameters:**
- `set_id` (path, string, required)

**Responses:** 200

### GET /videos/folders
The folders each of several videos is in (free)

**Parameters:**
- `video_ids` (query, string, required): Comma-separated

**Responses:** 200

### GET /folders/{folder_id}/suggestions
Videos that belong in a folder, picked by AI from titles (free)

**Parameters:**
- `folder_id` (path, string, required)

**Responses:** 200, 429

### GET /videos/{video_id}/folder-suggestions
Folders a video fits, picked by AI from its title (free)

**Parameters:**
- `video_id` (path, string, required)

**Responses:** 200, 404

### POST /playlists/resolve
Resolve playlist videos

**Request body:**
- `playlist_url` (string)
- `playlist_id` (string)
- `limit` (integer)

**Responses:** 200

### GET /playlists/{playlist_id}
Get playlist batch details

**Parameters:**
- `playlist_id` (path, string, required)

**Responses:** 200

### GET /playlists/history
List playlist batches

**Responses:** 200

### POST /channels/resolve
Resolve channel uploads

**Request body:**
- `channel_url` (string)
- `channel_id` (string)
- `handle` (string)
- `limit` (integer)

**Responses:** 200

### GET /channels/{channel_id}
Get channel batch details

**Parameters:**
- `channel_id` (path, string, required)

**Responses:** 200

### GET /channels/history
List channel batches

**Responses:** 200

### GET /recommendations/videos
Recommended videos to transcribe

Newest uploads from the channels the caller transcribes most, excluding videos already transcribed. No credits charged.

**Parameters:**
- `limit` (query, integer)
- `channels` (query, integer): How many top channels to draw from

**Responses:** 200

### GET /search/videos
Search Videos

Search YouTube videos by keyword. Costs 1 credit per request,
including each next page. Credits are refunded if the search fails.

**Parameters:**
- `undefined` (undefined)
- `undefined` (undefined)
- `undefined` (undefined)
- `undefined` (undefined)
- `undefined` (undefined)
- `undefined` (undefined)

**Responses:** 200, 400, 401, 402, 429, 502

### GET /search/shorts
Search Shorts

Search YouTube shorts by keyword. Costs 1 credit per request,
including each next page. Credits are refunded if the search fails.

**Parameters:**
- `undefined` (undefined)
- `undefined` (undefined)
- `undefined` (undefined)
- `undefined` (undefined)
- `undefined` (undefined)

**Responses:** 200, 400, 401, 402, 429, 502

### GET /search/channels
Search Channels

Search YouTube channels by keyword. Costs 1 credit per request,
including each next page. Credits are refunded if the search fails.

**Parameters:**
- `undefined` (undefined)
- `undefined` (undefined)
- `undefined` (undefined)
- `undefined` (undefined)
- `undefined` (undefined)

**Responses:** 200, 400, 401, 402, 429, 502

### GET /search/playlists
Search Playlists

Search YouTube playlists by keyword. Costs 1 credit per request,
including each next page. Credits are refunded if the search fails.

**Parameters:**
- `undefined` (undefined)
- `undefined` (undefined)
- `undefined` (undefined)
- `undefined` (undefined)
- `undefined` (undefined)

**Responses:** 200, 400, 401, 402, 429, 502
