# YouTube Transcripts & Subtitles (Timestamps, 20+ Languages) (`waseem9/youtube-transcripts`) Actor

Extract YouTube transcripts and subtitles with timestamps. One video or thousands, manual or auto-generated captions, language selection and translation. Clear per-video error rows instead of silent failures.

- **URL**: https://apify.com/waseem9/youtube-transcripts.md
- **Developed by:** [Waseem Ahmed](https://apify.com/waseem9) (community)
- **Categories:** Social media, Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 transcripts

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## YouTube Transcripts Scraper — Subtitles with Timestamps, Any Language

Extract the **full transcript of any YouTube video** — one video or thousands — with timestamps,
manual or auto-generated captions, language selection and optional translation. Built for RAG
pipelines, AI agents, content repurposing and research.

> **What this actor does:** it extracts the caption/subtitle track that YouTube already hosts for a
> video. It does **not** run speech-to-text on the audio. If a video has no captions at all, the row
> comes back with `status: "error"` and a clear reason.

### Why this one

Most transcript scrapers rely on a single YouTube client. When YouTube rate-limits that client you get
empty files or a run that silently "succeeds" with no text. This actor:

- **Rotates 4 YouTube clients** (Android VR, Android, iOS, Web) and retries across them per video.
- **Rotates proxy sessions** per video (Apify Proxy by default) so one blocked IP does not fail the run.
- **Never fails silently** — every requested video produces a row, with `status: "ok"` or
  `status: "error"` plus the exact reason.
- **Handles both caption formats** YouTube returns (srv3 XML and json3).
- **Keeps timestamps** so transcripts can be cited, seeked to, or turned into subtitle files.
- **Flags auto-generated captions** (`isAutoGenerated`) so you know the quality tier.
- **Translates** on request using YouTube's own caption translation.

### What you get

| Field | Description |
|---|---|
| `videoId` | 11-character YouTube video id |
| `url` | Canonical watch URL |
| `title`, `channel` | Video title and channel name |
| `durationSeconds`, `viewCount` | Video metadata |
| `language` | Language of the returned transcript |
| `isAutoGenerated` | `true` when the track is YouTube ASR |
| `translated` | `true` when a translation was requested |
| `availableLanguages` | Every caption language the video offers |
| `transcript` | Full transcript as one clean string |
| `segments` | `[{ start, duration, text }]` with second-level timestamps |
| `segmentCount` | Number of caption segments |
| `status` / `error` | `ok`, or `error` with the reason |
| `scrapedAt` | ISO timestamp |

### How to use

1. Paste video URLs (watch, `youtu.be`, Shorts, embed) or bare video IDs.
2. Pick your preferred languages, e.g. `en`, `es`, `de`. The actor uses the best available match.
3. Leave **Allow auto-generated captions** on unless you only want manual subtitles.
4. Run. Export as JSON, CSV or Excel, or pull the dataset through the API.

#### Example input

```json
{
  "videoUrls": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "https://youtu.be/jNQXAC9IVRw"
  ],
  "languages": ["en"],
  "includeAutoGenerated": true,
  "maxConcurrency": 5
}
```

#### Example output (trimmed)

```json
{
  "videoId": "jNQXAC9IVRw",
  "title": "Me at the zoo",
  "channel": "jawed",
  "language": "en",
  "isAutoGenerated": false,
  "segmentCount": 6,
  "transcript": "All right, so here we are, in front of the elephants...",
  "segments": [
    { "start": 1.2, "duration": 2.16, "text": "All right, so here we are, in front of the elephants" }
  ],
  "status": "ok"
}
```

### Use cases

- **RAG and AI agents** — index video content so an assistant can answer questions about it.
- **Content repurposing** — turn videos into blog posts, newsletters, threads or clip scripts.
- **Research** — analyse interviews, lectures, podcasts and conference talks at scale.
- **SEO** — publish searchable text versions of video content.
- **Accessibility and localisation** — generate subtitle bases and translated transcripts.
- **Competitive monitoring** — track what a channel says over time.

### Pricing

Pay per transcript via Apify's pay-per-event model: **$3 per 1,000 transcripts** ($0.003 each), plus a
negligible actor-start event. Failed rows and invalid inputs are **not** charged. Set a maximum charge
in the run options and the actor stops cleanly when it is reached.

### Run it with the API

```bash
curl -X POST "https://api.apify.com/v2/acts/waseem9~youtube-transcripts/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"videoUrls":["https://www.youtube.com/watch?v=dQw4w9WgXcQ"],"languages":["en"]}'
```

The synchronous endpoint returns dataset items directly, which makes this actor easy to wrap behind
your own REST endpoint or RapidAPI listing.

### Limitations (read before using)

- **Captions only.** No captions means no transcript. There is no audio transcription.
- **Auto-generated captions contain errors** (names, punctuation), which is why they are flagged.
- **Age-restricted or private videos** require authentication and will return an error row.
- Heavy use without a proxy can be rate-limited by YouTube; Apify Proxy is enabled by default.

### Notes for developers

- Built on the Apify SDK (Node 20), HTTP-only — no browser, so runs are fast and cheap.
- Pure parsing helpers live in `src/parse.js` and are covered by `npm test`.
- Client ladder lives in `src/youtube.js`; add a client there when YouTube changes a surface.

# Actor input Schema

## `videoUrls` (type: `array`):

Watch, Shorts, embed or youtu.be URLs, or bare 11-character video IDs. One row is produced per video.

## `languages` (type: `array`):

ISO language codes in priority order, e.g. en, es, de, fr, pt-BR. The actor uses the first available match, otherwise the video's default track.

## `includeAutoGenerated` (type: `boolean`):

When enabled, falls back to YouTube's auto-generated (ASR) captions if no manual captions exist. Auto-generated transcripts are flagged with isAutoGenerated: true.

## `translateTo` (type: `string`):

Optional ISO language code (e.g. es, de, ar). Uses YouTube's caption translation; leave empty to keep the original language.

## `maxConcurrency` (type: `integer`):

How many videos to fetch in parallel. Higher is faster but more likely to trigger YouTube rate limits.

## `proxyConfiguration` (type: `object`):

Apify Proxy is recommended. Datacenter proxies are usually enough; use residential if you see many blocked videos.

## `includeSegments` (type: `boolean`):

Keep the segments array (start, duration, text) in each row. Disable for smaller responses when you only need the full transcript text.

## Actor input object example

```json
{
  "videoUrls": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "https://youtu.be/jNQXAC9IVRw"
  ],
  "languages": [
    "en"
  ],
  "includeAutoGenerated": true,
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "includeSegments": true
}
```

# Actor output Schema

## `transcripts` (type: `string`):

All transcripts and per-video errors.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "videoUrls": [
        "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
        "https://youtu.be/jNQXAC9IVRw"
    ],
    "languages": [
        "en"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("waseem9/youtube-transcripts").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "videoUrls": [
        "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
        "https://youtu.be/jNQXAC9IVRw",
    ],
    "languages": ["en"],
}

# Run the Actor and wait for it to finish
run = client.actor("waseem9/youtube-transcripts").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "videoUrls": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "https://youtu.be/jNQXAC9IVRw"
  ],
  "languages": [
    "en"
  ]
}' |
apify call waseem9/youtube-transcripts --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,waseem9/youtube-transcripts"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/ZAevRbhayAU7e7eGq/builds/TCQmb4hRvYfw5DRCZ/openapi.json
