# YouTube Captions API: Any Language, Timestamps (`deepmine/youtube-captions-api`) Actor

YouTube captions API: send video, channel, playlist or search inputs and get each video's captions as timed segments and full text, manual or auto-generated, in any language (translated with timings kept), with every caption track listed. JSON, CSV or SRT. No YouTube API key.

- **URL**: https://apify.com/deepmine/youtube-captions-api.md
- **Developed by:** [DeepMine](https://apify.com/deepmine) (community)
- **Categories:** Videos, AI, Social media
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.19 / 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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 Captions API: Any Language, Timestamps

YouTube captions API: send video, Shorts, channel, playlist or search inputs and get each video's captions back as timed segments (`start`, `end`, `text`) and full text, manual or auto-generated, in the video's own language or any other with timings kept, plus every caption track the video has and its title, views and channel. Call it from code or run it here.

| 🎬 Title | 🌐 Language | 🤖 Auto | 🧩 Segments | 🌐 Caption tracks |
|---|---|---|---|---|
| Why the Future is Human … Plus AI | Esther Perel and Sal Khan | English (auto-generated) | yes | 626 | 1 (en (auto)) |
| How to Use Tech to Outsmart Dictators | Leopoldo López | TED | English | no | 271 | 4 (ar, en, iw, es) |
| How 180 Strangers Built a Library Together | Rebecca Toh | TED | English | no | 188 | 6 (ar, my, en, iw, …) |

<sub>Collected 2026-10-07 with the prefilled input: the TED channel's 10 newest videos.</sub>

**$1.39 per 1,000 transcripts on Starter** ($1.59 Free, $1.29 Scale, $1.19 Business). The prefilled run (10 videos) costs about $0.014. Videos without captions are free.

### Call it from your code

Start a run and get the captions in one HTTP call (your Apify token from Settings, Integrations):

```bash
curl -X POST "https://api.apify.com/v2/acts/deepmine~youtube-captions-api/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"urls": ["https://youtu.be/iG9CE55wbtY"], "languages": ["en"]}'
```

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("deepmine/youtube-captions-api").call(
    run_input={"urls": ["https://www.youtube.com/@TED"], "maxVideos": 10, "languages": ["en"]})
for video in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(video["title"], video["transcript"][0])
```

Each row's `transcript` is a list of caption lines with `start`, `end` and `duration` in seconds; `captionTracks` lists every track the video has (language, auto-generated or not). The **💬 Captions** tab shows the language, segment count and caption tracks per video. Same engine as [YouTube Transcript API](https://apify.com/deepmine/youtube-transcript-scraper), which has every option in one form.

### What you get

- **The right language.** With no language set, you get the video's own spoken language (manual captions first, then auto-generated), never an alphabetically first translation or an auto-dub. A Korean-only video gets its Korean track, and an English talk YouTube auto-dubbed into 20 languages gets English. `isOriginalLanguage` on every row says whether the transcript is in the spoken language.
- **Any language, timings kept.** Set *Languages* and a video without captions in yours is machine-translated segment by segment (Google's engine), so `start`/`end` stay exact and the original text stays in `originalTranscriptText`. Caption tracks people made in your language always come first. Every row says how it got its language: `translationSource` is `human`, `machine` or `youtube` (empty for the video's own language). No extra cost.
- **Text plus timings:** `transcriptText` (plain), `transcript` (segments with `start`, `end`, `duration` in seconds and `text`), word-level times for auto captions on request, SRT or WebVTT as a ready string.
- **Video and channel details:** title, description, keywords, category, views, likes, the exact comment count, duration, exact publish time (UTC), the countries it plays in, unlisted and family-safe flags, every thumbnail size up to 1280x720, channel name, handle, URL, subscribers, an 800 px channel avatar, verified badge, and every caption track the video has (language, auto or not) plus the languages YouTube lists for auto-translation.
- **Many videos at once:** single videos, Shorts, a channel's videos, Shorts or live streams, playlists and YouTube searches, each capped by *Max videos* and optionally by a date range: *Published after* and *Published before* (each a date or a period like 7 days; a channel skips its newer videos for free and stops at its first older one). A video listed by several inputs is read and charged once.
- **Nothing silent:** videos without a transcript (no captions, private, age-restricted, removed) get a free row in the **No transcript** tab saying why. A "removed" or "unavailable" answer is checked on a second IP first, and when every video comes back without captions the run checks a video known to have them and fails loudly if YouTube's answer has changed.

No login, no cookies, no YouTube API key.

### Input

| Field | What it does |
|---|---|
| 🎬 YouTube links or video IDs | Videos (`watch?v=`, `youtu.be/`, `/shorts/`, an 11-character ID), channels (`@handle`, add `/shorts` or `/streams`) and playlists |
| 🔍 Search YouTube | Optional search words; the top videos are read |
| 📊 Max videos per input | Videos read from each channel, playlist or search; default 20 |
| 🌐 Transcript languages | Codes in order of preference (`en`, `es`, `pt-BR`). Empty = the video's own spoken language (never an auto-dub). Set a code to get a dub's or translation's transcript on purpose. Caption tracks people made in your language (common on TED and big channels) are returned whenever they exist; otherwise the captions are translated into the first language (see Translation) |
| 🔤 Translation | Machine translation (default): every segment translated, timings kept, `translationSource: machine`. If the translator fails or stalls, the row comes in the original language with `translationStatus: failed` and the reason, and after 3 failures in a row the run stops translating instead of waiting. YouTube's auto-translation: tried once per video, but YouTube refused it for automated downloads on 20 of 21 tries in our tests, so you mostly get the original language; after 5 refusals the run stops asking. None: the original captions |
| 📄 Output format | Plain text + segments (default), plain text only, + SRT, + WebVTT |
| 🎛️ Filters | Caption type (manual first, manual only or auto-generated only); Searches return Shorts only; Published after and Published before (channels, playlists and searches: a date like 2026-09-01, that day included, or a period like 7 days; video links are always read) |
| 🧾 Output | Likes, comments, date and channel stats (on by default, no extra cost); word-level timings |

### Ready-made examples

- [Get the transcripts of a YouTube channel's latest videos (Y Combinator)](https://apify.com/deepmine/youtube-transcript-scraper/examples/youtube-channel-transcripts), with our YouTube Transcript API
- [Scrape a YouTube channel's videos, views and likes (@MrBeast)](https://apify.com/deepmine/youtube-scraper/examples/youtube-channel-videos-scraper), with our YouTube Scraper
- [Scrape YouTube search results: the most viewed "AI agents" videos this month](https://apify.com/deepmine/youtube-scraper/examples/youtube-search-results-scraper), with our YouTube Scraper

Open one and press Try for free: it copies the input to your account, where you change the links or the words.

### Use it from an AI agent or the API

Connect the Apify MCP server at `https://mcp.apify.com?tools=deepmine/youtube-captions-api` (Claude, VS Code or any MCP client), or call the Actor through the Apify API with `urls` (video links or IDs, Shorts, channels, playlists) and/or `searchQueries`:

```json
{"urls": ["https://www.youtube.com/watch?v=iG9CE55wbtY", "https://www.youtube.com/@TED"], "maxVideos": 20, "languages": ["en"], "outputFormat": "segments"}
```

`maxVideos` caps each channel, playlist or search; `languages` picks or translates the transcript language; `outputFormat` is `segments` (default), `text`, `srt` or `vtt`. Each row has the whole transcript in `transcriptText`. With neither `urls` nor `searchQueries`, the run reads the 3 example videos.

### Use cases

- **AI and RAG:** feed whole transcripts with titles, channels and dates to an LLM, a vector store or a summarizer.
- **Subtitles:** download SRT or WebVTT files ready for a video editor, or translated subtitles with the original timings.
- **Content research:** read what a channel or a search says across 20, 200 or 2,000 videos, with views and likes next to each.
- **Repurposing:** turn talks, podcasts and tutorials into blog posts, notes or quotes with timestamps.
- **Language learning and accessibility:** the video's own language, or a translation that keeps every line aligned.

### Output fields (transcripts, in row order)

`thumbnail`, `title`, `transcriptSnippet`, `channelName`, `videoUrl`, `language`, `languageCode`, `originalLanguageCode` and `originalLanguage` (the video's spoken language), `isOriginalLanguage` (true when the transcript is in that language, false for a dub or translation), `isAutoGenerated`, `isTranslated`, `translationSource` (`human`, `machine`, `youtube`, or empty for the video's own language), `isMachineTranslated`, `translationStatus` (`ok`, `failed`, `notNeeded` when a track in your language exists, `notRequested`), `translationError` (why a translation failed; the row is then in the original language), `requestedLanguage`, `viewCount`, `likeCount`, `commentCount` (exact, from the first comments page; YouTube's rounded figure if that page doesn't answer in time), `durationSeconds`, `duration`, `publishedAt` (exact UTC time; the date alone if YouTube gives only that), `publishedTimestamp`, `category`, `segmentCount`, `wordCount`, `transcriptText`, `originalTranscriptText` (the text before translation on machine-translated rows; `null` on the others), `transcript` (unless plain text only), `srt` / `vtt` (when chosen), `availableLanguages`, `captionTracks` (language code, name, auto-generated or not, translatable or not), `translationLanguages`, `isTranslatable`, `channelHandle`, `channelUrl`, `channelId`, `channelSubscribers` (rounded, as YouTube shows it), `channelAvatar` (800 px), `isChannelVerified`, `description`, `descriptionSnippet`, `keywords`, `isLive`, `isPrivate`, `isUnlisted`, `isFamilySafe`, `isShortsEligible`, `allowRatings`, `isCrawlable`, `isUnpluggedCorpus`, `isOwnerViewing`, `isTvfilmVideo`, `uploadedAt`, `availableCountries`, `thumbnails`, `videoId`, `inputs`, `scrapedAt` (when that row was read). The thumbnail field holds the 1280x720 image when YouTube has one (HD uploads), else 480x360.

**No transcript tab (free):** `thumbnail`, `title`, `channelName`, `videoUrl`, `hasTranscript` (false), `status` (`noCaptions`, `noMatchingCaptions`, `private`, `ageRestricted`, `notFound`, `unavailable`, `liveNotReady`, `emptyCaptions`, `blocked`), `reason`, `availableLanguages`, `videoId`, `inputs`, `scrapedAt`.

Output tabs: **💬 Captions**, **📊 Overview** (thumbnail, title, transcript snippet, channel, link, language, views, likes, duration, date, words), **📈 Stats** (every count), **🌐 Languages** (language, original or not, auto or not, translated or not, every caption language the video has), **📝 Full Text** and **🚫 No transcript**. The run summary (OUTPUT) lists each input with its video count and status.

### Measured runs

Apify platform, 1,024 MB (the default), 2026-10-05:

| Input | Transcripts | Free rows (no captions) | Run time | Peak memory |
|---|---|---|---|---|
| TED channel (60 newest) + search "sourdough bread recipe" (60) | 118 | 2 | 14-21 s | 221 MB |
| MrBeast Shorts (40) + a 3Blue1Brown playlist, translated into Spanish, SRT | 48 | 2 | 9-10 s | 208 MB |
| TED channel (100 newest) | 99 | 1 | about 9.5-14 s | 245 MB |
| Veritasium channel (100 newest, ~30 min videos) | 100 | 0 | 22-31 s (3.2-4.5 videos/s) | 237 MB |

Every transcript was charged once (118, 48 and 99 charges). The first row arrived about 3 s after the Actor process started for video links, and 3.6-6.5 s for channels and searches (their video list is read first).

### How it gets past YouTube's bot check

YouTube refuses most datacenter IPs and serves the rest without limit, so the run looks for IPs that pass and keeps using them. A video that no datacenter IP will serve (datacenter IPs keep failing, or the video has waited a few seconds for one) is read through a residential IP instead: one small call of a few KB, included in the transcript price. The run goes back to datacenter as soon as one passes again.

**On Apify's Free plan** your runs get only 5 datacenter IPs (paid plans get 30 or more), and YouTube can refuse all 5. The run then reads videos through residential IPs, enough for a typical small run; a big Free-plan run can still stop with a clear message when that runs out. If YouTube refuses every IP for 5 videos in a row, the run stops and fails with a clear message instead of finishing with empty rows; run it again later (videos already delivered are never charged twice).

### Pricing

Pay per transcript: $1.59 per 1,000 on Free, $1.39 Starter, $1.29 Scale, $1.19 Business. No start fee. Videos without a transcript cost nothing. Your run's max cost is never exceeded: the run stops at it.

### Feedback

Missing a field or a language? Open an issue on the Actor page. Useful? A quick review helps a lot.

# Actor input Schema

## `urls` (type: `array`):

Videos, Shorts, channels (@handle; add /shorts or /streams for those tabs) or playlists, one per line, e.g. https://youtu.be/9bZkp7q19f0 or an 11-character ID. Empty, with no search = the 3 example videos.

## `maxVideos` (type: `integer`):

Each channel, playlist or search reads at most this many videos (newest first on a channel). A single video link is always one. You pay per transcript.

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

Language codes in order of preference, e.g. en, es, pt-BR. Captions people made in your language come first; otherwise the captions are translated (see translation). Empty = each video's spoken language, never an auto-dub.

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

Every row has the full plain text. Timed segments add start, end and text per caption line; SRT or WebVTT also add a ready subtitle file.

## `searchQueries` (type: `array`):

Words to search on YouTube, one search per line, e.g. python tutorial. The top videos of each search are read, up to maxVideos. Empty = no search.

## `translation` (type: `string`):

Used only when a video has no captions in your language. Machine translation keeps every timestamp, at no extra cost. YouTube's own auto-translation is mostly refused for automated downloads. None keeps the original.

## `captionType` (type: `string`):

Manual captions first, then auto-generated (default), or only one kind, e.g. Manual only for human-made captions.

## `searchShortsOnly` (type: `boolean`):

Search YouTube Shorts instead of regular videos. Applies to 🔍 Search YouTube only.

## `publishedAfter` (type: `string`):

Channels, playlists and searches read only videos newer than this date (e.g. 2026-09-01) or period (e.g. 7 days); a channel stops at its first older video. Video links are always read.

## `publishedBefore` (type: `string`):

Channels, playlists and searches read only videos published up to this date, that day included (e.g. 2026-08-31), or at least this old (e.g. 30 days). Video links are always read.

## `includeEngagement` (type: `boolean`):

Adds likeCount, commentCount, publishedAt and the channel's handle, subscribers, avatar and verified badge. One light extra request per video, no extra cost.

## `includeWordTimings` (type: `boolean`):

For auto-generated captions, adds the start time of every word inside each segment (a words list per segment).

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

How many videos are read at once.

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

Leave as is: Apify datacenter proxy. A video YouTube won't serve on datacenter IPs is read through a residential IP instead: one small call (a few KB), included in the transcript price.

## Actor input object example

```json
{
  "urls": [
    "https://www.youtube.com/@TED"
  ],
  "maxVideos": 10,
  "outputFormat": "segments",
  "translation": "machine",
  "captionType": "any",
  "searchShortsOnly": false,
  "includeEngagement": true,
  "includeWordTimings": false,
  "maxConcurrency": 32,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

No description

## `noTranscript` (type: `string`):

No description

## `summary` (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 = {
    "urls": [
        "https://www.youtube.com/@TED"
    ],
    "maxVideos": 10,
    "outputFormat": "segments"
};

// Run the Actor and wait for it to finish
const run = await client.actor("deepmine/youtube-captions-api").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 = {
    "urls": ["https://www.youtube.com/@TED"],
    "maxVideos": 10,
    "outputFormat": "segments",
}

# Run the Actor and wait for it to finish
run = client.actor("deepmine/youtube-captions-api").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 '{
  "urls": [
    "https://www.youtube.com/@TED"
  ],
  "maxVideos": 10,
  "outputFormat": "segments"
}' |
apify call deepmine/youtube-captions-api --silent --output-dataset

```

## MCP server setup

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

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/NT1UycXdUM4S3eSxK/builds/k9okyajIMCKUPaAuw/openapi.json
