# Video Transcript - Direct Video Files to SRT (`tidytools/video-transcript-files`) Actor

Transcribe spoken audio in direct MP4/MOV video files to text and timed SRT/VTT subtitles. Upload video or paste public file URLs. Free failed and silent files. Same engine and prices as Audio Transcription.

- **URL**: https://apify.com/tidytools/video-transcript-files.md
- **Developed by:** [Yukai Lin](https://apify.com/tidytools) (community)
- **Categories:** AI, Videos, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.80 / 1,000 audio minutes

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

## Video Transcript — Direct Video Files to SRT

### What does Video Transcript do?

Turn spoken audio in a video file into a timestamped transcript and downloadable SRT/VTT subtitles. Paste direct MP4/MOV file links or upload video files in Apify Console. This Actor uses the same transcription engine and prices as [Audio Transcription](https://apify.com/tidytools/audio-transcriber); this form starts with video files instead of a podcast feed. It listens to the audio track; it does not read words displayed in video frames.

### How to use

1. Replace the example under **Direct video file URLs** (`urls`) with links to videos you own or are permitted to process. A link must download without signing in; test it in a private browser window. If the file cannot be shared that way, use **Upload video files** (`audioFiles`) in Apify Console.
2. Leave **Save SRT and VTT subtitles** and **Save TXT and Markdown transcripts** on. Optionally choose a spoken language, or let the Actor detect it.
3. Set Apify's **Maximum cost per run** before starting. The prefilled archive.org speech video lasts about 500 seconds and, if transcribed, is billed as **9 audio minutes** ($0.054 on the Free/Bronze plan). Remove it when using your own files. The `podcastFeeds` prefill is empty, so the first form run sends only the MP4.
4. Start the run. Each processed video gets a Dataset row with `text`, timed `segments`, and `srtUrl`/`vttUrl` when subtitles were saved. Download the subtitle file from its URL; TXT and Markdown links are in the full Dataset row.

### Input example

Example direct file (checked with an anonymous HTTP Range request on 19 October 2026: `206 video/mp4`, 48,964,530 bytes):

```json
{
  "urls": ["https://archive.org/download/gerald-ford-inaugural-address-august-9-1974-720p/Gerald%20Ford%20inaugural%20address_%20August%209%2C%201974%20%28720p%29.mp4"],
  "podcastFeeds": [],
  "saveSubtitles": true,
  "saveTranscriptFiles": true
}
```

The engine has previously transcribed this Ford video; host access and speech recognition can still vary between runs. This variant has not yet had a live Apify run.

### Output example

The overview shows `url`, `mediaTitle`, `language`, `text`, `segments`, `srtUrl`, `vttUrl`, `billedMinutes`, `charged`, and `errorType`. A successful spoken video normally has one row and an SRT link. A silent video is a free row with `errorType: "no_text"`, `noSpeech: true`, and `charged: false`; it has no transcript to download. An invalid link is a free `invalid_input` row. A host that refuses access or returns a login/challenge page may produce a free `blocked` row with the reason in `error`; other failed downloads are also free. Inspect the full row for `error`, `downloadAttempts`, and other diagnostic fields.

### Pricing

Standard transcription uses the engine's `audio-minute` event: $0.006 per started audio minute on Free/Bronze, with no start fee. SRT, VTT, TXT, and Markdown saving add no charge. Optional speaker labels, translated subtitles, or AI summary use the engine's separate events and prices if enabled through advanced JSON. Silent, blocked, invalid, or otherwise failed rows have no audio-minute event. A 500.14-second video rounds up to 9 `audio-minute` events when transcription succeeds.

### Limitations

The original engine also accepts audio URLs, podcast feeds, dataset rows (`datasetItems` plus optional `datasetField`), and supported public video pages through advanced JSON. Hiding an entry from this focused form does not reject an otherwise valid engine input. For an automated run, explicitly set unused `urls` and `podcastFeeds` to `[]` so the example is not included. This Actor does not offer the caption retrieval workflow of [YouTube Transcript](https://apify.com/tidytools/youtube-transcript).

Use recordings you own or may transcribe, and follow each host's terms. For user supplied media files, the engine does not check robots.txt; it first identifies itself as TidyTools, and its default download setting may retry a refused file with a standard browser User-Agent. It never logs in, sends cookies, solves challenges, or uses stealth. Explicit blocks are reported without charge. You can set `downloadUserAgent: "tidytools"` to keep the TidyTools User-Agent on every attempt.

### FAQ

**Does this read text shown in video frames?** No. It transcribes the audio track.

### Optional content pack

Set `"contentPack":true` for chapter, show notes, X and LinkedIn drafts in `contentPack`. Add `"contentPackSubtitleLanguages":["Spanish"]` for one translated SRT/VTT language in `contentPack.translatedSubtitles` (the existing $0.002/started-minute subtitle event applies on success). Drafts require review before publishing. Off by default; existing output and charges stay the same. A failed pack has `contentPackError` and no pack charge. **Charge starts 2026-11-06 or later:** one $0.10 `content-pack` event per successful pack, in addition to existing audio-minute charges. Until the operator sets `CONTENT_PACK_CHARGE_FROM` to an announced date on or after 2026-11-06 and that date arrives, the text pack is free.

# Actor input Schema

## `contentPack` (type: `boolean`):

Off by default. Draft chapters, show notes, X and LinkedIn posts. One content-pack event at $0.10 per successful episode from 2026-11-06; free before then. Translated subtitles use the existing minute event.

## `contentPackSubtitleLanguages` (type: `array`):

One optional translated SRT/VTT language billed by the existing subtitle-language-minute event.

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

Paste publicly downloadable MP4, MOV or other video file URLs, one per line. Use files with spoken audio; silent videos return a free no_text row. The file must open without login in a private window. Replace the prefilled example before running your own files.

## `audioFiles` (type: `array`):

Upload your own video files in Apify Console when a public file URL is unavailable. Files are stored in your account key-value store. In API calls, send file URLs in urls.

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

ISO 639-1 code such as en, es, de, ja or zh (names such as English also work). Leave empty to detect automatically. YouTube links are always transcribed in the spoken language; when it differs from this, the row says so in languageNote.

## `saveSubtitles` (type: `boolean`):

Also save subtitle files for every transcript (no extra charge).

## `saveTranscriptFiles` (type: `boolean`):

Also save a plain-text (.txt) and a Markdown (.md) transcript for every file, linked as txtUrl and mdUrl, plus one combined file for the whole run when there are 2 or more transcripts (no extra charge).

## `maxMinutesPerFile` (type: `integer`):

Skip files longer than this. Skipped files get a result row with errorType "too_large" and are not charged. Empty = no limit (0 also means no limit). YouTube videos: empty = 240 minutes (the maximum).

## `maxTotalMinutes` (type: `integer`):

Stop charging for new files once this many audio minutes were transcribed. Files that would go over it get a row with errorType "limit_reached" and are not charged. Empty or 0 = no limit (your Apify spending limit still applies).

## `datasetField` (type: `string`):

Field that holds the media URL, e.g. audioUrl or media.url (a dot path, for nested fields). Leave empty to detect it (audioUrl, mediaUrl, videoUrl, url...). Used for the dataset above and for pasted dataset rows.

## `datasetItems` (type: `array`):

JSON array of rows from another Actor dataset, e.g. {"datasetItems":\[{"audioUrl":"https://example.com/episode.mp3"}]}. Each row needs a media URL field (audioUrl, mediaUrl, videoUrl, url, etc.), or set datasetField to its field name; dot paths work. A row may itself be a URL. Rows without usable links get free error rows with their index; duplicate media is transcribed once. Other source columns are not copied to results.

## `podcastFeeds` (type: `array`):

Other legal audio-transcriber inputs remain available through advanced JSON. Leave empty to process only video files.

## `transcriptionEngine` (type: `string`):

Choose "fast" (default, Cloudflare Workers AI Whisper) or "accurate" (self-hosted Whisper with more thorough decoding), e.g. {"transcriptionEngine":"accurate"}. Accurate is slower; busy pieces fall back to Fast and each result reports asrBackend. Both cost the same. Speaker labels use Deepgram; YouTube links use the YouTube service regardless of this setting.

## `downloadVia` (type: `string`):

Some file hosts refuse downloads from data centers or after many files in a row (HTTP 401/403/429 or a bot-check page). Auto then tries our backend (any size), our Oracle server (up to 15 MB) and our home line in Taiwan (up to 10 MB) before giving up. Each failed file is a free row that lists what was tried. No extra charge. We never use cookies or logins and never solve CAPTCHAs; when a host sends Retry-After we wait that long.

## `downloadUserAgent` (type: `string`):

Every file is first requested with our own User-Agent (TidyToolsTranscriber). Some hosts serve media files only to browsers: with Auto, a file that was refused is requested once more with a standard desktop browser User-Agent string (a plain header; no cookies, logins, CAPTCHA solving or browser automation). Choose TidyTools only to never switch. Only used for the audio/video file links you give.

## Actor input object example

```json
{
  "contentPack": false,
  "contentPackSubtitleLanguages": [],
  "urls": [
    "https://archive.org/download/gerald-ford-inaugural-address-august-9-1974-720p/Gerald%20Ford%20inaugural%20address_%20August%209%2C%201974%20%28720p%29.mp4"
  ],
  "saveSubtitles": true,
  "saveTranscriptFiles": true,
  "podcastFeeds": [],
  "transcriptionEngine": "fast",
  "downloadVia": "auto",
  "downloadUserAgent": "auto"
}
```

# Actor output Schema

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

No description

## `full` (type: `string`):

No description

## `subtitles` (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 = {
    "contentPackSubtitleLanguages": [],
    "urls": [
        "https://archive.org/download/gerald-ford-inaugural-address-august-9-1974-720p/Gerald%20Ford%20inaugural%20address_%20August%209%2C%201974%20%28720p%29.mp4"
    ],
    "podcastFeeds": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("tidytools/video-transcript-files").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 = {
    "contentPackSubtitleLanguages": [],
    "urls": ["https://archive.org/download/gerald-ford-inaugural-address-august-9-1974-720p/Gerald%20Ford%20inaugural%20address_%20August%209%2C%201974%20%28720p%29.mp4"],
    "podcastFeeds": [],
}

# Run the Actor and wait for it to finish
run = client.actor("tidytools/video-transcript-files").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 '{
  "contentPackSubtitleLanguages": [],
  "urls": [
    "https://archive.org/download/gerald-ford-inaugural-address-august-9-1974-720p/Gerald%20Ford%20inaugural%20address_%20August%209%2C%201974%20%28720p%29.mp4"
  ],
  "podcastFeeds": []
}' |
apify call tidytools/video-transcript-files --silent --output-dataset

```

## MCP server setup

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

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/jH39RULgdW1imNiDZ/builds/sWmPhVbnTeLBUUzk1/openapi.json
