# YouTube Transcripts – Timed Captions and Full Text (`dougmanoel/youtube-transcripts`) Actor

Get the transcript of any YouTube video as timed segments and full text, in your language: manual captions first, auto-generated as fallback. No API key. A status row says why when a video has no captions.

- **URL**: https://apify.com/dougmanoel/youtube-transcripts.md
- **Developed by:** [Douglas Gouvea Manoel](https://apify.com/dougmanoel) (community)
- **Categories:** Videos, AI
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

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

## 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 – Timed Captions and Full Text

![YouTube Transcripts](https://errandcode.com/youtube-transcripts-cover.png)

[![Errand Code – YouTube Transcripts](https://img.youtube.com/vi/UtnMjzA6kbU/maxresdefault.jpg)](https://youtu.be/UtnMjzA6kbU)

Watch the demo: https://youtu.be/UtnMjzA6kbU

Part of Errand Code: https://errandcode.com/youtube-transcripts

Get the transcript of any YouTube video as clean dataset rows: **timed segments** (`start`, `duration`, `text`) and/or the **full text**, in the language you ask for. Manual captions are preferred over auto-generated ones. No login, no browser, no API key.

Videos with no captions, removed or private videos give you a free status row that says why.

Example, input `{"videos": ["https://www.youtube.com/watch?v=5MuIMqhT8DM"], "language": "en"}` returns (real run, 9 Nov 2026) the TED talk "Sleep Is Your Superpower" with `lengthSeconds: 1158`, `language: en`, `isAutoGenerated: false`, `languageMatched: true` and 327 segments, the first one `{"start": 0.829, "duration": 1.5, "text": "Thank you very much."}`.

### Input

| Field | Default | Notes |
|---|---|---|
| `videos` | one example | YouTube URLs (`watch`, `youtu.be`, `shorts`, `embed`, `live`) or 11-character ids. Up to 1000 per run. Duplicates are dropped. |
| `language` | `en` | `en`, `pt`, `pt-BR`, `es`... `pt` also matches `pt-BR`. |
| `outputFormat` | `both` | `both`, `segments` or `text`. |
| `proxyConfiguration` | `Residential (Apify Proxy)` | Residential by default. See *Why Residential proxy* below. |

Language choice: exact code first, then same base language; manual before auto-generated. If the video has nothing in your language you get its original-language track (the auto-generated one, or the first manual one) and `languageMatched` is `false`, so you can tell.

### Output

One row per video.

- `videoId`, `url`, `title`, `channel`, `channelId`, `lengthSeconds`
- `language`, `isAutoGenerated`, `languageMatched`, `requestedLanguage`
- `segments`: `[{ "start": 18.64, "duration": 3.24, "text": "..." }]` (seconds)
- `text`: all segments joined with spaces
- `status`: `ok`, or one of the free statuses below, plus `reason`
- `scrapedAt` (UTC)

Free status rows (`status` is not `ok`, `reason` explains):

| status | meaning |
|---|---|
| `no_captions` | the video has no captions, manual or auto-generated |
| `video_unavailable` | removed, private, members-only, region-locked, age-restricted... |
| `invalid_input` | the entry is not a YouTube video URL or id |
| `source_unavailable` | YouTube kept refusing after retries (check that the Residential proxy is on, or run again later) |

### Why Residential proxy is the default

From datacenter IPs, YouTube stops answering normally: the player endpoint starts replying `LOGIN_REQUIRED` (bot check) after one or two videos and caption downloads get HTTP 429. So this Actor sends both the player request and the caption download through Apify Proxy, and uses a fresh IP on each retry.

Our test on 9 Nov 2026, the same 20 videos (16 with captions, 3 without, 1 that does not exist; English, Portuguese, Spanish and Korean; from 19 seconds to 2 h 26 min):

| Proxy | Videos with captions delivered |
|---|---|
| None (Apify datacenter IP) | 1 of 16 |
| Datacenter (Apify Proxy) | 4 of 16 |
| **Residential (default)** | **16 of 16**, in 3 rounds out of 3 |

That is a small test (20 videos × 3 rounds), not a guarantee: we have not measured how YouTube reacts to hundreds of videos in one run. Residential traffic is part of your Apify platform usage (see Pricing). You can change the proxy in the input, but expect many `source_unavailable` rows without Residential.

### Pricing

**Early access: no Actor fee yet.** You pay only standard Apify platform usage, including the Residential proxy. In our tests on 9 Nov 2026 (3 rounds of 20 videos on Residential) platform usage averaged about US$ 0.40 per 1,000 videos (range US$ 0.21–0.51). Pay-per-result pricing may be introduced later; Apify notifies users 14 days in advance.

### Limits

- Live streams and premieres in progress usually have no captions yet.
- Translated captions (YouTube's auto-translate) are not returned, only tracks the video actually has.
- Captions are the video's own; this Actor does not transcribe audio.
- YouTube may change its clients without notice; the Actor may need an update when that happens.
- Scraping YouTube is subject to YouTube's terms: use reasonable volumes. Volume tested so far is small (20 videos per run).
- Not affiliated with YouTube or Google. Independent tool.

### Run locally

```bash
python3.13 -m venv .venv && .venv/bin/pip install -r requirements.txt -r requirements-dev.txt
echo '{"videos":["jNQXAC9IVRw"]}' | apify run --purge   # no proxyConfiguration needed locally
.venv/bin/python -m pytest -q
.venv/bin/ruff check .
```

### More from Errand Code

This Actor is built and maintained by [Errand Code](https://errandcode.com/youtube-transcripts). See the [other tools](https://errandcode.com/): an [Americanas Product Scraper](https://errandcode.com/americanas-product-scraper), a [Buscapé Price Scraper](https://errandcode.com/buscape-price-scraper), a [Google Trends Scraper](https://errandcode.com/google-trends-scraper), a [CNPJ Due Diligence](https://errandcode.com/cnpj-due-diligence) Actor, an AI visibility check ([GEO audit](https://errandcode.com/geo)), a Google Sheets add-on and MCP servers for AI agents. Not affiliated with YouTube or Google.

# Actor input Schema

## `videos` (type: `array`):

YouTube video URLs (watch, youtu.be, shorts, embed, live) or 11-character video ids. Up to 1000 per run.

## `language` (type: `string`):

Language code of the captions you want (en, pt, pt-BR, es...). Manual captions win over auto-generated ones. If the video has no captions in that language you get the original-language track and `languageMatched` is false.

## `outputFormat` (type: `string`):

Timed segments, plain text, or both.

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

Residential Apify Proxy by default. From datacenter IPs YouTube asks for login (LOGIN_REQUIRED) and rate-limits caption downloads (429); in our 9 Nov 2026 test (20 videos) 16 of 16 videos with captions worked on Residential, 4 of 16 on datacenter proxy and 1 of 16 with no proxy. Residential usage is billed as Apify platform usage.

## Actor input object example

```json
{
  "videos": [
    "https://www.youtube.com/watch?v=jNQXAC9IVRw"
  ],
  "language": "en",
  "outputFormat": "both",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# 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 = {
    "videos": [
        "https://www.youtube.com/watch?v=jNQXAC9IVRw"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("dougmanoel/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 = {
    "videos": ["https://www.youtube.com/watch?v=jNQXAC9IVRw"],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("dougmanoel/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 '{
  "videos": [
    "https://www.youtube.com/watch?v=jNQXAC9IVRw"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call dougmanoel/youtube-transcripts --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dougmanoel/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/CEq8LcO0bHfaTVDGJ/builds/eTcdPMalSmckOY2GG/openapi.json
