# Audio to Text — Whisper Transcription, SRT, RAG Chunks (`saliga/audio-to-text`) Actor

Transcribe audio and video files, Drive or Dropbox links and podcast feeds with Whisper. Timestamped text, SRT and VTT subtitles, Markdown and RAG chunks in 99 languages. Failed files are free.

- **URL**: https://apify.com/saliga/audio-to-text.md
- **Developed by:** [Salih Küçükkayış](https://apify.com/saliga) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 audio minute (base)s

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

Transcribe audio and video to text with Whisper: podcasts, interviews, meetings, lectures, voice notes and video files. Give it file links, Google Drive or Dropbox share links, or a podcast RSS feed, and get one result per file with the full text, **timestamped segments**, **SRT and VTT subtitles**, **Markdown** and optional **RAG chunks with start and end times**. 99 languages, detected automatically, and translation to English.

**$5 per 1,000 audio minutes** with the fast model, **$10 per 1,000 minutes** with the accurate model, plus $0.005 per run at the default 4 GB memory ($0.00125 per GB). Minutes are counted per started minute, per file. **Files that fail, cannot be read or contain no speech are free.** No subscription, no API key, no platform usage fees.

### Quick start

Press **Start** with the example input. It transcribes a 6-minute LibriVox recording of a Mark Twain story:

```json
{
  "urls": [
    "https://archive.org/download/short_story_036_0904_librivox/shortstory036_sciencevsluck_gam_64kb.mp3"
  ]
}
```

Each file becomes one dataset row (real output of that run, text and lists shortened):

```json
{
  "url": "https://archive.org/download/short_story_036_0904_librivox/shortstory036_sciencevsluck_gam_64kb.mp3",
  "fileName": "shortstory036_sciencevsluck_gam_64kb.mp3",
  "fileSizeMb": 2.92,
  "model": "base",
  "task": "transcribe",
  "language": "en",
  "languageProbability": 0.998,
  "durationSec": 364.3,
  "transcribedSec": 364.3,
  "processingSec": 48.7,
  "billedMinutes": 7,
  "segmentCount": 85,
  "wordCount": 1005,
  "text": "Science vs. Luck by Mark Twain This is a LibriVox recording. All LibriVox recordings are in the public domain. ...",
  "segments": [
    { "start": 0, "end": 4.24, "text": "Science vs. Luck by Mark Twain" },
    { "start": 4.24, "end": 10.24, "text": "This is a LibriVox recording. All LibriVox recordings are in the public domain." }
  ],
  "srt": "1\n00:00:00,000 --> 00:00:04,240\nScience vs. Luck by Mark Twain\n\n2\n00:00:04,240 --> ...",
  "vtt": "WEBVTT\n\n00:00:00.000 --> 00:00:04.240\nScience vs. Luck by Mark Twain\n...",
  "markdown": "# shortstory036_sciencevsluck_gam_64kb\n\nSource: https://archive.org/... · Duration: 06:04 · Language: en\n\n**[00:00]** Science vs. Luck by Mark Twain ...",
  "files": {
    "srt": "https://api.apify.com/v2/key-value-stores/.../records/000-shortstory036-sciencevsluck-gam-64kb.srt?signature=...",
    "vtt": "...", "md": "...", "txt": "..."
  }
}
```

The 6-minute file took 49 seconds to transcribe with the base model; the whole run, start to finish, took 64 seconds.

### What it does

- **Inputs**: direct links to audio or video files (mp3, m4a, aac, wav, ogg, opus, flac, mp4, mov, webm, mkv), **Google Drive** and **Dropbox** share links (turned into download links for you), and **podcast RSS/Atom feeds** (the newest episodes, with their title and publish date). Many links per run.
- **Text**: the whole transcript as paragraphs, split at pauses.
- **Segments**: every sentence-sized piece with its start and end time in seconds. Turn on *Word timestamps* for the time of every word.
- **Subtitles**: SRT and WebVTT, inline in the result and as downloadable files.
- **Markdown**: a title, source, duration and language line, then paragraphs with a `[mm:ss]` timestamp each. Ready for notes apps and LLMs.
- **RAG chunks** (optional): set *RAG chunk size* and each result gets `chunks`, made of whole sentences, each with `startSec`, `endSec` and `startTime`. A search hit can link straight to the right moment of the recording. The same chunk format as [Document to Markdown](https://apify.com/saliga/document-to-markdown), so audio and documents can go into the same vector database.
- **Languages**: 99, detected automatically, or set the language code. *Translate to English* gives English text from any spoken language.
- **Silence** is skipped (voice activity detection), so long quiet stretches go fast and are less likely to turn into made-up text.

### Models

| Model | Best for | Speed (4 GB memory) | Price |
|---|---|---|---|
| **Base** (default) | clear speech in common languages: podcasts, lectures, voice notes | about 6 to 7 times faster than real time: 10 minutes of audio in about 1.5 minutes | $5 / 1,000 minutes |
| **Small** | names, accents, noisy recordings, less common languages | about 2 to 3 times faster than real time: the 6-minute example took 2 minutes 42 seconds | $10 / 1,000 minutes |

Both run on CPU inside the Actor (faster-whisper, int8). Nothing is sent to a third-party speech API.

### Cost examples

| Audio | Base | Small |
|---|---|---|
| 1 voice note, 1 minute | $0.010 | $0.015 |
| 1 podcast episode, 30 minutes | $0.155 | $0.305 |
| 1 meeting, 60 minutes | $0.305 | $0.605 |
| 10 episodes of 45 minutes, one run | $2.255 | $4.505 |

Set a spending limit on the run (*Max total charge* in Apify) and the Actor stops cleanly at it: the last file is cut at the last minute that still fits, and is marked `truncated` and `stoppedAtSpendingLimit`. *Max minutes per file* (default 180) caps each file.

### Use it from code, n8n, Make or an AI agent

Call it like any Apify Actor: the API, the JavaScript and Python clients, the Apify node in n8n or Make, or Apify's MCP server for AI agents.

```bash
curl -X POST "https://api.apify.com/v2/acts/saliga~audio-to-text/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["https://example.com/episode-42.mp3"], "chunkSize": 1500}'
```

`run-sync` waits up to 5 minutes, which is enough for about 25 minutes of audio with the base model. For longer files, start the run and read the dataset when it finishes (a webhook or polling the run).

### Output fields

| Field | Meaning |
|---|---|
| `url` | the link you gave (or the episode's file, for feeds) |
| `feedUrl`, `podcastTitle`, `episodeTitle`, `publishedAt` | for podcast episodes |
| `fileName`, `fileSizeMb` | the downloaded file |
| `model`, `task`, `language`, `languageProbability` | what was used and what was detected |
| `durationSec`, `transcribedSec`, `truncated` | full length and the part transcribed |
| `billedMinutes` | minutes charged for this file |
| `text`, `markdown`, `srt`, `vtt` | the transcript in each format |
| `segments` | `[{start, end, text, words?}]` |
| `chunks` | `[{index, text, chars, startSec, endSec, startTime}]`, when *RAG chunk size* is set |
| `files` | download links for the `.srt`, `.vtt`, `.md` and `.txt` files |
| `error`, `note` | why a file was skipped or cut; these files are not charged |

Very long transcripts keep the dataset row small: if a row would pass Apify's size limit, the segments, subtitles and Markdown are left out of it (`inlineOutputsTrimmed: true`) and are read from `files`.

### Limits

- **Platform pages are not supported**: YouTube, TikTok, Instagram, Facebook, X, Spotify, Vimeo, SoundCloud and Apple Podcasts pages are refused with a clear error. Use a direct file link or the podcast's RSS feed.
- **No speaker labels** (diarization) yet.
- Up to 300 minutes per file and 2 GB per download. Live streams are not supported.
- Google Drive files must be shared as *Anyone with the link*. Private links can be read with *Request headers* (for example a bearer token or a pre-signed URL).
- Accuracy depends on the recording. Base makes mistakes with uncommon names; use Small when names matter.
- Like every Whisper model, it can now and then skip a short passage, most often a spoken intro in the first seconds. Check `segments` if every word matters.

### FAQ

**Is my audio sent anywhere else?** No. The file is downloaded into the run and transcribed there with an open-source Whisper model.

**Why was a file not charged?** Files that could not be downloaded, could not be decoded, or had no speech are never charged. The `error` or `note` field says why.

**Can I transcribe a YouTube video?** Not with this Actor. If you have the file itself (or its audio), pass a link to it.

**Other tools from the same developer**: [Video to Scenes](https://apify.com/saliga/video-to-scenes) (scene cuts, keyframes and the transcript of each scene), [Career Site Jobs](https://apify.com/saliga/career-site-jobs) (open jobs from company career sites, new-jobs monitor), [Document to Markdown](https://apify.com/saliga/document-to-markdown) (PDF, Word, PowerPoint, Excel, OCR, RAG chunks), [Bulk HTTP Requests](https://apify.com/saliga/bulk-http-requests), [Website Screenshot + Change Detection](https://apify.com/saliga/website-screenshot-change-detection), [Keyword Search Volume](https://apify.com/saliga/keyword-search-volume).

# Actor input Schema

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

Direct links to audio or video files (mp3, m4a, aac, wav, ogg, opus, flac, mp4, mov, webm, mkv), or Google Drive and Dropbox share links. One result per file. YouTube, TikTok and other platform pages are not supported: use a link to the file itself.

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

Podcast RSS or Atom feed URLs. The newest episodes of each feed are transcribed, with their title and date.

## `episodesPerFeed` (type: `integer`):

How many of the newest episodes to transcribe from each feed.

## `model` (type: `string`):

Base: fast and cheap, good for clear speech in common languages. Small: more accurate with names, accents, noisy recordings and less common languages; about 3 times slower and priced higher.

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

Two-letter code of the spoken language (en, es, de, fr, pt, tr, ja, ...), or auto to detect it. Setting it helps with short files and with speakers who mix languages.

## `task` (type: `string`):

Transcribe: text in the spoken language. Translate: English text from speech in any language.

## `wordTimestamps` (type: `boolean`):

Add start and end times for every word inside each segment (karaoke-style captions, precise search). A little slower.

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

Only the first this many minutes of each file are transcribed and charged. Longer files are marked truncated.

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

Also return the transcript split into chunks of about this many characters (0 = off). Chunks are made of whole sentences and keep their time range, so search results can link to the right moment. Try 1500.

## `chunkOverlap` (type: `integer`):

The last sentences of a chunk, up to this many characters, are repeated at the start of the next one (at most half the chunk size).

## `saveFiles` (type: `boolean`):

Also save each transcript as .srt, .vtt, .md and .txt files in the run's key-value store, with download links in the result.

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

Files larger than this are skipped with an error (not charged). Video files are large: the audio track is read from them.

## `requestHeaders` (type: `object`):

Extra HTTP headers for downloading, for example {"Authorization": "Bearer …"} for files behind a token. Sent only to the host of each URL you gave, never to a redirect on another host. They are stored with the run input, so prefer short-lived tokens or pre-signed URLs.

## Actor input object example

```json
{
  "urls": [
    "https://archive.org/download/short_story_036_0904_librivox/shortstory036_sciencevsluck_gam_64kb.mp3"
  ],
  "episodesPerFeed": 1,
  "model": "base",
  "language": "auto",
  "task": "transcribe",
  "wordTimestamps": false,
  "maxMinutesPerFile": 180,
  "chunkSize": 0,
  "chunkOverlap": 200,
  "saveFiles": true,
  "maxFileSizeMb": 500,
  "requestHeaders": {}
}
```

# Actor output Schema

## `results` (type: `string`):

One row per file: language, duration, billed minutes, text and any error.

## `fullResults` (type: `string`):

Every field: timestamped segments, SRT, VTT, Markdown, RAG chunks and links to the saved files.

## `files` (type: `string`):

The .srt, .vtt, .md and .txt files saved for each transcript.

# 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://archive.org/download/short_story_036_0904_librivox/shortstory036_sciencevsluck_gam_64kb.mp3"
    ],
    "requestHeaders": {}
};

// Run the Actor and wait for it to finish
const run = await client.actor("saliga/audio-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://archive.org/download/short_story_036_0904_librivox/shortstory036_sciencevsluck_gam_64kb.mp3"],
    "requestHeaders": {},
}

# Run the Actor and wait for it to finish
run = client.actor("saliga/audio-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://archive.org/download/short_story_036_0904_librivox/shortstory036_sciencevsluck_gam_64kb.mp3"
  ],
  "requestHeaders": {}
}' |
apify call saliga/audio-to-text --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,saliga/audio-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/owFerREcHK9mglSs4/builds/1DPFyVT9fjQwyL0a6/openapi.json
