caption-extractor
v2.1.1
Published
A simple and efficient package to scrape and parse captions (subtitles) from YouTube videos.
Maintainers
Readme
caption-extractor
A simple, efficient, and dependency-light package to scrape and parse captions (subtitles) and video details from YouTube.
It works in both Node.js and browser environments, uses YouTube's latest internal APIs, and includes a fallback mechanism for maximum reliability.
Features
- Fetch Subtitles: Get timed transcript segments for any public YouTube video.
- Get Video Details: Retrieve comprehensive video information, including title, description, duration, and channel name.
- Smart Fetching: Prioritizes the modern JSON-based transcript API and automatically falls back to the legacy XML caption system.
- Resilient: Includes automatic retries with configurable jitter to handle network issues gracefully.
- Language Support: Fetch captions for different languages.
- Universal: Works in Node.js and modern browsers (requires a CORS proxy).
- Lightweight: Minimal dependencies (
he,striptags) to keep your project lean. - Typed: Fully written in TypeScript with complete type definitions.
- URL & Time Utilities: Includes helpers for parsing YouTube URLs and formatting timestamps for formats like SRT and VTT.
Installation
# Using npm
npm install caption-extractor
# Using yarn
yarn add caption-extractor
# Using pnpm
pnpm add caption-extractor
# Using bun
bun add caption-extractorQuick Start
Get Subtitles for a Video
This is the simplest way to get a video's transcript.
import { getSubtitles, extractVideoId } from 'caption-extractor';
const videoUrl = 'https://www.youtube.com/watch?v=dQw4w9WgXcQ';
const videoId = extractVideoId(videoUrl); // 'dQw4w9WgXcQ'
if (videoId) {
try {
const { subtitles, metadata } = await getSubtitles({ videoId, lang: 'en' });
console.log('Subtitles fetched successfully!');
console.log(`Source: ${metadata.source}`); // 'transcript' or 'caption'
console.log(subtitles.slice(0, 5));
/*
[
{ start: 0.1, duration: 1.5, end: 1.6, text: "We're no strangers to love" },
{ start: 1.6, duration: 2.1, end: 3.7, text: "You know the rules and so do I" },
...
]
*/
} catch (error) {
console.error('Error fetching subtitles:', error);
}
}Get Full Video Details
Fetch metadata and subtitles in a single call.
import { getVideoDetails } from 'caption-extractor';
const videoId = 'dQw4w9WgXcQ';
try {
const details = await getVideoDetails({ videoId, lang: 'en' });
console.log(`Title: ${details.title}`);
console.log(`Channel: ${details.channelName}`);
console.log(`Duration (s): ${details.duration}`);
console.log(`Found ${details.subtitles.length} subtitle segments.`);
} catch (error) {
console.error('Error fetching video details:', error);
}API Reference
Main Functions
getSubtitles(options: Options): Promise<SubtitlesResult>
Fetches only the subtitles for a given YouTube video.
options: An object with the following properties:videoId: string: The 11-character YouTube video ID.lang?: string: The two-letter ISO 639-1 language code for the desired subtitles (e.g.,'en','es','de'). Defaults to'en'.requestJitter?: number: Maximum random delay (in ms) to add before each network request to prevent rate-limiting. Defaults to500.maxAttempts?: number: The maximum number of retry attempts for each network request. Defaults to3.
- Returns: A
Promisethat resolves to aSubtitlesResultobject:subtitles: TranscriptSegment[]: An array of subtitle objects.metadata: FetchMetadata: Metadata about the fetch process.
getVideoDetails(options: Options): Promise<VideoDetails>
Fetches all available details for a YouTube video, including its subtitles.
options: Same asgetSubtitles.- Returns: A
Promisethat resolves to aVideoDetailsobject:videoId: stringtitle: stringdescription: stringchannelName: stringduration: number(in seconds)language: string(the language code used)subtitles: TranscriptSegment[]metadata: FetchMetadata
Utility Functions
The package also exports several helpful utility functions.
extractVideoId(url: string): string | null: Extracts the video ID from various YouTube URL formats.detectUrlType(url: string): 'video' | 'playlist' | 'channel' | 'invalid': Detects the type of YouTube URL.setLogLevel(level: LogLevel): Sets the global log level for the library.LogLevel: An enum with valuesDEBUG,INFO,WARN,ERROR,NONE.
formatSrtTime(seconds: number): string: Formats a timestamp intoHH:MM:SS,ms(SRT format).formatVttTime(seconds: number): string: Formats a timestamp intoHH:MM:SS.ms(VTT format).
Error Handling
If an error occurs, the library throws a CaptionExtractorError. You can use a try...catch block to handle it and inspect the error code for specific scenarios.
import { getSubtitles, CaptionExtractorError, ErrorCode } from 'caption-extractor';
try {
const result = await getSubtitles({ videoId: 'invalidVideoId' });
} catch (error) {
if (error instanceof CaptionExtractorError) {
console.error(`Error Code: ${error.code}`);
console.error(`Title: ${error.title}`);
console.error(`Description: ${error.description}`);
if (error.code === ErrorCode.VIDEO_UNAVAILABLE) {
// Handle cases where the video is private, deleted, or region-locked
}
} else {
// Handle generic errors
console.error('An unexpected error occurred:', error);
}
}ErrorCode values:
YOUTUBE_API_ERROR: The internal YouTube API returned an error.VIDEO_UNAVAILABLE: The video is private, deleted, or otherwise inaccessible.CAPTION_XML_FETCH_FAILED: The fallback mechanism failed to fetch the caption file.
How It Works
This library fetches subtitles by mimicking a web browser's requests to YouTube's internal APIs. It employs a two-pronged strategy for maximum success:
- Modern Transcript API: It first attempts to use the modern transcript API, which provides clean, timed JSON data. This is the preferred method and works for most videos.
- Legacy Caption XML: If the Transcript API is unavailable or returns no data for the requested language, the library automatically falls back to fetching the legacy timed-text XML file.
This dual approach ensures that if captions are available in any form, caption-extractor can retrieve them.
Browser Usage
This library can run in a browser, but due to CORS (Cross-Origin Resource Sharing) policies, you cannot directly call youtube.com APIs from your own domain.
To use this in a browser, you must route API requests through a proxy server.
The proxy server will receive requests from your web app, forward them to https://www.youtube.com, and then return the response with the appropriate CORS headers (e.g., Access-Control-Allow-Origin: *).
Example of a proxied request:
- Without proxy (fails):
fetch('https://www.youtube.com/youtubei/v1/player?key=...') - With proxy (works):
fetch('https://my-proxy.example.com/youtubei/v1/player?key=...')
Setting up a CORS proxy is outside the scope of this library, but can be done easily with services like Cloudflare Workers, Vercel/Netlify Edge Functions, or a simple Express.js server.
Contributing
Contributions are welcome! Please follow these steps:
- Fork the repository.
- Create a new branch (
git checkout -b feature/my-feature). - Make your changes.
- Run
bun testandbun run lintto ensure code quality. - Commit your changes and push to your branch.
- Open a pull request.
License
This project is licensed under the MIT License.
