# Video to Text — Transcribe Any Video, $0.009/min (`lergassy/video-to-text`) Actor

\[$0.009/min] Turn any video into text: YouTube, TikTok, Instagram, X or Vimeo links, or MP4, MOV and WEBM files. Timed transcript in 90+ languages, optional translation to English, speaker labels and SRT subtitles. No API key, no per-run fee.

- **URL**: https://apify.com/lergassy/video-to-text.md
- **Developed by:** [Matvey](https://apify.com/lergassy) (community)
- **Categories:** Videos, AI, Developer tools
- **Stats:** 4 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $6.30 / 1,000 minute transcribeds

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 to Text** turns a video into a written transcript. Paste a link from YouTube, TikTok, Instagram, Facebook, X, Vimeo and other video sites, or a direct MP4, MOV or WEBM file, and get the spoken words back as text, with every passage tied to the second it was said. 90+ languages, videos of any length, no API key and no fee per run.

It is the [Speech to Text](https://apify.com/lergassy/speech-to-text) engine with the form set up for video; anything that works there works here, at the same price.

### Tested head-to-head — 9 October 2026

![Video to text Actors tested on a 1-minute MP4 file and a 19-second YouTube video: this Actor returned both word-perfect](https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/benchmarks/video-to-text-tested-2026-10-09.png)

The same two videos — a 1-minute MP4 file (a passage read aloud) and a 19-second YouTube video — sent to this Actor and to the five most-used video transcribers on the Apify Store (A–E, by monthly users), every Actor on its default settings.

| | This Actor | The five others |
|---|---|---|
| MP4 file: text | **Word-perfect** | 4 of 5 word-perfect; one lost 7 words |
| YouTube video: text | **Word-perfect** | 2 of 5 returned text, each with 3 words wrong; 3 returned nothing or an error |
| MP4 file: cost | $0.011 | $0.006–$0.058 |
| YouTube video: cost | $0.101 (incl. $0.09 per video link) | $0.015–$0.026 from the two that returned text |
| Time per run | 10.8 s (file), 28.0 s (YouTube) | 2.8–40.5 s (file), 23.4–46.3 s (YouTube, the two that returned text) |

A video page costs more here because its audio is pulled through a residential exit node; a direct file link never pays that fee.

### What it is for

Repurposing videos into articles and posts, searchable archives of webinars and lectures, quotes from interviews, subtitles, competitor and creator research, and AI agents that need to "watch" a video as text.

| What you give it | What you get back |
| --- | --- |
| A YouTube, TikTok, Instagram, X or Vimeo link | Transcript with timed passages |
| An MP4, MOV or WEBM file | The audio track is pulled out; the picture is discarded |
| A two-hour webinar | One transcript, timings continuous across parts |
| A video in any language | Text in that language, or translated to English |
| `subtitleFormat: "srt"` | A ready SRT file alongside the text |
| `chunkForRag: true` | Chunks with timecodes, ready to embed |

Video pages (YouTube, TikTok and the rest) cost an extra $0.09 per link because their audio is fetched through a residential exit node; a direct file link never pays it.

### What data does it return?

| Field | Example |
| --- | --- |
| `source`, `fileName` | `https://example.com/episode-12.mp3` · `episode-12.mp3` |
| `durationSeconds`, `durationMinutes` | `461.05` · `7.68` |
| `language`, `model`, `translatedToEnglish` | `English` · `fast` · `false` |
| `transcript`, `wordCount`, `charCount` | the full text · `1665` · `9218` |
| `segments` | `[{"start": 0, "duration": 4.56, "text": "…", "speaker": "Speaker 1"}]` |
| `speakerCount`, `speakers` | `3` · `["Speaker 1", "Speaker 2", "Speaker 3"]` |
| `chunks` | `[{"index": 0, "startTimecode": "00:00:00", "text": "…"}]` |
| `subtitles` | a complete SRT or WebVTT file |
| `status`, `errorCode`, `errorMessage` | `ok`, or why a file failed |

### How much does it cost?

| Event | Price | When it is charged |
| --- | --- | --- |
| Minute transcribed | $0.009 | Per started minute, using the built-in key |
| Minute with your own key | $0.004 | Per started minute when you supply a Groq key |
| Add-on: Speaker labels | $0.006 | Per started minute, only when speaker labels are on |
| File processed | $0.002 | Per file downloaded and prepared |
| Video link resolved | $0.09 | Per YouTube, TikTok, Instagram, X, Vimeo or other video page whose audio has to be pulled through a residential exit node. Direct file links never pay it |

A failed file comes back as an error row and is never charged. Larger monthly plans get 10–30 % off.

#### Bulk export

This Actor is built for bulk jobs — put hundreds of links into one run, or call it from the API on
a schedule. There is **no fee per run**: you pay per started minute of audio plus $0.002 per file,
and $0.09 only for a video page (not a direct file link) whose audio has to be fetched through a
residential exit node. Files that fail are returned as error rows and are free.

### ⬇️ Input

```json
{
  "urls": ["https://example.com/episode-12.mp3"],
  "quality": "accurate",
  "includeSegments": true
}
```

Speaker labels for a two-person interview:

```json
{ "urls": ["https://example.com/interview.m4a"], "diarize": true, "speakerCount": 2 }
```

#### Every input field

| Field | What it does |
|---|---|
| `urls` | Public links to audio or video files. Several at a time is fine. |
| `file` | An uploaded file instead of a link. |
| `quality` | `fast` for speed, `accurate` for difficult audio and non-English speech. |
| `language` | Name the spoken language instead of letting the model guess — faster and safer on short clips. |
| `translateToEnglish` | Write the text in English whatever was spoken. |
| `diarize`, `speakerCount` | Split the transcript by speaker. Give the count when you know it. |
| `vocabularyHint` | Names, brands and jargon, so they come out spelled your way. |
| `includeSegments` | Time-stamped passages alongside the full text. |
| `subtitleFormat` | `srt` or `vtt` to get a ready subtitle file. |
| `apiKey` | Your own Groq key — cuts the per-minute price by more than half. |
| `maxConcurrency` | Files processed in parallel. |
| `maxFileSizeMb`, `timeoutPerFileSecs` | Guard rails for very large or very slow downloads. |

### ⬆️ Output

```json
{
  "source": "https://example.com/episode-12.mp3",
  "fileName": "episode-12.mp3",
  "durationMinutes": 7.68,
  "language": "English",
  "transcript": "Welcome back to the show…",
  "wordCount": 1665,
  "segments": [{ "start": 0, "duration": 4.56, "text": "Welcome back to the show" }],
  "status": "ok"
}
```

### Use cases

#### Podcasts and shows

A transcript to pull quotes from, plus show notes and a searchable archive of every past episode.

#### Meetings and calls

Minutes that cite the minute something was said, and speaker labels so the actions land on the right person.

#### Lectures and webinars

Hours of talk turned into text you can search, instead of scrubbing a video for one sentence.

#### Compliance and records

A written record of calls kept alongside the audio, with timings that point back into the file.

#### Feeding audio into RAG

`chunkForRag` returns overlapping chunks with timecodes, sized for embeddings — no post-processing needed.

### 🤖 For AI agents and LLM apps

Compact reference for agents calling this Actor through the [Apify MCP server](https://mcp.apify.com) or the Apify API (`lergassy/video-to-text`).

**Purpose:** turn a recording into text with time-stamped passages, optionally split by speaker.

**Minimal input:**

```json
{ "urls": ["https://example.com/audio.mp3"] }
```

**Behaviours an agent should know:**

- Billing is **per started minute of audio**, not per row. A 40-minute file costs the same whether you keep the segments or not.
- `durationMinutes` on the row tells you what the run actually cost.
- Speaker labels are an extra per-minute charge. Turn `diarize` on only when who-said-what matters.
- Passing `speakerCount` when you know it makes the split noticeably cleaner.
- `language` is detected automatically, but naming it removes the guess on short or noisy clips.
- A file that cannot be downloaded or decoded returns a `status: "error"` row with the reason, free of charge — check `status` before reading `transcript`.
- Video files work: the audio track is extracted and the picture discarded.

### Use via API

Run this Actor from your own code or pipeline. Get your token in Apify Console → Settings → API & Integrations. Every input field below has the same name as in the JSON tab of the input form.

**Python**

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("lergassy/video-to-text").call(run_input={
    "urls": [
        "https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/audio-samples/sample-1min.mp4"
    ],
    "quality": "fast",
    "translateToEnglish": False,
    "diarize": False,
    "includeSegments": True,
    "chunkForRag": False,
    "chunkSize": 1200,
    "chunkOverlapSeconds": 0,
    "subtitleFormat": "none",
    "maxConcurrency": 3,
    "maxFileSizeMb": 500,
    "timeoutPerFileSecs": 600
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)
```

**JavaScript**

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });
const run = await client.actor('lergassy/video-to-text').call({
    "urls": [
        "https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/audio-samples/sample-1min.mp4"
    ],
    "quality": "fast",
    "translateToEnglish": false,
    "diarize": false,
    "includeSegments": true,
    "chunkForRag": false,
    "chunkSize": 1200,
    "chunkOverlapSeconds": 0,
    "subtitleFormat": "none",
    "maxConcurrency": 3,
    "maxFileSizeMb": 500,
    "timeoutPerFileSecs": 600
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

**cURL** (runs the Actor and returns the results in one call)

```bash
curl -X POST "https://api.apify.com/v2/acts/lergassy~video-to-text/run-sync-get-dataset-items?token=<YOUR_API_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/audio-samples/sample-1min.mp4"], "quality": "fast", "translateToEnglish": false, "diarize": false, "includeSegments": true, "chunkForRag": false, "chunkSize": 1200, "chunkOverlapSeconds": 0, "subtitleFormat": "none", "maxConcurrency": 3, "maxFileSizeMb": 500, "timeoutPerFileSecs": 600}'
```

### Limitations, stated plainly

- **Audio quality decides accuracy.** Overlapping speech, heavy accents and phone-line compression cost accuracy no model recovers fully.
- **Speaker labels are labels, not identities.** You get "Speaker 1" and "Speaker 2", never names — mapping them to people is your step.
- **Timings are passage-level**, not word-level.
- **Very large files need time.** Raise `timeoutPerFileSecs` for multi-hour recordings, and expect a long run.
- **Links must be public.** A file behind a login or an expiring signed URL cannot be fetched.

### Integrations

Runs from the Apify API and the Python and JavaScript clients, and from **n8n**, **Make** or **Zapier** through the Apify app. Point a webhook at the dataset to push transcripts into Notion, Google Docs or your own database, or let an agent call it through the Apify MCP server.

### ❓ FAQ

#### Do I need an API key or an account anywhere?

No. Recognition runs with a built-in key. Supply your own Groq key only if you want the cheaper per-minute rate.

#### Which languages are supported?

Around 90, including Russian, Indonesian, Spanish, German, French, Japanese and Chinese. Use `accurate` for non-English audio.

#### Can it handle a three-hour file?

Yes. Long files are processed in parts and the timings stay continuous across them.

#### Does it work with video?

Yes. Give it an MP4, MOV or WEBM link and the audio track is pulled out for you.

#### What happens if a file is unreachable?

You get an error row with the reason and no charge for that file. The other files in the run are still transcribed.

#### How accurate is it?

It runs Whisper large v3. On clean speech it is close to a careful human first pass; on overlapping or noisy audio it is not.

#### Can I get speaker names?

No — you get "Speaker 1", "Speaker 2" and so on. Naming them is your step.

#### How do I make it spell product names correctly?

Put them in `vocabularyHint`. The hint is given to the model before it starts.

### You might also like

| Actor | What it does |
|---|---|
| [Speech to Text](https://apify.com/lergassy/speech-to-text) | The main Actor: the same engine, framed as a speech-to-text API |
| [Audio Transcriber](https://apify.com/lergassy/audio-transcriber) | The same engine for podcasts, calls and meetings |
| [SRT Subtitle Generator](https://apify.com/lergassy/srt-subtitle-generator) | Ready subtitle files from audio or video |
| [YouTube Transcript Scraper](https://apify.com/lergassy/youtube-transcript-scraper) | Captions straight from YouTube, no transcription needed |
| [Document Text Extractor](https://apify.com/lergassy/document-text-extractor) | The same idea for PDFs and Word files |

### Also known as

People look for this Actor as video to text, video transcription, transcribe video to text, MP4 to text, YouTube video to text, TikTok video to text and a video transcription API.

### Notes

Recognition runs on Whisper large v3 with a built-in key, so there is nothing to sign up for. Choose **fast** (Whisper large v3 turbo, several times quicker) for everyday work, or **accurate** for accents, noise and translation; both cost the same per minute. A vocabulary hint helps with names and jargon.

# Changelog

This Actor's version history is a separate document: https://apify.com/lergassy/video-to-text/changelog.md

# Actor input Schema

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

Paste a link from <b>YouTube, TikTok, Instagram, Facebook, X, Vimeo, SoundCloud, Twitch, Dailymotion, VK, Rutube or Bilibili</b> — the audio track is resolved for you — or a direct file: MP3, M4A, WAV, FLAC, OGG, MP4, MOV, WEBM and anything else ffmpeg can read. Video is stripped to its audio track automatically.

## `file` (type: `string`):

Upload one audio or video file from your computer instead of giving a URL.

## `quality` (type: `string`):

Fast is the everyday choice. Accurate makes fewer mistakes on accents, noise and technical vocabulary, and costs more.

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

Two-letter code of the language spoken, for example <code>en</code>, <code>ru</code>, <code>id</code>. Leave empty to detect it automatically.

## `translateToEnglish` (type: `boolean`):

Return an English transcript no matter what language is spoken.

## `diarize` (type: `boolean`):

Mark who is speaking in each part of the recording. Every segment gets a speaker ("Speaker 1", "Speaker 2", …), the transcript is laid out as a conversation, and subtitles carry the names. Charged per minute on top of the transcription, and slow: the speaker model runs on the machine, so expect several minutes of processing per hour of audio, while a plain transcript is near-instant.

## `speakerCount` (type: `integer`):

How many people talk in the recording. Leave empty and the Actor works it out on its own; set it when you already know, for example two people in an interview.

## `vocabularyHint` (type: `string`):

Names, jargon or spellings that appear in the audio, comma separated. Helps the engine get proper nouns right.

## `includeSegments` (type: `boolean`):

Return every recognised passage with its start time and duration, alongside the full transcript.

## `chunkForRag` (type: `boolean`):

Split the transcript into retrieval chunks that keep the timecode where each one starts.

## `chunkSize` (type: `integer`):

Target size of one chunk. Chunks break on sentence ends, so real sizes vary.

## `chunkOverlapSeconds` (type: `integer`):

How many seconds of the previous chunk to repeat at the start of the next one.

## `subtitleFormat` (type: `string`):

Also return a ready-made subtitle file as text.

## `apiKey` (type: `string`):

Optional. Supply your own key and the per-minute price drops sharply; you then pay Groq directly for the recognition. Leave empty to use the built-in key with nothing to set up.

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

How many files to transcribe at the same time.

## `maxFileSizeMb` (type: `integer`):

Files larger than this are skipped with an error row instead of being downloaded.

## `timeoutPerFileSecs` (type: `integer`):

How long one file may take to download and recognise before it is given up on.

## Actor input object example

```json
{
  "urls": [
    "https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/audio-samples/sample-1min.mp4"
  ],
  "quality": "fast",
  "translateToEnglish": false,
  "diarize": false,
  "includeSegments": true,
  "chunkForRag": false,
  "chunkSize": 1200,
  "chunkOverlapSeconds": 0,
  "subtitleFormat": "none",
  "maxConcurrency": 3,
  "maxFileSizeMb": 500,
  "timeoutPerFileSecs": 600
}
```

# Actor output Schema

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

Dataset with one row per audio or video file.

## `transcriptsInConsole` (type: `string`):

Browse the transcripts in the Apify Console.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "urls": [
        "https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/audio-samples/sample-1min.mp4"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("lergassy/video-to-text").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "urls": ["https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/audio-samples/sample-1min.mp4"] }

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

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "urls": [
    "https://raw.githubusercontent.com/lergassy/apify-actor-assets/main/audio-samples/sample-1min.mp4"
  ]
}' |
apify call lergassy/video-to-text --silent --output-dataset

```

## MCP server setup

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

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/ikSo1tNe98icxDsiy/builds/TnTeAVyOArnLEkVFO/openapi.json
