npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

caption-extractor

v2.1.1

Published

A simple and efficient package to scrape and parse captions (subtitles) from YouTube videos.

Readme

caption-extractor

npm version npm downloads License

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-extractor

Quick 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 to 500.
    • maxAttempts?: number: The maximum number of retry attempts for each network request. Defaults to 3.
  • Returns: A Promise that resolves to a SubtitlesResult object:
    • 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 as getSubtitles.
  • Returns: A Promise that resolves to a VideoDetails object:
    • videoId: string
    • title: string
    • description: string
    • channelName: string
    • duration: 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 values DEBUG, INFO, WARN, ERROR, NONE.
  • formatSrtTime(seconds: number): string: Formats a timestamp into HH:MM:SS,ms (SRT format).
  • formatVttTime(seconds: number): string: Formats a timestamp into HH: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:

  1. 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.
  2. 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:

  1. Fork the repository.
  2. Create a new branch (git checkout -b feature/my-feature).
  3. Make your changes.
  4. Run bun test and bun run lint to ensure code quality.
  5. Commit your changes and push to your branch.
  6. Open a pull request.

License

This project is licensed under the MIT License.