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

Extract transcripts, subtitles and captions from video URLs on 1,000+ platforms, including YouTube, Vimeo, TikTok, Instagram, X, Facebook, Twitch and TED. Native captions first, Whisper AI when none exist; output JSON, SRT, VTT, text or Markdown.

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

## Pricing

from $3.50 / 1,000 transcript extracteds

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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

### What is Video Transcript & Subtitle Extractor?

Extract transcripts, subtitles and captions from public video URLs across 1,000+ platforms, including YouTube, Vimeo, TikTok, Instagram, X, Facebook, Twitch, TED, Bilibili, SoundCloud, and Reddit. Each accepted URL produces one dataset record with the original URL, inferred platform, transcript content, and useful metadata such as title, channel, duration, language, transcript source, segment count, character count, and subtitle file keys when available. YouTube playlist and channel URLs are expanded into individual videos. Native captions come first; Whisper AI transcribes videos without captions when you opt in. This Actor is usable through Apify and Apify MCP, and it returns one record per video.

**Cost at a glance:** one `Transcript extracted` event per video with a transcript, plus `Whisper minute` events only for platform-funded Whisper audio (none when you bring your own OpenAI key), plus Apify platform usage. Only videos that return a transcript are billed as transcripts. See the Pricing tab for current prices.

### Best fit and connected workflows

This Actor fits workflows that start with a known public video URL and need structured transcript output. It routes naturally into:

- transcript indexing for search, embeddings, and knowledge bases
- content repurposing from talks, interviews, webinars, and podcasts
- accessibility file generation with SRT or VTT
- multilingual caption review for YouTube videos with captions
- AI agents that need one transcript record per input URL through Apify MCP
- metadata plus transcript extraction in one dataset row

### Practical scenario

Maya, a content analyst, has three public video URLs from YouTube and Vimeo. She wants the title, channel, duration, transcript source, language, and transcript text for each one. She runs this Actor with `outputFormat: "json"` and `includeMetadata: true`. In the dataset, she reviews the transcript segments, sees whether each record came from manual captions, auto captions, translated captions, or Whisper fallback, and then decides which videos to turn into an internal summary and which ones to archive with subtitle files.

### Input

| Field | Type | Description |
|---|---|---|
| `videoUrls` *(required)* | array of objects | Public video URLs. Each item is one video, or a YouTube playlist/channel URL that is expanded into videos. |
| `maxVideosPerPlaylist` | integer | For YouTube playlist or channel URLs, the number of newest listed videos to process. Default 20; `0` disables expansion. |
| `preferredLanguages` | array of strings | ISO 639-1 codes in priority order. The first available match is returned. Use `auto` to accept any language. |
| `outputFormat` | string | Transcript format in each result: `json`, `srt`, `vtt`, `text`, or `markdown`. |
| `useWhisperFallback` | boolean | Optional fallback when native captions are unavailable. Defaults to `false`; enable it only with an OpenAI key or when Whisper coverage and its cost are explicitly desired. |
| `whisperModel` | string | `auto` selects a supported managed Whisper route; explicit `whisper-1` keeps OpenAI. |
| `openaiApiKey` | string (secret) | Optional BYOK OpenAI API key for Whisper fallback billing through your OpenAI account. |
| `translateTo` | string | Optional ISO 639-1 code for YouTube caption translation. |
| `includeAutoCaptions` | boolean | Accept YouTube auto-generated captions. |
| `maxDurationMinutes` | integer | Set the maximum video length sent to Whisper. Native captions from longer videos are still extracted. |
| `perVideoTimeoutSecs` | integer | Time limit for one video (default 900 s). A slower video is skipped with a `video_timeout` note and the run moves on. |
| `includeMetadata` | boolean | Include title, channel, channel link and thumbnail in each result. |
| `includeFullMetadata` | boolean | Also return view count, like count, upload date, description and exact duration. Uses the slower full extraction route. Default off. |
| `proxyConfiguration` | object | Optional proxy settings. |

#### Focused JSON input example

```json
{
  "videoUrls": [
    {
      "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
    }
  ],
  "preferredLanguages": [
    "en",
    "es"
  ],
  "outputFormat": "json",
  "useWhisperFallback": false,
  "whisperModel": "auto",
  "openaiApiKey": "",
  "translateTo": "",
  "includeAutoCaptions": true,
  "maxDurationMinutes": 120,
  "perVideoTimeoutSecs": 900,
  "maxVideosPerPlaylist": 20,
  "includeMetadata": true,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

### Output

Each successfully extracted video produces one validated dataset record. Raw SRT and VTT files are also saved to the default key-value store. The output schema also exposes terminal run records for summary and downstream automation.

| Field | Type | Description |
|---|---|---|
| `videoUrl` | string | Original URL processed. |
| `platform` | string | Platform inferred from the URL. |
| `title` | string or null | Video title from platform metadata. |
| `channel` | string or null | Uploader or channel name. |
| `channelUrl` | string or null | URL of the uploader or channel page. |
| `durationSeconds` | number or null | Video duration in seconds. |
| `viewCount` | number or null | View count at extraction time. |
| `likeCount` | number or null | Like count at extraction time. |
| `uploadDate` | string or null | Upload date in `YYYYMMDD` format. |
| `thumbnail` | string or null | Thumbnail image URL. |
| `description` | string or null | Video description, truncated to 1000 characters. |
| `transcript` | array or string | Transcript in the requested format. JSON returns `{start, end, text}` segments. |
| `transcriptFormat` | string or null | Format used for the transcript field. |
| `transcriptSource` | string or null | Source of the transcript: `manual`, `auto`, `translated`, or `whisper`. |
| `language` | string or null | ISO 639-1 code of the returned transcript. |
| `isAutoGenerated` | boolean or null | Indicates whether captions were auto-generated or Whisper-transcribed. |
| `segmentCount` | number or null | Number of timestamped segments in the transcript. |
| `characterCount` | number or null | Total transcript character count. |
| `whisperMinutesCharged` | number or null | Confirmed units at delivery; null while owner speech billing is pending. See final terminal receipts below. |
| `srtKey` | string or null | Key for the saved SRT file in the default key-value store. |
| `vttKey` | string or null | Key for the saved VTT file in the default key-value store. |
| `playlistUrl` | string or null | Playlist or channel URL the video was expanded from, when the input was a YouTube playlist or channel. |
| `diagnostics` | string or null | Issue detail recorded for a specific URL. |

#### Sample output record (real run, transcript shortened)

The input was the TED channel URL with `maxVideosPerPlaylist: 2`, so the row carries `playlistUrl`.

```json
{
  "videoUrl": "https://www.youtube.com/watch?v=ohpIw6iGXNA",
  "platform": "youtube",
  "title": "Why I’m Building Libraries Inside Prisons | Reginald Dwayne Betts | TED",
  "channel": "TED",
  "channelUrl": "https://www.youtube.com/@TED",
  "durationSeconds": 749,
  "viewCount": 41793,
  "likeCount": 175,
  "uploadDate": "20261015",
  "thumbnail": "https://i.ytimg.com/vi_webp/ohpIw6iGXNA/maxresdefault.webp",
  "transcript": [
    {
      "start": 4.201,
      "end": 7.538,
      "text": "I ate my first mango in a hole."
    },
    {
      "start": 8.506,
      "end": 10.307,
      "text": "What some called a shoe,"
    }
  ],
  "transcriptFormat": "json",
  "transcriptSource": "manual",
  "language": "en",
  "isAutoGenerated": false,
  "segmentCount": 215,
  "characterCount": 8323,
  "whisperMinutesCharged": 0,
  "srtKey": "srt-youtube-ohpIw6iGXNA.srt",
  "vttKey": null,
  "playlistUrl": "https://www.youtube.com/@TED"
}
```

### How it works

The Actor expands YouTube playlist and channel URLs into individual videos and fetches YouTube captions through a fast route first (title, channel and thumbnail included; `transcriptSource` is `manual`, `auto` or `caption` when the source track cannot be told apart). If the fast route has no answer, or when you request translation or full metadata, it probes each video with the full extraction and prefers native captions first. If a probe is refused or times out, it retries once on a fresh residential session and then directly before recording the video in `FAILURES`. When multiple preferred languages are listed, it uses the first available match from the ordered list. When captions are available, it returns a validated transcript record and saves raw SRT and VTT files to the default key-value store—even when the video is longer than `maxDurationMinutes`. That cap applies only when no native captions are usable and Whisper fallback would incur per-minute transcription cost. The run also persists terminal outcome records such as COMPLETE, PARTIAL, CONFIG_ERROR, or UPSTREAM_FAILED, and stores per-URL diagnostics (for example `no_transcript_available`, `invalid_video_url`, `video_timeout`, `probe_failed`) in the `FAILURES` key-value record rather than the result dataset. Each video has its own time limit (`perVideoTimeoutSecs`), and the run stops cleanly before the platform timeout with a PARTIAL outcome that keeps every transcript already saved.

### What's new (November 2026)

- **Much faster YouTube runs.** Captions now come from a fast route first: 12 videos finished in about 30 seconds (the previous full extraction took 30-60 seconds per video). Playlists and channels are fetched in parallel, and the full extraction still runs automatically if the fast route has no answer.
- **New `includeFullMetadata` option** for views, likes, upload date, description and exact duration.
- **Cost example:** 100 YouTube videos with captions = 100 × `Transcript extracted` + 1 × `Actor start`. Videos without a transcript are not charged. Store discounts lower the per-video price on higher Apify plans.
- **Free plan:** speech fallback is limited to videos up to 10 minutes.

### Pricing

This Actor uses Apify Pay per event plus Apify platform usage. Open the live Pricing tab on the Actor page for the current charges and platform usage details.

| Job | Events charged |
|---|---|
| 10 videos, all with native captions | 10 × `Transcript extracted` + 1 × `Actor start` |
| 100 videos, 10 of them need Whisper (10 min each), platform-funded | 100 × `Transcript extracted` + 100 × `Whisper minute` + 1 × `Actor start` |
| Same job with your own `openaiApiKey` | 100 × `Transcript extracted` + 1 × `Actor start` (Whisper is billed by OpenAI to your key) |
| A playlist URL with `maxVideosPerPlaylist: 20` | up to 20 × `Transcript extracted` (only videos with a transcript) |

Videos that end without a transcript (no captions, private, removed, refused by the site or timed out) are not charged as `Transcript extracted`. Apify platform usage is charged on top of the events; check the Pricing tab for current prices before large runs.

### Use with AI agents (MCP)

This Actor is available as an Apify Actor usable through Apify MCP. The exact Actor identity is `khadinakbar/video-subtitle-extractor`.

Tool description: extract subtitles, captions, and AI transcripts from a supplied public video URL, then return one structured dataset record per video with transcript text, metadata, transcript source, and subtitle file references when available.

> Extract transcripts from these public video URLs and return one dataset row per URL. Prefer native captions first, then Whisper fallback when needed. Include transcript source, language, and subtitle file keys in the output.

Output interpretation:

- `transcript` is the primary content field.
- `transcriptSource` shows whether the record came from manual captions, auto captions, translated captions, or Whisper.
- `segmentCount` and `characterCount` help estimate transcript size.
- `srtKey` and `vttKey` point to subtitle files in the default key-value store.
- `diagnostics` is available when a URL produces a record with an issue detail.

Provenance and scope:

- The dataset contains one record per processed video URL.
- The transcript record keeps the original `videoUrl` and inferred `platform`.
- Raw subtitle files are stored separately in the key-value store.
- The output schema also exposes run summary records for terminal outcome handling.

Pagination and cost guidance:

- For multiple videos, pass each video as its own URL, or pass a YouTube playlist/channel URL with `maxVideosPerPlaylist`.
- The primary event is `Transcript extracted`.
- Whisper-minute billing applies only when platform-funded Whisper transcription is used.
- Check the live Pricing tab for the current Pay per event and platform usage details before large runs.

### JavaScript API example

```javascript
const { ApifyClient } = require("apify-client");

const client = new ApifyClient({
  token: process.env.APIFY_TOKEN,
});

(async () => {
  const run = await client.actor("khadinakbar/video-subtitle-extractor").call({
    videoUrls: [
      { url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ" }
    ],
    preferredLanguages: ["en"],
    outputFormat: "json",
    useWhisperFallback: false,
    includeMetadata: true
  });

  const dataset = client.dataset(run.defaultDatasetId);
  const { items } = await dataset.listItems();

  console.log("First record:", items[0]);
})();
```

### Best results and outcome guidance

Use one public URL per item. Put preferred caption languages in priority order so the Actor can return the first matching transcript. Keep `outputFormat: "json"` when you need segment-level timestamps for AI processing, and switch to `srt` or `vtt` when you need subtitle files for editing tools. Leave `includeMetadata` on when you want title and channel context alongside the transcript; turn on `includeFullMetadata` only when you need views, likes, upload date or description. Use `useWhisperFallback: true` when you want coverage for videos without native captions and have supplied `openaiApiKey` (or explicitly accept the platform Whisper cost). The default `false` keeps prefilled and native-caption runs credential-free; set `maxDurationMinutes` to control Whisper cost for longer videos.

### Continue the workflow

- Then use [Vimeo Video Downloader](https://apify.com/khadinakbar/vimeo-video-downloader) to extend Video Transcript & Subtitle Extractor with a neighboring creator and media research source when the brief calls for Vimeo data.
- Use [YouTube Transcript and Subtitle Data Scraper](https://apify.com/khadinakbar/youtube-transcript-extractor) when every URL is a YouTube video and you only need YouTube caption data.
- Then use [YouTube Comments Scraper](https://apify.com/khadinakbar/youtube-comments-scraper) to add audience comments and replies for the same YouTube videos.

### Design note

I found that the live output contract centers on a single transcript record per video URL, with `videoUrl` and `platform` required in every dataset item. That makes the dataset shape straightforward for batch processing and agent workflows.

### FAQ

#### Can I pass a playlist or channel URL?

Yes, for YouTube. A `youtube.com/playlist?list=…` URL or a channel URL (`youtube.com/@handle`, `/channel/…`, or its `/videos` or `/shorts` tab) is expanded into up to `maxVideosPerPlaylist` newest videos (default 20). Each output row carries `playlistUrl`. For other sites, pass individual video URLs.

#### Why is a video missing from the results, and was I charged?

Open the `FAILURES` key-value record. Each entry names the video and a reason such as `no_transcript_available`, `no_captions_and_no_whisper_key`, `invalid_video_url`, `probe_failed` or `video_timeout`. Videos without a transcript are not charged as `Transcript extracted`.

#### What happens with very long videos?

Native captions are extracted at any length. Whisper is only used up to `maxDurationMinutes`, and each video has a `perVideoTimeoutSecs` limit so the run keeps moving past one slow video.

#### Which transcript sources can appear in the dataset?

The dataset contract includes `manual`, `auto`, `translated`, and `whisper` as transcript sources.

#### What file formats are saved outside the dataset?

Raw SRT and VTT files are saved to the default key-value store, and the output schema exposes links for subtitle files and run summary records.

#### Can I use it with Apify MCP?

Yes. This Actor is built as an Apify Actor usable through Apify MCP, and the exact Actor identity is `khadinakbar/video-subtitle-extractor`.

#### How does it handle multiple caption languages?

Use `preferredLanguages` as a priority list. The Actor falls through the ordered list and returns the first available match.

### Responsible use

Use this Actor only with public video URLs and in ways that respect the source platform's terms, applicable rights, and local law. Review the transcript source and metadata before reusing content, especially when captions are auto-generated or when Whisper fallback is used.

#### Speech billing receipts

Native captions and your own OpenAI key report `whisperMinutesCharged: 0`. Owner-funded speech results report `whisperMinutesCharged: null` while post-delivery billing is pending, plus `whisperMinutesRequested` and `whisperBillingReceiptId`. The final `OUTPUT` and `RUN_SUMMARY` records contain `whisperBilling.receipts`: requested units, confirmed accepted units, completion, and any uncertainty. A charge limit or lost billing reply produces a partial outcome and stops further work; an uncertain unit is never treated as accepted or retried automatically. Result and speech prices are unchanged.

#### Requested caption translations

An existing manual caption track in the target language is preferred. Native YouTube translation keeps the original caption source and respects your preferred languages. Turning off automatic captions excludes speech-generated sources; a translation derived from manual captions remains a manual source.

When a requested YouTube translation fails, `useAiTranslationFallback` (default `true`) can translate complete available captions using OpenAI. Only caption text is sent; timings stay unchanged. `translationMethod` distinguishes `native` and `ai`. Set this option to `false` for native-only translation. No extra translation event or speech-minute fee is added; existing transcript and platform-usage pricing stays unchanged.

The AI route requires a verified creator-cost allowance and a verifiable target language. Long or otherwise unaffordable translations stop before the paid request. Partial, refused, wrong-language or uncertain results are not delivered or billed as transcripts. Provider failures may still incur a bounded creator cost; this route does not promise every language or length can fit the current price.

#### Automatic speech and exact model selection

Speech remains optional. Native captions are tried first. `whisperModel: "auto"` selects a supported multilingual Whisper route when you enable `useWhisperFallback`. Explicit `whisper-1` selects OpenAI and is never silently replaced. With your own OpenAI key, `auto` also resolves to `whisper-1`; your key is sent only to OpenAI. `speechModel` records the configured model used.

The existing transcript and managed speech-minute prices are unchanged. No managed speech-minute event is charged with your own OpenAI key. Audio duration is checked from the downloaded file, and complete timed segments are required before transcript delivery. If the selected managed model cannot serve the video's length at the current service price, choose Automatic speech or supply your own OpenAI key. Quota exhaustion, incomplete responses and uncertain attempts stop without a transcript result fee; requests are not automatically replayed. Files must fit the 25 MB upload limit. The managed Large V3 route supports up to 120 minutes per file, subject to available provider quota.

#### YouTube transfer limits

For each YouTube video using the Actor's configured connection routes, metadata, captions and retries share a transfer allowance. Native captions and caption translation share 8 MiB; managed speech uses 16 MiB for a maximum duration of three minutes or less, and 32 MiB for longer duration limits. A video that reaches the allowance stops with a failed-video diagnostic and no transcript result fee. Duration and file-size limits still apply separately.

Playlist/channel listing has a separate shared 8 MiB allowance across its discovery routes and retries. This discovery cost belongs to the collection and is not counted as a new revenue allowance for each child video. If listing reaches its allowance or cannot save its transfer checkpoint, its child videos are not admitted.

These guards preserve existing paths for a caller-supplied custom proxy or OpenAI key, which remain outside the transfer allowances above. Other video sites also retain their existing paths. Transfer counters describe observed traffic; they do not guarantee an invoice amount. Existing prices and included platform usage are unchanged.

When the current run confirms that platform usage is paid by the caller, managed YouTube extraction tries direct access first and can fall back to Apify's configured proxy. This also applies to playlist discovery. A caller's custom proxy or OpenAI key preserves its existing connection path. If the run's usage payer or platform share cannot be verified, managed YouTube work stops with `CONFIG_ERROR` before discovery, proxy allocation or video requests, with no transcript or managed speech-minute fee. Other video sites in a mixed request still run and their completed results are retained. Connection choice can change the total platform usage consumed; per-event prices and the included-usage setting stay unchanged.

`translateTo` still applies to YouTube captions, not to speech fallback. Native SRT/VTT/JSON behavior, output formats and timing preservation are unchanged.

# Changelog

This Actor's version history is a separate document: https://apify.com/khadinakbar/video-subtitle-extractor/changelog.md

# Actor input Schema

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

Public video URLs to extract transcripts and subtitles from, on 1,000+ supported sites (YouTube, Vimeo, TikTok, Instagram, X/Twitter, Facebook, Twitch, Dailymotion, TED, Bilibili, SoundCloud, Reddit and more). YouTube playlist and channel URLs are expanded into individual videos (see maxVideosPerPlaylist). NOT a search query — use full https:// URLs. Use public URLs without embedded usernames or passwords; local/private network targets are not accepted.

## `maxVideosPerPlaylist` (type: `integer`):

For YouTube playlist (youtube.com/playlist?list=…) or channel URLs (youtube.com/@handle, /channel/…, /videos, /shorts tabs), process up to this many newest listed videos. Each video with a transcript is billed as one transcript. Defaults to 20. Set 0 to disable expansion. Single video URLs are not affected.

## `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`):

When captions are unavailable, opt in to supported Whisper speech transcription. Managed speech retains the existing $0.02 per audio-minute service event; an OpenAI key waives that managed minute event. Native captions remain the default path. Speech quota, file-size and duration limits apply.

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

Select Automatic speech for a cost-controlled multilingual Whisper route. Explicit whisper-1 uses OpenAI and is never silently replaced. Your OpenAI key is used only with OpenAI; with that key, Automatic speech resolves to whisper-1. Managed model availability depends on video length, verified costs and provider quota.

## `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). Apify encrypts this secret in the run input. The Actor reads it during execution and does not copy it to result datasets, OUTPUT, RUN_SUMMARY or logs. The encrypted input follows Apify's storage-retention settings.

## `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`):

Maximum duration for Whisper AI fallback. Prevents runaway transcription costs on long podcasts or lectures. Native/manual/auto captions are still extracted from longer videos because they do not incur per-minute Whisper charges. Defaults to 120 minutes (2 hours). Set to 0 for no Whisper duration cap.

## `perVideoTimeoutSecs` (type: `integer`):

Maximum time spent on one video before it is skipped with a video_timeout note in the FAILURES record, so one slow or stuck video cannot use up the whole run. Defaults to 900 seconds. Raise it for very long Whisper transcriptions. Range 60–3600.

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

Add title, channel, channel link and thumbnail to each result. Turn off for transcript only. Views, likes, upload date, description and exact duration need the option below. Defaults to true.

## `includeFullMetadata` (type: `boolean`):

Also return viewCount, likeCount, uploadDate, description and exact durationSeconds. Uses the slower full extraction route, so runs take longer. Leave off for the fastest transcript-only output. Defaults to false.

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

Optional custom or Apify proxy settings. When left blank, the actor uses its managed mobile proxy. Supply your own proxy only when you need to control egress or billing.

## `useAiTranslationFallback` (type: `boolean`):

When an explicitly requested YouTube translation is unavailable, translate complete available captions with AI at a capped creator cost. Preserves all timings; adds no translation event fee. Disable for native-only translation. Unaffordable or unverifiable translations stop without a transcript fee.

## Actor input object example

```json
{
  "videoUrls": [
    {
      "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
    }
  ],
  "maxVideosPerPlaylist": 20,
  "preferredLanguages": [
    "en"
  ],
  "outputFormat": "json",
  "useWhisperFallback": false,
  "whisperModel": "auto",
  "translateTo": "",
  "includeAutoCaptions": true,
  "maxDurationMinutes": 120,
  "perVideoTimeoutSecs": 900,
  "includeMetadata": true,
  "includeFullMetadata": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "useAiTranslationFallback": true
}
```

# 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.

## `summary` (type: `string`):

Machine-readable terminal outcome and video counts.

## `runSummary` (type: `string`):

Alias of the terminal outcome at RUN_SUMMARY.

# 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": false
    }
};

// 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": False },
}

# 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(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": [
    {
      "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
    }
  ],
  "preferredLanguages": [
    "en"
  ],
  "outputFormat": "json",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call khadinakbar/video-subtitle-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,khadinakbar/video-subtitle-extractor"
        }
    }
}
```

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/9kBiJb4eoCmCWVNA2/builds/i3E4QKj8FGCBZlJlC/openapi.json
