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.
Base URL
Section titled “Base URL”All API requests are made to:
https://transcriptapi.com/api/v2Endpoint
Section titled “Endpoint”The API provides a single endpoint:
GET /youtube/transcriptQuick Start
Section titled “Quick Start”Here’s how to make your first API request:
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 } ]}Authentication
Section titled “Authentication”The API uses Bearer token authentication. Include your API key in the Authorization header of every request:
Authorization: Bearer YOUR_API_KEYExample:
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.
Request Parameters
Section titled “Request Parameters”video_url (required)
Section titled “video_url (required)”The YouTube video URL or video ID to fetch transcripts for.
| Property | Value |
|---|---|
| Type | string |
| Pattern | ^([a-zA-Z0-9_-]{11}|https?://.\*)$ |
| Required | Yes |
Accepted Formats:
- Full YouTube URL:
https://www.youtube.com/watch?v=dQw4w9WgXcQ - Short YouTube URL:
https://youtu.be/dQw4w9WgXcQ - Video ID only:
dQw4w9WgXcQ
Examples:
# Full URL?video_url=https://www.youtube.com/watch?v=dQw4w9WgXcQ
# Short URL?video_url=https://youtu.be/dQw4w9WgXcQ
# Video ID only?video_url=dQw4w9WgXcQformat (optional)
Section titled “format (optional)”The output format for the transcript response.
| Property | Value |
|---|---|
| Type | string |
| Values | json, text |
| Default | json |
json: Returns structured data with transcript segmentstext: Returns plain text transcript
include_timestamp (optional)
Section titled “include_timestamp (optional)”Whether to include timestamps in the transcript output.
| Property | Value |
|---|---|
| Type | boolean |
| Default | true |
Behavior Matrix:
| Format | include_timestamp | Output |
|---|---|---|
json | true | Segments with text, start, duration |
json | false | Segments with only text |
text | true | Lines formatted as [123.45s] text |
text | false | Plain concatenated text |
send_metadata (optional)
Section titled “send_metadata (optional)”Whether to include video metadata in the response.
| Property | Value |
|---|---|
| Type | boolean |
| Default | false |
When enabled, includes:
title: Video titleauthor_name: Channel nameauthor_url: Channel URLthumbnail_url: Video thumbnail
Response Formats
Section titled “Response Formats”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" }}{ "video_id": "dQw4w9WgXcQ", "language": "en", "transcript": [ { "text": "Never gonna give you up" }, { "text": "Never gonna let you down" } ]}{ "video_id": "dQw4w9WgXcQ", "language": "en", "transcript": "[0.0s] Never gonna give you up\n[4.12s] Never gonna let you down\n[7.97s] Never gonna run around and desert you"}{ "video_id": "dQw4w9WgXcQ", "language": "en", "transcript": "Never gonna give you up Never gonna let you down Never gonna run around and desert you"}Response Headers
Section titled “Response Headers”| Header | Description |
|---|---|
X-Cache-Status | Cache status: HIT, PARTIAL-HIT, or MISS |
Credit Usage & Billing
Section titled “Credit Usage & Billing”Credit Cost
Section titled “Credit Cost”| Endpoint | Credits per Request | Notes |
|---|---|---|
GET /api/v2/youtube/transcript | 1 credit | Only charged on successful response (200) |
When Credits Are Charged
Section titled “When Credits Are Charged”- ✅ Successful requests (HTTP 200) - 1 credit
- ✅ Cached responses (HTTP 200) - 1 credit
- ❌ Failed requests (4xx, 5xx errors) - 0 credits
- ❌ Rate limited requests (HTTP 429) - 0 credits
Credits are deducted in real-time only when a transcript is successfully returned.
When credits are exhausted, the API returns HTTP 402 Payment Required.
Rate Limits
Section titled “Rate Limits”All API keys are subject to the following rate limits:
- 200 requests per minute per API key
- 2 concurrent requests maximum
Rate Limit Headers
Section titled “Rate Limit Headers”Each response includes rate limit information in the headers:
| Header | Description |
|---|---|
X-RateLimit-Limit | Total allowed requests in the window |
X-RateLimit-Remaining | Remaining requests in the window |
X-RateLimit-Reset | UTC epoch seconds when the window resets |
Retry-After | Seconds until you can retry (only on 429) |
Example Headers:
X-RateLimit-Limit: 200X-RateLimit-Remaining: 195X-RateLimit-Reset: 1678901234Best Practices
Section titled “Best Practices”- Implement exponential backoff on 429 errors
- Respect the
Retry-Afterheader value - Cache responses when appropriate to reduce API calls
- Don’t retry failed requests more than 2 times within 3 seconds
- Monitor rate limit headers to avoid hitting limits
Error Handling
Section titled “Error Handling”The API uses standard HTTP status codes:
| Status Code | Meaning | Action |
|---|---|---|
200 | Success | Transcript returned, 1 credit charged |
400 | Bad Request | Check your request parameters |
401 | Unauthorized | Invalid or missing API key |
402 | Payment Required | No credits remaining, add more credits |
404 | Not Found | Video not found or transcript unavailable |
422 | Validation Error | Invalid YouTube URL or ID |
429 | Too Many Requests | Rate limit exceeded, retry after delay |
500 | Server Error | Contact support if persistent |
Error Response Format
Section titled “Error Response Format”All error responses follow this format:
{ "detail": "Human-readable error message", "code": "ERROR_CODE"}401 Unauthorized
Section titled “401 Unauthorized”Includes a WWW-Authenticate header:
WWW-Authenticate: Bearer402 Payment Required
Section titled “402 Payment Required”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" }}Code Examples
Section titled “Code Examples”Here are complete examples in multiple languages:
# Basic requestcurl -X GET "https://transcriptapi.com/api/v2/youtube/transcript?video_url=dQw4w9WgXcQ" \ -H "Authorization: Bearer YOUR_API_KEY"
# With all parameterscurl -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 timestampscurl -X GET "https://transcriptapi.com/api/v2/youtube/transcript?video_url=dQw4w9WgXcQ&format=text&include_timestamp=false" \ -H "Authorization: Bearer YOUR_API_KEY"import requestsimport json
# ConfigurationAPI_KEY = "YOUR_API_KEY"BASE_URL = "https://transcriptapi.com/api/v2"
def get_transcript(video_url, format="json", include_timestamp=True, send_metadata=False): """Fetch YouTube video transcript"""
headers = { "Authorization": f"Bearer {API_KEY}" }
params = { "video_url": video_url, "format": format, "include_timestamp": include_timestamp, "send_metadata": send_metadata }
try: response = requests.get( f"{BASE_URL}/youtube/transcript", headers=headers, params=params )
# Check for errors response.raise_for_status()
# Parse JSON response data = response.json()
# Handle different formats if format == "json": transcript = data["transcript"] if include_timestamp: for segment in transcript: print(f"[{segment['start']}s] {segment['text']}") else: for segment in transcript: print(segment['text']) else: # Text format print(data["transcript"])
return data
except requests.exceptions.HTTPError as e: if e.response.status_code == 402: error_data = e.response.json() print(f"Payment required: {error_data['detail']['message']}") print(f"Action: {error_data['detail']['action_url']}") elif e.response.status_code == 429: retry_after = e.response.headers.get('Retry-After', '60') print(f"Rate limited. Retry after {retry_after} seconds") else: print(f"HTTP error: {e}") except Exception as e: print(f"Error: {e}")
# Example usageif __name__ == "__main__": # Basic usage get_transcript("dQw4w9WgXcQ")
# With metadata data = get_transcript( "https://www.youtube.com/watch?v=dQw4w9WgXcQ", send_metadata=True )
if data and "metadata" in data: print(f"Title: {data['metadata']['title']}") print(f"Author: {data['metadata']['author_name']}")// Using native fetch (Node.js 18+) or install node-fetch for older versionsconst API_KEY = 'YOUR_API_KEY';const BASE_URL = 'https://transcriptapi.com/api/v2';
async function getTranscript(videoUrl, options = {}) { const { format = 'json', includeTimestamp = true, sendMetadata = false } = options;
const params = new URLSearchParams({ video_url: videoUrl, format: format, include_timestamp: includeTimestamp, send_metadata: sendMetadata });
try { const response = await fetch( `${BASE_URL}/youtube/transcript?${params}`, { headers: { 'Authorization': `Bearer ${API_KEY}` } } );
if (!response.ok) { const error = await response.json();
if (response.status === 402) { console.error('Payment required:', error.detail.message); console.error('Action:', error.detail.action_url); } else if (response.status === 429) { const retryAfter = response.headers.get('Retry-After') || '60'; console.error(`Rate limited. Retry after ${retryAfter} seconds`); } else { console.error('API Error:', error.detail); }
throw new Error(error.detail); }
const data = await response.json();
// Process transcript based on format if (format === 'json' && includeTimestamp) { data.transcript.forEach(segment => { console.log(`[${segment.start}s] ${segment.text}`); }); }
return data;
} catch (error) { console.error('Error fetching transcript:', error); throw error; }}
// Example usage with async/await(async () => { try { // Basic usage const transcript = await getTranscript('dQw4w9WgXcQ'); console.log('Video ID:', transcript.video_id);
// With metadata const withMetadata = await getTranscript( 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', { sendMetadata: true } );
if (withMetadata.metadata) { console.log('Title:', withMetadata.metadata.title); console.log('Author:', withMetadata.metadata.author_name); }
// Text format without timestamps const plainText = await getTranscript('dQw4w9WgXcQ', { format: 'text', includeTimestamp: false }); console.log('Plain text:', plainText.transcript);
} catch (error) { // Error already logged }})();// Browser JavaScript with Fetch APIconst API_KEY = 'YOUR_API_KEY';const BASE_URL = 'https://transcriptapi.com/api/v2';
async function getYouTubeTranscript(videoUrl, options = {}) { const { format = 'json', includeTimestamp = true, sendMetadata = false } = options;
const params = new URLSearchParams({ video_url: videoUrl, format: format, include_timestamp: includeTimestamp, send_metadata: sendMetadata });
try { const response = await fetch( `${BASE_URL}/youtube/transcript?${params}`, { method: 'GET', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' } } );
if (!response.ok) { const error = await response.json();
// Handle specific error cases switch (response.status) { case 401: throw new Error('Invalid API key'); case 402: // Show payment required UI window.location.href = error.detail.action_url; break; case 429: const retryAfter = response.headers.get('Retry-After'); throw new Error(`Rate limited. Retry after ${retryAfter}s`); default: throw new Error(error.detail || 'API request failed'); } }
return await response.json();
} catch (error) { console.error('Transcript fetch error:', error);
// Display user-friendly error if (error.message.includes('Failed to fetch')) { throw new Error('Network error. Please check your connection.'); }
throw error; }}
// Example: Display transcript in DOMasync function displayTranscript(videoUrl) { const container = document.getElementById('transcript-container'); container.innerHTML = 'Loading transcript...';
try { const data = await getYouTubeTranscript(videoUrl, { sendMetadata: true });
// Display metadata if available if (data.metadata) { container.innerHTML = ` <h3>${data.metadata.title}</h3> <p>By: ${data.metadata.author_name}</p> `; }
// Display transcript const transcriptHtml = data.transcript .map(segment => ` <div class="transcript-segment"> <span class="timestamp">[${segment.start}s]</span> <span class="text">${segment.text}</span> </div> `) .join('');
container.innerHTML += `<div class="transcript">${transcriptHtml}</div>`;
} catch (error) { container.innerHTML = ` <div class="error"> Error: ${error.message} </div> `; }}
// UsagedisplayTranscript('dQw4w9WgXcQ');Best Practices
Section titled “Best Practices”Error Handling Strategies
Section titled “Error Handling Strategies”-
Implement retry logic with exponential backoff
async function fetchWithRetry(url, options, maxRetries = 3) {for (let i = 0; i < maxRetries; i++) {try {const response = await fetch(url, options);if (response.status !== 429) return response;const retryAfter =response.headers.get("Retry-After") || Math.pow(2, i);await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));} catch (error) {if (i === maxRetries - 1) throw error;}}} -
Handle payment required errors gracefully
- Redirect users to billing page
- Show clear messaging about credit status
- Provide action buttons for top-up
Caching Recommendations
Section titled “Caching Recommendations”- Cache successful responses to reduce API calls
- Respect cache headers if provided
- Consider transcript immutability (transcripts rarely change)
- Implement cache invalidation for metadata
Rate Limit Management
Section titled “Rate Limit Management”- Monitor
X-RateLimit-Remainingheader - Implement request queuing when approaching limits
- Use exponential backoff on 429 errors
- Consider implementing client-side rate limiting
Security
Section titled “Security”- 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
Monitoring and Logging
Section titled “Monitoring and Logging”- 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
Related Resources
Section titled “Related Resources”Need Help?
Section titled “Need Help?”If you have questions or need assistance:
- Visit our Contact page for support options
- Manage your account and billing at Billing