youtube-caption-extractor
Turn public YouTube videos into clean, timestamped transcripts.
Extract YouTube captions, subtitles, auto-generated transcripts, and video metadata
with a tiny TypeScript library: ~10.8 kB packed, ~31.1 kB unpacked.
Quickstart
· Live demo
· Sample app
· API
· Deployment notes
---
## Why use it?
- **One-call transcript extraction** — `getSubtitles()` returns timestamped segments ready for search, summarization, indexing, RAG, slide ready research notes, or export.
- **Metadata included** — `getVideoDetails()` returns title, description, and captions in one response.
- **Manual + auto captions** — prefers exact language matches, then gracefully falls back to available tracks.
- **Tiny install** — ~10.8 kB packed on npm, with only two runtime dependencies.
- **Runtime-friendly** — uses global `fetch`, with an optional custom transport for retries, caching, regional routing, or proxies.
- **Production-aware sample** — includes a Next.js demo plus a token-protected Cloudflare Container API.
## Built for
- YouTube caption extraction, subtitle extraction, and timestamped transcript data.
- AI summaries, search indexes, RAG pipelines, and agent workflows that need clean video text.
- Slide ready notes, presentation research, and content workflows built from public YouTube videos.
- Lightweight video metadata enrichment without pulling in a large SDK.
```ts
import { getSubtitles } from 'youtube-caption-extractor';
const subtitles = await getSubtitles({ videoID: '7GeFt8suV8E', lang: 'en' });
// → [
// { start: '1.12', dur: '4.56', text: 'This scraper can scrape almost anything' },
// { start: '3.36', dur: '5.84', text: 'on the internet and you will be' },
// { start: '5.68', dur: '6.64', text: 'surprised how easy it is to use it.' },
// ...
// ]
```
## Try it quickly
```sh
npm install youtube-caption-extractor
```
```ts
import { getVideoDetails } from 'youtube-caption-extractor';
const video = await getVideoDetails({
videoID: '7GeFt8suV8E',
lang: 'en',
});
console.log(video.title);
console.log(video.subtitles.map((s) => s.text).join('\n'));
```
Want to click around first? Try the hosted demo:
[youtube-caption-extractor.vercel.app](https://youtube-caption-extractor.vercel.app/).
Want a full app example? See [`sample/`](./sample), which includes:
- A polished Next.js UI
- Local API testing with your machine's network egress
- A Dockerized Hono API deployed through Cloudflare Containers
- A server-side token-protected proxy so the container API is not publicly open
## Installation
```sh
npm install youtube-caption-extractor
```
Requires **Node.js ≥ 18** when running on Node.js because the library uses the
global `fetch` API. It also works in Bun, Deno, Cloudflare Workers, and other
modern JavaScript runtimes that provide `fetch`. See
[Deployment notes](#deployment-notes) for tips on keeping calls reliable from
your runtime of choice.
## API
The library exports two functions and three types.
### `getSubtitles({ videoID, lang?, fetch? })`
Returns the caption track as an array of timed segments.
| Param | Type | Default | Notes |
| --------- | -------------- | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `videoID` | `string` | (required) | The 11-character YouTube video ID, e.g. `7GeFt8suV8E`. Not the full URL. |
| `lang` | `string` | `'en'` | ISO language code (`'en'`, `'es'`, `'fr'`, `'ja'`, …). Manual captions are preferred over auto-generated, and an exact match is preferred over a partial match. |
| `fetch` | `typeof fetch` | global `fetch` | Custom fetch implementation. Useful for adding caching, custom retries, or routing through a proxy. See [Customizing the transport](#customizing-the-transport). |
Resolves to `Subtitle[]`. Returns an empty array if the video plays but has no caption track in the requested language. **Throws** if the video is unavailable on any extraction path (see [Error handling](#error-handling)).
### `getVideoDetails({ videoID, lang?, fetch? })`
Same arguments as `getSubtitles`. Returns title, description, and the same subtitle array:
```ts
const details = await getVideoDetails({ videoID: '7GeFt8suV8E', lang: 'en' });
// → {
// title: 'Master Web Scraping with Firecrawl!',
// description: 'Get started with Firecrawl here: https://firecrawl.link/…',
// subtitles: [{ start: '1.12', dur: '4.56', text: 'This scraper can scrape almost anything' }, …],
// }
```
If subtitles fail to extract but the video metadata is available, `subtitles` will be an empty array and the call still resolves (rather than throwing). This way you can always show title/description even when captions aren't available.
### Types
```ts
interface Subtitle {
start: string; // Segment start time, seconds
dur: string; // Segment duration, seconds
text: string; // Decoded text content
}
interface VideoDetails {
title: string;
description: string;
subtitles: Subtitle[];
}
interface Options {
videoID: string;
lang?: string;
fetch?: typeof fetch;
}
```
All three are exported by name and can be imported directly:
```ts
import type {
Subtitle,
VideoDetails,
Options,
} from 'youtube-caption-extractor';
```
## Languages
The `lang` argument is a hint, not a strict filter. Track selection precedence:
1. **Manual captions in the requested language** (`vssId === '.