# Video Subtitle & Caption Extractor (`khadinakbar/video-subtitle-extractor`) Actor

Extract subtitles, captions, and AI transcripts from any video URL across 1000+ platforms (YouTube, Vimeo, TikTok, Instagram, X/Twitter, Facebook, Twitch, TED, Bilibili). Native captions first, Whisper AI fallback when none. JSON, SRT, VTT, text, or LLM-ready markdown.

- **URL**: https://apify.com/khadinakbar/video-subtitle-extractor.md
- **Developed by:** [Khadin Akbar](https://apify.com/khadinakbar) (community)
- **Categories:** Videos, AI, Social media
- **Stats:** 10 total users, 7 monthly users, 100.0% runs succeeded, NaN bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event + usage

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## Video Subtitle & Caption Extractor — 1000+ Sites + Whisper AI

**Extract subtitles, captions, and AI transcripts from any video URL across 1000+ platforms — YouTube, Vimeo, TikTok, Instagram, X (Twitter), Facebook, Twitch, Dailymotion, TED, Bilibili, SoundCloud, Reddit and more.** Native captions when available (cheap), Whisper AI fallback when not (accurate). Five output formats: timestamped JSON, SRT, VTT, plain text, and LLM-ready Markdown. No yt-dlp install, no FFmpeg setup, no API juggling — paste a URL and get a transcript.

### What does Video Subtitle & Caption Extractor do?

This Actor turns **any public video URL** into a clean, timestamped transcript. It tries the cheap path first — native human-uploaded captions, then YouTube auto-generated captions — and only falls back to **OpenAI Whisper** for the small share of videos where no captions exist. You stay in full control of cost: cap video length, disable Whisper entirely, or bring your own OpenAI key to bypass the per-minute charge.

Under the hood it uses **yt-dlp**, the same tool that powers most professional video pipelines, so it handles 1000+ sites including YouTube, Vimeo, Twitch, TikTok, Instagram, Facebook, X, Twitch VODs, Dailymotion, TED, Bilibili, BBC iPlayer, SoundCloud, Reddit, and dozens of regional platforms. The Apify platform handles proxy rotation, retries, scheduling, and storage — you just call the API or click Run.

Try it: paste a YouTube URL into the Input tab and hit **Start**. You'll get back a JSON record with title, channel, duration, full timestamped transcript, plus an SRT file in the key-value store.

### Why use Video Subtitle & Caption Extractor?

- **One tool, every video site** — stop maintaining 5 different scrapers for YouTube, TikTok, Instagram, Vimeo, and friends.
- **Smart pricing** — pay $0.005 per video that has captions; only pay the $0.02/min Whisper rate when there's no other option.
- **5 output formats** — JSON for AI pipelines, SRT/VTT for video editing, text for search/embeddings, Markdown for LLM context windows.
- **MCP-ready** — designed to be called by Claude, GPT, and other agents. The tool description and output shape are built for LLM consumption.
- **Translation built in** — set `translateTo: "es"` and YouTube's auto-translation kicks in for any video with captions.
- **Apify residential proxies** — geo-locked or rate-limited videos work out of the box.
- **No infrastructure** — no FFmpeg install, no yt-dlp updates to chase, no OpenAI account required (BYOK is optional).

Common use cases:

- **AI training data** — build transcript corpora at scale across mixed video sources.
- **Content repurposing** — turn podcasts, talks, and interviews into blog posts, X threads, and LinkedIn articles.
- **Accessibility** — generate WCAG-compliant SRT/VTT files for your video library.
- **Competitor / market research** — extract messaging from competitor ads, demos, and webinars.
- **SEO** — pull full transcripts of YouTube videos to repurpose into search-indexed long-form pages.
- **Localization** — translate captions to ship videos in multiple languages without re-recording.

### How to use Video Subtitle & Caption Extractor

1. Open the Actor and click **Try for free**.
2. Paste one or more video URLs into the **Video URLs** field. Any public URL works — YouTube, Vimeo, TikTok, Instagram, X, etc.
3. (Optional) Pick your **output format** — JSON for AI/programmatic use, SRT for video editors, Markdown for LLM context.
4. (Optional) Add an **OpenAI API key** if you want to bypass the platform Whisper charge ($0.005 flat per video, you pay OpenAI directly).
5. Click **Save & Start**.
6. When the run finishes, open the **Output** tab to view results, or grab the **Dataset URL** from the Output schema to pull JSON/CSV/Excel via API.

That's it. The Actor handles language selection, proxy rotation, retries, and Whisper fallback automatically.

### Input

| Field | Type | Description |
|---|---|---|
| `videoUrls` *(required)* | array of `{url}` | Public video URLs. Any platform supported by yt-dlp (1000+). |
| `preferredLanguages` | array of strings | ISO 639-1 codes in priority order. Default `["en"]`. Use `"auto"` for any. |
| `outputFormat` | enum | `json` (default), `srt`, `vtt`, `text`, `markdown`. |
| `useWhisperFallback` | boolean | Transcribe with Whisper when no captions exist. Default `true`. |
| `openaiApiKey` | string (secret) | BYOK: bypass platform Whisper, pay OpenAI directly. |
| `translateTo` | string | ISO 639-1 code to auto-translate captions (YouTube only). |
| `includeAutoCaptions` | boolean | Accept auto-generated captions. Default `true`. |
| `maxDurationMinutes` | integer | Skip videos longer than this. Default `120`. |
| `includeMetadata` | boolean | Include title, channel, duration, etc. Default `true`. |
| `proxyConfiguration` | object | Defaults to Apify residential proxies. |

Example JSON input:

```json
{
  "videoUrls": [
    { "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ" },
    { "url": "https://www.tiktok.com/@scout2015/video/6718335390845095173" },
    { "url": "https://vimeo.com/76979871" }
  ],
  "preferredLanguages": ["en", "es"],
  "outputFormat": "json",
  "useWhisperFallback": true,
  "maxDurationMinutes": 60
}
````

### Output

Each video produces one dataset record. Download as JSON, CSV, Excel, or HTML from the Output tab, or stream via the Apify API.

```json
{
  "videoUrl": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "platform": "youtube",
  "title": "Rick Astley - Never Gonna Give You Up",
  "channel": "Rick Astley",
  "channelUrl": "https://www.youtube.com/@RickAstleyYT",
  "durationSeconds": 213,
  "viewCount": 1700000000,
  "likeCount": 18000000,
  "uploadDate": "20091025",
  "thumbnail": "https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg",
  "transcript": [
    { "start": 18.96, "end": 22.96, "text": "We're no strangers to love" },
    { "start": 22.96, "end": 26.96, "text": "You know the rules and so do I" }
  ],
  "transcriptFormat": "json",
  "transcriptSource": "manual",
  "language": "en",
  "isAutoGenerated": false,
  "segmentCount": 87,
  "characterCount": 2104,
  "whisperMinutesCharged": 0,
  "srtKey": "youtube-dQw4w9WgXcQ.srt",
  "vttKey": "youtube-dQw4w9WgXcQ.vtt"
}
```

Raw `.srt` and `.vtt` files for each video are also saved to the default key-value store — fetch them at `KEY_VALUE_STORE_URL/keys/{srtKey}`.

#### Data table

| Field | Description |
|---|---|
| `videoUrl` | Original URL processed. |
| `platform` | Inferred platform (youtube, vimeo, tiktok, instagram, etc.). |
| `title`, `channel`, `channelUrl` | Video metadata. |
| `durationSeconds` | Length in seconds. |
| `viewCount`, `likeCount`, `uploadDate`, `thumbnail`, `description` | Engagement + presentation metadata. |
| `transcript` | Array of `{start, end, text}` (json) or formatted string (srt/vtt/text/markdown). |
| `transcriptFormat` | Echoes the input `outputFormat`. |
| `transcriptSource` | `manual` (human captions), `auto` (auto-captions), `translated`, or `whisper`. |
| `language` | ISO 639-1 code of the returned transcript. |
| `isAutoGenerated` | True if captions were auto-generated or Whisper-transcribed. |
| `segmentCount`, `characterCount` | Quick sizing for downstream pipelines. |
| `whisperMinutesCharged` | Minutes that contributed to the bill. 0 when captions were used or BYOK key supplied. |
| `srtKey`, `vttKey` | Keys in the key-value store for raw subtitle files. |
| `error` | Populated when extraction failed (with a hint to fix). |

### Pricing — How much does it cost to extract video subtitles?

Pay-per-event, capped at three line items so cost is predictable:

| Event | Price | When charged |
|---|---|---|
| Actor start | $0.001 / GB RAM | Once per run start. |
| Transcript extracted | $0.005 | Per video where a transcript was returned (any source). |
| Whisper minute | $0.02 | Per minute when platform Whisper is used. **Not charged with BYOK key.** |

**Typical run costs:**

- 1 YouTube video with captions: **~$0.006**
- 1 TikTok video without captions, 30s, platform Whisper: **~$0.026**
- 10 mixed YouTube/Vimeo videos, all with captions: **~$0.05**
- 100 podcast episodes, 30 min each, BYOK Whisper: **$0.50** (you also pay OpenAI ~$18 directly)

Bring your own OpenAI key to drop platform Whisper charges to zero — you'll pay OpenAI directly at ~$0.006/min.

### Tips & advanced options

- **Cap cost on long videos** — keep `maxDurationMinutes` at the default 120, or lower it to 30 for tight budgets when running mixed-length playlists.
- **Reduce noise** — set `includeAutoCaptions: false` to require human-reviewed captions only (lower coverage, higher quality).
- **Translation** — `translateTo: "es"` works on any YouTube video with captions, native or auto. Whisper itself does not translate — set the OpenAI Whisper-1 model and use a separate translation step if your source is non-YouTube.
- **Memory** — default is 2 GB. Bump to 4 GB if you're feeding very long videos through Whisper in parallel.
- **Datacenter proxy** — switch from residential to datacenter on cheap, uncontested sites (TED, Vimeo public) to drop proxy costs.
- **MCP usage** — call this Actor as `apify--video-subtitle-extractor` in Apify MCP. Works out of the box with Claude Desktop, Cursor, and any Streamable HTTP MCP client.

### FAQ, disclaimers, and support

**Which platforms are supported?** Anything yt-dlp supports — see the [yt-dlp supported sites list](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md). 1000+ sites including all major social and video platforms.

**What's the difference between this and the YouTube-only transcript actors?** Multi-platform support, smart caption-first pricing, MCP-ready descriptions, 5 output formats, and Whisper fallback all in one place. Single-platform actors (`khadinakbar/youtube-transcript-extractor` for YouTube only) remain cheaper if you exclusively need one platform.

**What happens if a video is private or geo-locked?** The Actor returns an `error` field with a message explaining the issue. Try a different `proxyConfiguration.countryCode` for geo-locked content.

**Can I scrape playlists or channels?** Not directly in v0.1 — pass individual video URLs. Pair with `khadinakbar/youtube-shorts-scraper` or similar for URL discovery, then feed URLs here.

**Will Whisper work on every site?** Yes — if yt-dlp can reach the audio, Whisper transcribes it. The 25 MB Whisper file size cap means very long high-bitrate sources may be skipped. Reduce duration or accept lossier audio formats.

**Legal disclaimer:** This Actor extracts publicly accessible subtitle data. It is your responsibility to comply with each platform's Terms of Service and your local copyright laws. Do not extract content you do not have rights to use. The Actor does not bypass paywalls, age gates, or login walls.

**Found a bug or need a feature?** Open an issue on the Actor's **Issues** tab — bugs are usually fixed within a few days. Custom builds (private platforms, bulk channel ingestion, real-time webhooks) available on request.

### Related Actors in this portfolio

- [`youtube-transcript-extractor`](https://apify.com/khadinakbar/youtube-transcript-extractor) — YouTube-only transcripts at $0.005/transcript, no Whisper.
- [`youtube-shorts-scraper`](https://apify.com/khadinakbar/youtube-shorts-scraper) — Discover YouTube Shorts URLs to feed into this extractor.
- [`tiktok-video-comments-scraper`](https://apify.com/khadinakbar/tiktok-video-comments-scraper) — Pair with this to get full TikTok engagement data + transcript.
- [`instagram-reels-scraper`](https://apify.com/khadinakbar/instagram-reels-scraper) — Discover Reel URLs at scale, then transcribe with this Actor.
- [`meta-ad-library-scraper`](https://apify.com/khadinakbar/meta-ad-library-scraper) — Pull competitor video ads, then run them through here for messaging analysis.

# Actor input Schema

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

Public video URLs to extract subtitles from. Works on 1000+ sites supported by yt-dlp (YouTube, Vimeo, TikTok, Instagram, X/Twitter, Facebook, Twitch, Dailymotion, TED, Bilibili, SoundCloud, Reddit, etc.). Each item is one video — for playlists or channels, expand to individual video URLs first. NOT a search query — must be a full URL with https://.

## `preferredLanguages` (type: `array`):

ISO 639-1 language codes in priority order. The first available match is returned (e.g., \['en', 'es'] picks English if present, else Spanish). Use 'auto' to accept any language. Defaults to \['en']. NOT a translation target — for translation set 'translateTo'.

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

Format of the transcript field in each result. 'json' returns timestamped segments (best for AI/programmatic use). 'srt' or 'vtt' returns standard subtitle file format strings. 'text' is plain unformatted text. 'markdown' is LLM-ready with timestamps as headings. Defaults to 'json'. Raw .srt/.vtt files are also saved to the key-value store regardless of this setting.

## `useWhisperFallback` (type: `boolean`):

If a video has no native captions, transcribe its audio with OpenAI Whisper. Adds $0.02 per minute of audio to the run cost (vs $0.005 flat for caption extraction). Disable to skip videos that lack captions. Defaults to true.

## `whisperModel` (type: `string`):

Whisper model used for AI fallback transcription. 'whisper-1' is OpenAI's hosted model (fastest, best accuracy). Only used when 'useWhisperFallback' is true. Ignored otherwise.

## `openaiApiKey` (type: `string`):

Bring your own OpenAI API key to bypass the Whisper per-minute charge — you pay OpenAI directly (~$0.006/min) and the actor only charges $0.005 per video processed. Leave empty to use the platform's Whisper service ($0.02/min). Stored only for the duration of one run.

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

ISO 639-1 code to translate captions into using YouTube's auto-translation (e.g., 'es', 'fr', 'de'). Only works for YouTube videos with captions. Leave empty to keep the original language. NOT supported on Whisper fallback (Whisper detects language but does not translate).

## `includeAutoCaptions` (type: `boolean`):

Accept YouTube's automatic speech-recognition captions (lower quality but cover more videos). Set false to require human-uploaded captions only. Defaults to true.

## `maxDurationMinutes` (type: `integer`):

Skip videos longer than this many minutes. Prevents runaway costs on long podcasts/lectures when Whisper fallback is on. Defaults to 120 minutes (2 hours). Set to 0 for no cap.

## `includeMetadata` (type: `boolean`):

Add title, channel, duration, viewCount, uploadDate, thumbnail to each result. Disable for minimal output (transcript only). Defaults to true.

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

Apify proxy used to reach geo-locked or rate-limited videos. Defaults to residential proxies — switch to datacenter for cheaper runs on uncontested sites.

## Actor input object example

```json
{
  "videoUrls": [
    {
      "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
    }
  ],
  "preferredLanguages": [
    "en"
  ],
  "outputFormat": "json",
  "useWhisperFallback": true,
  "whisperModel": "whisper-1",
  "translateTo": "",
  "includeAutoCaptions": true,
  "maxDurationMinutes": 120,
  "includeMetadata": true,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

All extracted transcripts, one record per video URL.

## `subtitleFiles` (type: `string`):

Raw SRT and VTT files, one per video, available in the default key-value store.

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

// Run the Actor and wait for it to finish
const run = await client.actor("khadinakbar/video-subtitle-extractor").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": [{ "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ" }],
    "preferredLanguages": ["en"],
    "outputFormat": "json",
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("khadinakbar/video-subtitle-extractor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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": [
    {
      "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
    }
  ],
  "preferredLanguages": [
    "en"
  ],
  "outputFormat": "json",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call khadinakbar/video-subtitle-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=khadinakbar/video-subtitle-extractor",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Video Subtitle & Caption Extractor",
        "description": "Extract subtitles, captions, and AI transcripts from any video URL across 1000+ platforms (YouTube, Vimeo, TikTok, Instagram, X/Twitter, Facebook, Twitch, TED, Bilibili). Native captions first, Whisper AI fallback when none. JSON, SRT, VTT, text, or LLM-ready markdown.",
        "version": "0.2",
        "x-build-id": "i1XGF4kjiNtiqY1ZB"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/khadinakbar~video-subtitle-extractor/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-khadinakbar-video-subtitle-extractor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/khadinakbar~video-subtitle-extractor/runs": {
            "post": {
                "operationId": "runs-sync-khadinakbar-video-subtitle-extractor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/khadinakbar~video-subtitle-extractor/run-sync": {
            "post": {
                "operationId": "run-sync-khadinakbar-video-subtitle-extractor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "videoUrls"
                ],
                "properties": {
                    "videoUrls": {
                        "title": "Video URLs",
                        "type": "array",
                        "description": "Public video URLs to extract subtitles from. Works on 1000+ sites supported by yt-dlp (YouTube, Vimeo, TikTok, Instagram, X/Twitter, Facebook, Twitch, Dailymotion, TED, Bilibili, SoundCloud, Reddit, etc.). Each item is one video — for playlists or channels, expand to individual video URLs first. NOT a search query — must be a full URL with https://.",
                        "items": {
                            "type": "object"
                        }
                    },
                    "preferredLanguages": {
                        "title": "Preferred caption languages (priority order)",
                        "type": "array",
                        "description": "ISO 639-1 language codes in priority order. The first available match is returned (e.g., ['en', 'es'] picks English if present, else Spanish). Use 'auto' to accept any language. Defaults to ['en']. NOT a translation target — for translation set 'translateTo'.",
                        "default": [
                            "en"
                        ],
                        "items": {
                            "type": "string"
                        }
                    },
                    "outputFormat": {
                        "title": "Output format",
                        "enum": [
                            "json",
                            "srt",
                            "vtt",
                            "text",
                            "markdown"
                        ],
                        "type": "string",
                        "description": "Format of the transcript field in each result. 'json' returns timestamped segments (best for AI/programmatic use). 'srt' or 'vtt' returns standard subtitle file format strings. 'text' is plain unformatted text. 'markdown' is LLM-ready with timestamps as headings. Defaults to 'json'. Raw .srt/.vtt files are also saved to the key-value store regardless of this setting.",
                        "default": "json"
                    },
                    "useWhisperFallback": {
                        "title": "Use Whisper AI when no captions available",
                        "type": "boolean",
                        "description": "If a video has no native captions, transcribe its audio with OpenAI Whisper. Adds $0.02 per minute of audio to the run cost (vs $0.005 flat for caption extraction). Disable to skip videos that lack captions. Defaults to true.",
                        "default": true
                    },
                    "whisperModel": {
                        "title": "Whisper model",
                        "enum": [
                            "whisper-1"
                        ],
                        "type": "string",
                        "description": "Whisper model used for AI fallback transcription. 'whisper-1' is OpenAI's hosted model (fastest, best accuracy). Only used when 'useWhisperFallback' is true. Ignored otherwise.",
                        "default": "whisper-1"
                    },
                    "openaiApiKey": {
                        "title": "OpenAI API key (optional, BYOK)",
                        "type": "string",
                        "description": "Bring your own OpenAI API key to bypass the Whisper per-minute charge — you pay OpenAI directly (~$0.006/min) and the actor only charges $0.005 per video processed. Leave empty to use the platform's Whisper service ($0.02/min). Stored only for the duration of one run."
                    },
                    "translateTo": {
                        "title": "Translate captions to (optional)",
                        "type": "string",
                        "description": "ISO 639-1 code to translate captions into using YouTube's auto-translation (e.g., 'es', 'fr', 'de'). Only works for YouTube videos with captions. Leave empty to keep the original language. NOT supported on Whisper fallback (Whisper detects language but does not translate).",
                        "default": ""
                    },
                    "includeAutoCaptions": {
                        "title": "Include auto-generated captions",
                        "type": "boolean",
                        "description": "Accept YouTube's automatic speech-recognition captions (lower quality but cover more videos). Set false to require human-uploaded captions only. Defaults to true.",
                        "default": true
                    },
                    "maxDurationMinutes": {
                        "title": "Max video duration (minutes)",
                        "minimum": 0,
                        "maximum": 600,
                        "type": "integer",
                        "description": "Skip videos longer than this many minutes. Prevents runaway costs on long podcasts/lectures when Whisper fallback is on. Defaults to 120 minutes (2 hours). Set to 0 for no cap.",
                        "default": 120
                    },
                    "includeMetadata": {
                        "title": "Include video metadata",
                        "type": "boolean",
                        "description": "Add title, channel, duration, viewCount, uploadDate, thumbnail to each result. Disable for minimal output (transcript only). Defaults to true.",
                        "default": true
                    },
                    "proxyConfiguration": {
                        "title": "Proxy configuration",
                        "type": "object",
                        "description": "Apify proxy used to reach geo-locked or rate-limited videos. Defaults to residential proxies — switch to datacenter for cheaper runs on uncontested sites.",
                        "default": {
                            "useApifyProxy": true,
                            "apifyProxyGroups": [
                                "RESIDENTIAL"
                            ]
                        }
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
