Skip to content

YouTube Transcript API Reference

The YouTube Transcript API provides a simple and reliable way to extract transcripts from YouTube videos programmatically. This complete reference covers authentication, request parameters, response formats, and practical examples.

Want to test the API interactively? Try our Swagger UI where you can make live requests right in your browser.

All API requests are made to:

https://transcriptapi.com/api/v2

The API provides the following endpoints:

EndpointDescriptionCredits
GET /youtube/transcriptExtract video transcript (supports a language priority list)1
GET /youtube/infoVideo metadata + available transcript languagesFree
GET /youtube/video/metadataRich video metadata (structured description, channel, optional details/related)1
GET /youtube/searchSearch videos, channels, playlists, or movies — with sort/upload_date/duration/features filters1
GET /youtube/channel/resolveResolve @handle/URL to channel IDFree
GET /youtube/channel/infoChannel profile / identity (title, handle, counts, tags, banners, tabs)1
GET /youtube/channel/searchSearch within a channel (accepts @handle, URL, or UC… ID)1
GET /youtube/channel/videosPaginated channel feed — tab=videos (default), shorts, or streams; optional sort1/page
GET /youtube/channel/playlistsPaginated channel playlists1/page
GET /youtube/channel/postsPaginated community (Posts tab) content1/page
GET /youtube/channel/sectionsCurated channel sections (featured Home page, podcasts, releases)1
GET /youtube/channel/latestLatest 15 videos via RSS (accepts @handle, URL, or UC… ID)Free
GET /youtube/playlist/videosPaginated playlist videos (accepts URL or playlist ID)1/page

Here’s how to make your first API request:

Terminal window
curl -X GET "https://transcriptapi.com/api/v2/youtube/transcript?video_url=dQw4w9WgXcQ" \
-H "Authorization: Bearer YOUR_API_KEY"

Don’t have an API key yet? Get one from your dashboard.

Expected Response:

{
"video_id": "dQw4w9WgXcQ",
"language": "en",
"transcript": [
{
"text": "Never gonna give you up",
"start": 0.0,
"duration": 4.12
},
{
"text": "Never gonna let you down",
"start": 4.12,
"duration": 3.85
}
],
"length_seconds": 213,
"lengthText": "3:33"
}

The API uses Bearer token authentication. Include your API key in the Authorization header of every request:

Authorization: Bearer YOUR_API_KEY

Example:

Terminal window
curl -X GET "https://transcriptapi.com/api/v2/youtube/transcript?video_url=..." \
-H "Authorization: Bearer YOUR_API_KEY"

Get your API key from your API Keys dashboard.

The YouTube video URL or video ID to fetch transcripts for.

PropertyValue
Typestring
Pattern^([a-zA-Z0-9_-]{11}|https?://.\*)$
RequiredYes

Accepted Formats:

  • Full YouTube URL: https://www.youtube.com/watch?v=dQw4w9WgXcQ
  • Short YouTube URL: https://youtu.be/dQw4w9WgXcQ
  • Video ID only: dQw4w9WgXcQ

Examples:

Terminal window
# Full URL
?video_url=https://www.youtube.com/watch?v=dQw4w9WgXcQ
# Short URL
?video_url=https://youtu.be/dQw4w9WgXcQ
# Video ID only
?video_url=dQw4w9WgXcQ

The output format for the transcript response.

PropertyValue
Typestring
Valuesjson, text
Defaultjson
  • json: Returns structured data with transcript segments
  • text: Returns plain text transcript

Whether to include timestamps in the transcript output.

PropertyValue
Typeboolean
Defaulttrue

Behavior Matrix:

Formatinclude_timestampOutput
jsontrueSegments with text, start, duration
jsonfalseSegments with only text
texttrueLines formatted as [123.45s] text
textfalsePlain concatenated text

Whether to include video metadata in the response.

PropertyValue
Typeboolean
Defaultfalse

When enabled, includes:

  • title: Video title
  • author_name: Channel name
  • author_url: Channel URL
  • thumbnail_url: Video thumbnail

A comma-separated priority list of language codes. The API returns the first one that’s available.

PropertyValue
Typestring
Default(omitted) — English, otherwise the video’s first available language
  • Provide a list like de,en,asr; codes are tried left to right and the first available wins. If none resolve, you receive a 404 that lists the languages the video does offer.
  • Codes are case-insensitive and region is ignored (en-GB/en-US → en, de-DE → de), so de matches a German track. Up to 10 codes.
  • asr requests the video’s auto-generated captions (ASR = automatic speech recognition). Use asr-<code> (for example asr-hi) to request a specific auto-generated language.
  • A plain code such as hi returns the creator’s captions when they exist, otherwise the auto-generated ones (asr-hi).
  • The language field in a 200 response is the resolved code (en, de, asr-hi, …), so you always know exactly which track you received.

Tip: Call GET /youtube/info first — it’s free — to see which languages a video offers.

The API supports multiple response formats based on your parameters:

{
"video_id": "dQw4w9WgXcQ",
"language": "en",
"transcript": [
{
"text": "Never gonna give you up",
"start": 0.0,
"duration": 4.12
},
{
"text": "Never gonna let you down",
"start": 4.12,
"duration": 3.85
}
],
"metadata": {
"title": "Rick Astley - Never Gonna Give You Up",
"author_name": "RickAstleyVEVO",
"author_url": "https://www.youtube.com/@RickAstley",
"thumbnail_url": "https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg"
},
"length_seconds": 213,
"lengthText": "3:33"
}

Every 200 response also includes the total video length as two top-level fields — you don’t need any parameter to get them:

FieldTypeDescription
length_secondsintegerTotal video length in whole seconds (e.g. 213).
lengthTextstringThe same length, human-readable (e.g. 3:33, or 1:02:45 for videos over an hour). Matches the lengthText from the search, channel, and playlist endpoints.

Both are null when the source doesn’t expose a fixed length — live streams, and the rare fallback extraction path. They are independent of send_metadata.

HeaderDescription
X-Cache-StatusCache status: HIT, PARTIAL-HIT, or MISS

Discover a video’s metadata and the transcript languages it offers — before spending a transcript credit.

Terminal window
curl -X GET "https://transcriptapi.com/api/v2/youtube/info?video_url=dQw4w9WgXcQ" \
-H "Authorization: Bearer YOUR_API_KEY"
{
"video_id": "dQw4w9WgXcQ",
"metadata": {
"title": "...",
"author_name": "...",
"author_url": "...",
"thumbnail_url": "..."
},
"available_languages": [
{ "code": "en", "name": "English" },
{ "code": "asr-en", "name": "English (auto-generated)" }
]
}
  • Free — no credit is consumed. The same API key and active plan are required as for the transcript endpoint.
  • Each available_languages code can be passed directly to the transcript endpoint’s language parameter (for example en, or asr-en for auto-generated English).
  • Returns 404 when the video does not exist or has no captions.
GET /youtube/video/metadata

Inspect a video’s metadata without needing captions — title, view/like-count text, publish date, a structured description with extracted links, an uploading-channel summary, and thumbnails. Unlike the free /youtube/info endpoint (which is scoped to transcript-language discovery), this returns the full metadata surface and can optionally pull heavier player-sourced data.

ParameterTypeRequiredDefaultDescription
video_urlstringYes—YouTube video URL or 11-character video ID.
includestringNo—Comma-separated extras: details, related.
  • include=related — adds a related list of suggested videos.
  • include=details — adds a details object with lengthSeconds, category, tags, official thumbnails, and the caption-track inventory. These come from YouTube’s player endpoint, which requires our proxied (production) path; where it can’t be read, details.available is false with a reason instead of guessed values.

Display strings and numeric values are kept distinct; hidden counts are null, never 0.

{
"videoId": "dQw4w9WgXcQ",
"title": "Rick Astley - Never Gonna Give You Up",
"viewCountText": "1,600,000,000 views",
"viewCountShort": "1.6B views",
"publishDate": "2009-10-25",
"relativeDate": "16 years ago",
"likeCountText": "18M",
"description": "The official video for ...",
"descriptionLinks": [
{ "text": "Listen On Spotify", "url": "https://open.spotify.com/..." }
],
"channel": {
"channelId": "UCuAXFkgsw1L7xaCfnd5JJOw",
"name": "Rick Astley",
"subscriberCountText": "3.9M subscribers",
"thumbnails": [...]
},
"thumbnails": [...],
"details": {
"available": true,
"lengthSeconds": 213,
"category": "Music",
"tags": ["Rick Astley", "Never Gonna Give You Up"],
"isLiveContent": false,
"captionTracks": [
{ "code": "en", "name": "English", "isAutoGenerated": false }
]
},
"related": [
{ "kind": "video", "id": "abc123xyz00", "title": "...", "viewCountText": "12M views", "lengthText": "3:32", "channelName": "..." }
]
}

details and related are present only when requested via include.

Freshness: cached for 5 minutes. Credit cost: 1 credit per request.

Example:

Terminal window
# Metadata only
curl -X GET "https://transcriptapi.com/api/v2/youtube/video/metadata?video_url=dQw4w9WgXcQ" \
-H "Authorization: Bearer YOUR_API_KEY"
# With player details + related videos
curl -X GET "https://transcriptapi.com/api/v2/youtube/video/metadata?video_url=dQw4w9WgXcQ&include=details,related" \
-H "Authorization: Bearer YOUR_API_KEY"
GET /youtube/search

Search YouTube for videos or channels.

ParameterTypeRequiredDefaultDescription
qstringConditional—Search query (1–200 characters). Required for the first page.
typestringNovideoResult type: video, channel, playlist, or movie (first page only).
sortstringNorelevanceSort order (first page): relevance or views (YouTube’s “Popularity”).
upload_datestringNo—Upload-date window (first page, videos only): hour, today, week, month, year.
durationstringNo—Duration bucket (first page, videos only): short (under 4m), medium (4–20m), long (over 20m).
featuresstringNo—Comma-separated feature filters (first page), e.g. hd, subtitles, cc, live, 4k, hdr, 360, creative_commons.
spstringNo—Advanced: raw YouTube sp filter (base64). Overrides the structured filters above.
continuationstringConditional—Continuation token from previous response (subsequent pages).

Provide exactly one of q or continuation. When continuation is provided, q/type/filters are ignored (the token already encodes them).

Each call returns YouTube’s full page (~20 items). Use continuation_token to fetch more.

{
"results": [
{
"type": "video",
"videoId": "abc123xyz00",
"title": "The future of design — TED",
"channelId": "UCAuUUnT6oDeKwE6v1NGQxug",
"channelTitle": "TED",
"channelHandle": "@TED",
"channelVerified": true,
"lengthText": "12:34",
"viewCountText": "3.4M views",
"publishedTimeText": "2 years ago",
"hasCaptions": true,
"thumbnails": [
{"url": "https://i.ytimg.com/vi/abc123xyz00/default.jpg", "width": 120, "height": 90}
]
}
],
"result_count": 20,
"continuation_token": "4qmFsgKlARIYVVV1QVhGa2dz...",
"has_more": true
}

Pagination flow:

  1. First request: ?q=design&type=video — returns first page + continuation_token
  2. Next request: ?continuation=4qmFsgKlARIYVVV1QVhGa2dz... — returns next page + new token
  3. Repeat until has_more is false or continuation_token is null

Each page costs 1 credit.

Example:

Terminal window
# First page
curl -X GET "https://transcriptapi.com/api/v2/youtube/search?q=innovation&type=video" \
-H "Authorization: Bearer YOUR_API_KEY"
# Filtered: most-viewed long videos uploaded this month
curl -X GET "https://transcriptapi.com/api/v2/youtube/search?q=innovation&sort=views&duration=long&upload_date=month" \
-H "Authorization: Bearer YOUR_API_KEY"
# Next page
curl -X GET "https://transcriptapi.com/api/v2/youtube/search?continuation=4qmFsgKlARIYVVV1QVhGa2dz..." \
-H "Authorization: Bearer YOUR_API_KEY"

GET /youtube/channel/resolve

Resolve any channel reference (@handle, URL, or UC… ID) to a canonical UC… channel ID. Free — no credits charged.

ParameterTypeRequiredDescription
inputstringYes@handle, channel URL, or UC… ID (1–200 characters)

Response:

{
"channel_id": "UCAuUUnT6oDeKwE6v1NGQxug",
"resolved_from": "@TED"
}

Example:

Terminal window
curl -X GET "https://transcriptapi.com/api/v2/youtube/channel/resolve?input=@TED" \
-H "Authorization: Bearer YOUR_API_KEY"
GET /youtube/channel/search

Search for videos within a specific channel. Accepts an @handle, channel URL, or UC… channel ID.

ParameterTypeRequiredDefaultDescription
channelstringConditional—@handle, channel URL, or UC… channel ID (first page).
qstringConditional—Search query (1–200 characters, first page).
continuationstringConditional—Continuation token from previous response (subsequent pages).

Provide exactly one of (channel + q) or continuation. When continuation is provided, channel/q are ignored.

Each call returns YouTube’s full page (~30 items). Use continuation_token to fetch more.

Response:

{
"results": [
{
"type": "video",
"videoId": "abc123xyz00",
"title": "Video Title",
"channelId": "UCAuUUnT6oDeKwE6v1NGQxug",
"channelTitle": "TED",
"channelHandle": "@TED",
"channelVerified": true,
"lengthText": "12:34",
"viewCountText": "2.1M views",
"publishedTimeText": "3 months ago",
"hasCaptions": true,
"thumbnails": [...]
}
],
"result_count": 12,
"continuation_token": "4qmFsgKlARIYVVV1QVhGa2dz...",
"has_more": true
}

Pagination flow:

  1. First request: ?channel=@TED&q=innovation — returns first page + continuation_token
  2. Next request: ?continuation=4qmFsgKlARIYVVV1QVhGa2dz... — returns next page + new token
  3. Repeat until has_more is false or continuation_token is null

Each page costs 1 credit.

Example:

Terminal window
# First page
curl -X GET "https://transcriptapi.com/api/v2/youtube/channel/search?channel=@TED&q=innovation" \
-H "Authorization: Bearer YOUR_API_KEY"
# Next page
curl -X GET "https://transcriptapi.com/api/v2/youtube/channel/search?continuation=4qmFsgKlARIYVVV1QVhGa2dz..." \
-H "Authorization: Bearer YOUR_API_KEY"
GET /youtube/channel/videos

List a channel’s feed, paginated at ~100 per page. Accepts an @handle, channel URL, or UC… channel ID. Use tab to select the uploads feed (default), Shorts, or live streams, and sort to reorder it.

ParameterTypeRequiredDefaultDescription
channelstringConditional—@handle, channel URL, or UC… channel ID (first page).
tabstringNovideosWhich feed: videos (uploads), shorts, or streams (live). Repeat the same tab when paginating.
sortstringNo—latest, popular, or oldest. Omit for the default uploads feed. Repeat the same sort when paginating.
continuationstringConditional—Continuation token from previous response (next pages).

Provide exactly one of channel or continuation. When paginating a shorts or streams feed, or any sorted feed, pass the same tab and sort values on every page.

sort is opt-in, and omitting it is not the same as sort=latest — they read two different YouTube feeds:

tab=videos, no sorttab=videos + any sort
Sourceuploads playlistthe channel’s Videos tab
Page size~100~30
playlist_infopopulatednull
Shortsmixed in with long-form uploadsexcluded — use tab=shorts
Members-only videosexcludedincluded, flagged members_only: true

These are different sets, not one list in two orders. The uploads playlist interleaves Shorts with long-form uploads; the Videos tab holds long-form only and adds membership content. So sort=latest can return a different first video than omitting sort — the unsorted feed’s newest item may be a Short.

Walking a full catalogue with sort set costs about 3.3x the pages, and therefore 3.3x the credits. Omit sort when you just want newest-first.

tab=shorts and tab=streams read the same feed with or without sort; there, sort only reorders it.

Every item carries members_only (a boolean, false unless YouTube badges the video “Members only”). Members-only items have viewCountText: null — YouTube does not publish view counts for them.

Sorted response:

{
"results": [
{
"videoId": "abc123xyz00",
"title": "Most-Watched Talk",
"lengthText": "18:04",
"viewCountText": "72M views",
"publishedTimeText": "11 years ago",
"thumbnails": [...],
"members_only": false
}
],
"playlist_info": null,
"continuation_token": "4qmFsgJkEhhVQ0F1VVVuVDZvRGVLd0U2djFOR1F4dWca...",
"has_more": true
}

Response:

{
"results": [
{
"videoId": "abc123xyz00",
"title": "Latest Video",
"channelId": "UCAuUUnT6oDeKwE6v1NGQxug",
"channelTitle": "TED",
"channelHandle": "@TED",
"lengthText": "15:22",
"viewCountText": "3.2M views 2 weeks ago",
"thumbnails": [...],
"index": "0",
"members_only": false
}
],
"playlist_info": {
"title": "Uploads from TED",
"numVideos": "5200",
"description": "",
"ownerName": "TED",
"viewCount": null
},
"continuation_token": "4qmFsgKlARIYVVV1QVhGa2dz...",
"has_more": true
}

Pagination flow:

  1. First request: ?channel=@TED — returns first ~100 videos + continuation_token
  2. Next request: ?continuation=4qmFsgKlARIYVVV1QVhGa2dz... — returns next ~100 + new token
  3. Repeat until has_more is false or continuation_token is null

Example:

Terminal window
# First page (using @handle)
curl -X GET "https://transcriptapi.com/api/v2/youtube/channel/videos?channel=@TED" \
-H "Authorization: Bearer YOUR_API_KEY"
# First page (using channel URL)
curl -X GET "https://transcriptapi.com/api/v2/youtube/channel/videos?channel=https://www.youtube.com/@TED" \
-H "Authorization: Bearer YOUR_API_KEY"
# Next page
curl -X GET "https://transcriptapi.com/api/v2/youtube/channel/videos?continuation=4qmFsgKlARIYVVV1QVhGa2dz..." \
-H "Authorization: Bearer YOUR_API_KEY"
# Most-viewed first (channel-tab feed, ~30/page)
curl -X GET "https://transcriptapi.com/api/v2/youtube/channel/videos?channel=@TED&sort=popular" \
-H "Authorization: Bearer YOUR_API_KEY"
# Oldest live streams first — repeat tab AND sort on every page
curl -X GET "https://transcriptapi.com/api/v2/youtube/channel/videos?channel=@NASA&tab=streams&sort=oldest" \
-H "Authorization: Bearer YOUR_API_KEY"
GET /youtube/channel/info

Fetch a channel’s profile: title, @handle, verified flag, subscriber/video-count text, description, keywords, tags, thumbnails, banners, and the tabs the channel exposes.

ParameterTypeRequiredDescription
channelstringYes@handle, channel URL, or UC… channel ID.

Formatted counts are display strings (e.g. 27.8M subscribers) and are null when YouTube does not expose them — never 0. About-tab fields YouTube does not serve over an unauthenticated request (join date, total views, country, external links) are omitted rather than guessed.

Response:

{
"channelId": "UCAuUUnT6oDeKwE6v1NGQxug",
"title": "TED",
"handle": "@TED",
"verified": true,
"subscriberCountText": "23.8M subscribers",
"videoCountText": "4,300 videos",
"description": "The TED Talks channel features ...",
"keywords": "ted talks, technology, ...",
"tags": ["TED", "TED Talks"],
"isFamilySafe": true,
"thumbnails": [...],
"banners": [...],
"availableTabs": ["videos", "shorts", "playlists", "community"]
}

Freshness: cached for 5 minutes. Credit cost: 1 credit per request. Returns 404 when the channel does not exist (do not retry).

Terminal window
curl -X GET "https://transcriptapi.com/api/v2/youtube/channel/info?channel=@TED" \
-H "Authorization: Bearer YOUR_API_KEY"
GET /youtube/channel/playlists

List the playlists shown on a channel (id, title, URL, video-count text, thumbnails), paginated.

ParameterTypeRequiredDescription
channelstringConditional@handle, channel URL, or UC… channel ID (first page).
continuationstringConditionalContinuation token from previous response (next pages).

Provide exactly one of channel or continuation. Video-count text is a display string (e.g. 11 videos) and is null when YouTube omits it — never 0.

Response:

{
"results": [
{
"playlistId": "PLabc...",
"title": "Most-viewed talks",
"url": "https://www.youtube.com/playlist?list=PLabc...",
"videoCountText": "25 videos",
"thumbnails": [...]
}
],
"result_count": 30,
"continuation_token": "4qmFsgKlARIYVVV1QVhGa2dz...",
"has_more": true
}

Freshness: cached for 5 minutes. Credit cost: 1 credit per page.

Terminal window
curl -X GET "https://transcriptapi.com/api/v2/youtube/channel/playlists?channel=@TED" \
-H "Authorization: Bearer YOUR_API_KEY"
GET /youtube/channel/posts

List a channel’s community (Posts tab) content — text, publish time, like-count text, and any attachment (image, multi-image, video, playlist, or poll), paginated.

ParameterTypeRequiredDescription
channelstringConditional@handle, channel URL, or UC… channel ID (first page).
continuationstringConditionalContinuation token from previous response (next pages).

Provide exactly one of channel or continuation. Channels with no community tab return an empty results list (HTTP 200, not an error).

Response:

{
"results": [
{
"postId": "Ugkx...",
"authorName": "TED",
"authorThumbnails": [...],
"text": "New talk out now!",
"publishedTimeText": "2 days ago",
"voteCountText": "1.2K",
"attachment": {
"type": "video",
"videoId": "abc123xyz00",
"title": "...",
"lengthText": "12:34"
}
}
],
"result_count": 20,
"continuation_token": "4qmFsgKlARIYVVV1QVhGa2dz...",
"has_more": true
}

attachment.type is one of image, multi_image, video, playlist, poll (only the fields relevant to that type are present). Like counts are display strings — null when hidden, never 0.

Freshness: cached for 5 minutes. Credit cost: 1 credit per page.

Terminal window
curl -X GET "https://transcriptapi.com/api/v2/youtube/channel/posts?channel=@TED" \
-H "Authorization: Bearer YOUR_API_KEY"
GET /youtube/channel/sections

Return a channel’s curated sections (the shelves on its Home page and other curated tabs) in the channel’s own order — each shelf holds videos, playlists, shorts, or featured channels.

ParameterTypeRequiredDefaultDescription
channelstringYes—@handle, channel URL, or UC… channel ID.
tabstringNofeaturedWhich curated page: featured (Home), podcasts, or releases.

podcasts and releases exist only on channels that have them (an empty results list otherwise). This endpoint is not paginated.

Response:

{
"tab": "featured",
"results": [
{
"title": "Popular videos",
"type": "shelf",
"items": [
{
"kind": "video",
"videoId": "abc123xyz00",
"title": "...",
"viewCountText": "12M views",
"thumbnails": [...]
}
]
}
],
"result_count": 8
}

items[].kind is one of video, playlist, short, channel. Freshness: cached for 5 minutes. Credit cost: 1 credit per request. Returns 404 when the channel does not exist (do not retry).

Terminal window
curl -X GET "https://transcriptapi.com/api/v2/youtube/channel/sections?channel=@TED" \
-H "Authorization: Bearer YOUR_API_KEY"
GET /youtube/channel/latest

Get the latest 15 videos from a channel via YouTube RSS feed. Returns exact publish timestamps and view counts. Accepts an @handle, channel URL, or UC… channel ID. Free — no credits charged.

ParameterTypeRequiredDescription
channelstringYes@handle, channel URL, or UC… channel ID

Response:

{
"channel": {
"channelId": "UCAuUUnT6oDeKwE6v1NGQxug",
"title": "TED",
"author": "TED",
"url": "https://www.youtube.com/channel/UCAuUUnT6oDeKwE6v1NGQxug",
"published": "2006-12-18T00:00:00Z"
},
"results": [
{
"videoId": "abc123xyz00",
"title": "Latest Video Title",
"channelId": "UCAuUUnT6oDeKwE6v1NGQxug",
"author": "TED",
"published": "2026-01-30T16:00:00Z",
"updated": "2026-01-31T02:00:00Z",
"link": "https://www.youtube.com/watch?v=abc123xyz00",
"description": "Full video description...",
"thumbnail": {"url": "https://i1.ytimg.com/vi/abc123xyz00/hqdefault.jpg", "width": "480", "height": "360"},
"viewCount": "2287630",
"starRating": {"average": "4.92", "count": "45000", "min": "1", "max": "5"}
}
],
"result_count": 15
}

Example:

Terminal window
curl -X GET "https://transcriptapi.com/api/v2/youtube/channel/latest?channel=@TED" \
-H "Authorization: Bearer YOUR_API_KEY"

GET /youtube/playlist/videos

List videos in a playlist, paginated at ~100 per page. Accepts a YouTube playlist URL or a bare playlist ID.

ParameterTypeRequiredDescription
playliststringConditionalYouTube playlist URL or playlist ID (PL…, UU…, LL…, FL…, OL…).
continuationstringConditionalContinuation token from previous response (next pages).

Provide exactly one of playlist or continuation.

Response:

{
"results": [
{
"videoId": "abc123xyz00",
"title": "Playlist Video",
"channelId": "UCAuUUnT6oDeKwE6v1NGQxug",
"channelTitle": "TED",
"channelHandle": "@TED",
"lengthText": "10:05",
"viewCountText": "1.5M views 6 months ago",
"thumbnails": [...],
"index": "0"
}
],
"playlist_info": {
"title": "Best Tech of 2025",
"numVideos": "47",
"description": "My picks for the best tech this year",
"ownerName": "TED",
"viewCount": "5000000"
},
"continuation_token": "4qmFsgKlARIYVVV1QVhGa2dz...",
"has_more": true
}

Pagination: Same flow as channel/videos — use continuation_token from each response to fetch the next page.

Example:

Terminal window
# First page (using playlist ID)
curl -X GET "https://transcriptapi.com/api/v2/youtube/playlist/videos?playlist=PLrAXtmErZgOeiKm4sgNOknGvNjby9efdf" \
-H "Authorization: Bearer YOUR_API_KEY"
# First page (using playlist URL)
curl -X GET "https://transcriptapi.com/api/v2/youtube/playlist/videos?playlist=https://www.youtube.com/playlist?list=PLrAXtmErZgOeiKm4sgNOknGvNjby9efdf" \
-H "Authorization: Bearer YOUR_API_KEY"
# Next page
curl -X GET "https://transcriptapi.com/api/v2/youtube/playlist/videos?continuation=4qmFsgKlARIYVVV1QVhGa2dz..." \
-H "Authorization: Bearer YOUR_API_KEY"

EndpointCredits per RequestNotes
GET /youtube/transcript1 creditOnly charged on successful response (200)
GET /youtube/infoFreeTranscript-language discovery; requires auth + ≥1 active credit
GET /youtube/video/metadata1 creditRich metadata; include=details,related does not change the cost
GET /youtube/search1 creditVideos, channels, playlists, or movies (per page)
GET /youtube/channel/resolveFreeRequires auth + at least 1 active credit
GET /youtube/channel/info1 creditChannel profile / identity
GET /youtube/channel/search1 creditSearch within a channel
GET /youtube/channel/videos1 credit/pagePaginated — each page (any tab/sort) costs 1 credit. A sorted tab=videos feed pages at ~30 instead of ~100, so a full walk costs ~3.3x
GET /youtube/channel/playlists1 credit/pagePaginated — each page costs 1 credit
GET /youtube/channel/posts1 credit/pagePaginated — each page costs 1 credit
GET /youtube/channel/sections1 creditCurated sections (not paginated)
GET /youtube/channel/latestFreeRequires auth + at least 1 active credit
GET /youtube/playlist/videos1 credit/pagePaginated — each page costs 1 credit
  • ✅ Successful requests (HTTP 200) - 1 credit (paid endpoints only)
  • ✅ Cached responses (HTTP 200) - 1 credit (paid endpoints only)
  • ✅ Free endpoints (HTTP 200) - 0 credits (requires at least 1 active credit)
  • ❌ Failed requests (4xx, 5xx errors) - 0 credits
  • ❌ Rate limited requests (HTTP 429) - 0 credits

Credits are deducted in real-time only when a request is successfully returned.

When credits are exhausted, the API returns HTTP 402 Payment Required.

All API keys are subject to the following rate limits:

  • 300 requests per minute per API key

Each response includes rate limit information in the headers:

HeaderDescription
X-RateLimit-LimitTotal allowed requests in the window
X-RateLimit-RemainingRemaining requests in the window
X-RateLimit-ResetUTC epoch seconds when the window resets
Retry-AfterSeconds until you can retry (only on 429)

Example Headers:

X-RateLimit-Limit: 200
X-RateLimit-Remaining: 195
X-RateLimit-Reset: 1678901234
  1. Implement exponential backoff on 429 errors
  2. Respect the Retry-After header value
  3. Cache responses when appropriate to reduce API calls
  4. Don’t retry failed requests more than 2 times within 3 seconds
  5. Monitor rate limit headers to avoid hitting limits

The API uses standard HTTP status codes:

Status CodeMeaningRetry?Action
200Success—Transcript returned, 1 credit charged
400Bad Request❌ NoCheck your request parameters
401Unauthorized❌ NoInvalid or missing API key
402Payment Required❌ NoNo credits remaining - visit billing
404Not Found❌ NoVideo not found or transcript unavailable
408Timeout / Retry✅ YesTemporary failure (bot detection, network) - retry in 1-5s
422Validation Error❌ NoInvalid YouTube URL or ID
429Too Many Requests✅ YesRate limit exceeded - retry after Retry-After header
500Server Error⚠️ MaybeContact support if persistent
503Service Unavailable✅ YesService temporarily down - retry in 1-5s

For retryable errors (408, 429, 503):

  1. Wait the recommended delay (1-5 seconds, or check Retry-After header for 429)
  2. Retry up to 2-3 times with exponential backoff
  3. Give up after max retries and log the failure

For non-retryable errors (400, 401, 402, 404, 422): Do not retry - fix the request parameters or check your account status.

All error responses follow this format:

{
"detail": "Human-readable error message",
"code": "ERROR_CODE"
}

404 — No transcript for the requested languages

Section titled “404 — No transcript for the requested languages”

When you pass a language priority list and none of the requested codes are available, the response is a 404 that lists the languages the video does offer, so you can retry with a valid one:

{
"detail": "No transcript available for the requested languages: en, de",
"code": "no_transcript_for_requested_languages",
"available_languages": [
{ "code": "hi", "name": "Hindi" },
{ "code": "asr-hi", "name": "Hindi (auto-generated)" }
]
}

When you omit language, a video without captions instead returns a plain 404 with only a detail message.

Includes a WWW-Authenticate header:

WWW-Authenticate: Bearer

Special format with action details:

Insufficient Credits:

{
"detail": {
"message": "You have an active plan, but you've run out of credits.",
"reason": "insufficient_credits",
"action_label": "Top up credits",
"action_url": "https://transcriptapi.com/top-up"
}
}

No Active Plan:

{
"detail": {
"message": "You don't have an active paid plan yet.",
"reason": "no_active_paid_plan",
"action_label": "Go to billing to choose a plan",
"action_url": "https://transcriptapi.com/billing"
}
}

Here are complete examples in multiple languages:

Terminal window
# Basic request
curl -X GET "https://transcriptapi.com/api/v2/youtube/transcript?video_url=dQw4w9WgXcQ" \
-H "Authorization: Bearer YOUR_API_KEY"
# With all parameters
curl -X GET "https://transcriptapi.com/api/v2/youtube/transcript?video_url=dQw4w9WgXcQ&format=json&include_timestamp=true&send_metadata=true" \
-H "Authorization: Bearer YOUR_API_KEY"
# Text format without timestamps
curl -X GET "https://transcriptapi.com/api/v2/youtube/transcript?video_url=dQw4w9WgXcQ&format=text&include_timestamp=false" \
-H "Authorization: Bearer YOUR_API_KEY"
  1. Implement retry logic with exponential backoff

    async function fetchWithRetry(url, options, maxRetries = 3) {
    const RETRYABLE_CODES = [408, 429, 503];
    for (let i = 0; i < maxRetries; i++) {
    try {
    const response = await fetch(url, options);
    // Return immediately if not a retryable error
    if (!RETRYABLE_CODES.includes(response.status)) {
    return response;
    }
    // Calculate delay: use Retry-After header or exponential backoff
    const retryAfter =
    response.headers.get("Retry-After") || Math.pow(2, i);
    console.log(
    `Retryable error ${response.status}, waiting ${retryAfter}s...`
    );
    await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));
    } catch (error) {
    // Network error - retry with backoff
    if (i === maxRetries - 1) throw error;
    await new Promise((resolve) =>
    setTimeout(resolve, Math.pow(2, i) * 1000)
    );
    }
    }
    throw new Error("Max retries exceeded");
    }
  2. Handle payment required errors gracefully

    • Redirect users to billing page
    • Show clear messaging about credit status
    • Provide action buttons for top-up
  • Cache successful responses to reduce API calls
  • Respect cache headers if provided
  • Consider transcript immutability (transcripts rarely change)
  • Implement cache invalidation for metadata
  • Monitor X-RateLimit-Remaining header
  • Implement request queuing when approaching limits
  • Use exponential backoff on 429 errors
  • Consider implementing client-side rate limiting
  • Never expose API keys in client-side code
  • Store API keys in environment variables
  • Use backend proxy for browser applications
  • Rotate API keys regularly
  • Use separate keys for different environments
  • Log all API responses including headers
  • Monitor credit usage patterns
  • Track rate limit approaches
  • Set up alerts for 402 and 429 responses
  • Monitor response times and cache hit rates

If you have questions or need assistance: