# Speech to Text: Audio Transcriber, Whisper, SRT & VTT (`pennydata/audio-transcriber`) Actor

Audio transcriber and speech to text: transcribe mp3, wav, m4a, mp4, webm, mov URLs or podcast RSS with Whisper, timestamps, SRT and VTT. No API key. $6 per 1,000 audio minutes, first 2 minutes of each run free, failed files free.

- **URL**: https://apify.com/pennydata/audio-transcriber.md
- **Developed by:** [Penny Data](https://apify.com/pennydata) (community)
- **Categories:** AI, Videos, Automation
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.20 / 1,000 audio 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

## Audio & Video to Text - Whisper Transcription API

Transcribe **video and audio file URLs** (mp4, webm, mov, mp3, wav, m4a, ogg, flac) or **podcast RSS feeds** to text, SRT and VTT subtitles with OpenAI Whisper. Paste a link, get the transcript, timestamped segments and duration back through the Apify API, no OpenAI key needed. $0.006 per audio minute, first 2 minutes of every run free, failed files never charged.

Quick start, transcribe an mp4 (video to text):

```json
{ "urls": ["https://example.com/interview.mp4"], "outputFormats": ["text", "srt"] }
```

Turn podcast RSS feeds and audio or video **file URLs** into text, SRT and VTT subtitles. You give direct links to mp3, wav, m4a, ogg, flac, mp4, webm or mov files. You get the full transcript, timestamped segments and the duration for each file. The speech model (OpenAI Whisper, run locally inside the Actor) needs no API key and no third-party service.

**Video to text:** link an mp4, webm or mov file and the Actor extracts the audio track with ffmpeg and transcribes it. Billing is per audio minute, same as audio. A video with no audio track returns an error row and is not charged.

**Not supported:** YouTube, Vimeo, TikTok or other page links. Link the media file itself. For podcasts, paste the podcast's RSS/Atom feed URL into `feedUrl` and the newest episodes are found and transcribed for you.

### Example output

Real result of a local run on the 3-second sample clip `https://upload.wikimedia.org/wikipedia/commons/d/d1/En-uk-hello-6.ogg` (the default input of this Actor):

```json
{ "url": "https://upload.wikimedia.org/wikipedia/commons/d/d1/En-uk-hello-6.ogg", "status": "ok", "durationSeconds": 2.97, "text": "Hello", "srtUrl": "(key-value store link, when outputFormats includes srt)" }
```

Full field list and a longer example are under Output below.

### Input

| Field | Meaning |
|---|---|
| `feedUrl` | Podcast RSS/Atom feed URL. The newest `maxEpisodes` episodes with an audio enclosure are transcribed (one row per episode). |
| `maxEpisodes` | Episodes to take from the feed (default 3, max 50). |
| `podcastName` / `podcastUrl` | No feed URL? Give a show name (e.g. `Planet Money`) or a podcasts.apple.com link; the Actor finds the RSS feed through Apple's public directory and transcribes the newest `maxEpisodes`. Ignored if `feedUrl` is set. Only shows with a public RSS feed are found. |
| `urls` | Direct http(s) links to media files. Provide `feedUrl`, `urls` or both. |
| `audioUrls`, `audioUrl`, `url` | Aliases of `urls` (array, single string, single string) so inputs written for other transcriber actors work unchanged. Merged with `urls`. |
| `language` | `auto` (default, the model detects it) or a language code such as `en`, `es`, `de`. |
| `model` | `base` (default, more accurate) or `tiny` (about 1.5x faster). Same price. |
| `outputFormats` | `text` (default). Add `srt` and/or `vtt` to also get subtitle files. |
| `maxMinutesPerFile` | Skip files longer than this (default 60, max 120). Skipped files are not charged. |
| `maxCostUsd` | Optional spending cap. A file that would exceed it is skipped and the run stops. |

### Podcasters and editors: transcribe the latest episodes from an RSS feed

Paste your show's (or any public podcast's) RSS feed URL and the newest episodes are transcribed, one row per episode. Use it for show notes, searchable archives, quote pulling, SEO pages and subtitle files for editors. Example input (NPR's Planet Money feed, newest 1 episode):

```json
{ "feedUrl": "https://feeds.npr.org/510289/podcast.xml", "maxEpisodes": 1, "outputFormats": ["text", "srt"] }
```

Each episode row has `episodeTitle`, `pubDate`, `audioUrl` and `transcript` (the same text as `text`), plus `feedTitle`, `segments` and `durationSeconds`. Pricing: $0.006 per audio minute, first 2 minutes of every run free.

### Output

One dataset row per file or podcast episode. Episodes from a feed also carry `feedTitle`, `title` (also as `episodeTitle`), `pubDate` (ISO), `audioUrl` and `transcript` (same as `text`):

```json
{
  "url": "https://example.com/talk.mp3",
  "status": "ok",
  "language": "auto-detected",
  "durationSeconds": 133.23,
  "task": "transcribe",
  "wordCount": 312,
  "minutesBilled": 1,
  "billedMinutes": 1,
  "text": "Four score and seven years ago ...",
  "segments": [{ "start": 0, "end": 7, "text": "This is a LibriVox recording." }]
}
```

`minutesBilled` is the number of minutes charged for that file (after the 2 free minutes per run), so minutesBilled times the per-minute price is the charge. `wordCount` is the number of words in `text`.

Set `task: "translate"` to get English text from speech in another language (set `language` to the spoken language for best results). Set `wordTimestamps: true` to add `words: [{word, start, end}]` to every row.

With `outputFormats: ["text", "srt", "vtt"]`, each transcribed file also gets subtitle files built from the segment timestamps (SRT uses `HH:MM:SS,mmm`, VTT uses `HH:MM:SS.mmm`). They are saved in the run's key-value store (`file-000.srt`, `file-000.vtt`, ...) and the row gets `srtUrl` and `vttUrl` links. No extra charge.

Failed files (`status: "error"`, with the `error`) and skipped files (`status: "skipped"`) are in the dataset too and are never charged. A `SUMMARY` record in the key-value store lists files, minutes billed and cost.

### Pricing (pay per event)

| Event | Price |
|---|---|
| `audio-minute`: one minute of audio successfully transcribed (rounded up per file, minimum 1) | $0.006 |

The first 2 audio minutes of every run are free (per run, not per user). Example: a 10-minute file as the first file of a run bills 8 minutes, $0.048. A 60-minute episode bills 58 minutes, about $0.35. Apify's standard platform usage rules apply on top of the event price as shown in the Console. Charges happen only after the transcript is saved.

### Switching from media-transcriber, audio-video-to-text or mp3-to-text

Drop-in compatible with lergassy/mp3-to-text input: `translateToEnglish` is the same as `task: translate`, `subtitleFormat` (`srt`, `vtt`, `both`) adds subtitle files, and `quality`, `diarize`, `chunkForRag` and the other fields are accepted and ignored (no speaker labels or RAG chunks here).

Price: $0.006 per audio minute (first 2 minutes of every run free, failed files free), and this Actor accepts the same input fields, so you can paste your existing input unchanged. Accepted aliases: `mediaUrls` (array) and `source_url` (string) are merged into `urls`; `rssFeedUrls` (first feed) maps to `feedUrl`; `maxEpisodesPerFeed` to `maxEpisodes`; `maxDurationMinutes` to `maxMinutesPerFile` (capped at 120); `translate: true` is the same as `task: translate`; `outputFormats` may list `segments`, `markdown` or `rag` (segments are always returned, the other two are ignored). `model` values `small` and `medium` are accepted but run `base` here (only `base` and `tiny` exist). `includeSubtitles: true` (tidydata/audio-video-transcription) adds an SRT file, and its `urls`, `language`, `maxEpisodes` and `maxMinutesPerFile` fields have the same names here. Other fields of those Actors (monitor, RAG chunks, downloads, proxy and so on) are not implemented and are ignored.

```json
{ "mediaUrls": ["https://upload.wikimedia.org/wikipedia/commons/d/d1/En-uk-hello-6.ogg"], "model": "base", "language": "en" }
```

Price comparison, as listed on the Apify Store on 2026-10-21 (prices from the public Store API `pricingInfos`: [ours](https://api.apify.com/v2/acts/pennydata~audio-transcriber), [sauliusautomatesit/media-transcriber](https://api.apify.com/v2/acts/sauliusautomatesit~media-transcriber), [donjuan_mime/audio-video-to-text](https://api.apify.com/v2/acts/donjuan_mime~audio-video-to-text)). Cost of one hour of audio, Free plan, before Apify platform usage:

| One hour of audio | pennydata/audio-transcriber | media-transcriber "Basic" |
|---|---|---|
| Until 2026-10-24 20:10 UTC | $0.348 ($0.006/min, first 2 min of a run free) | $0.60 ($0.01/min) |
| From 2026-10-24 20:10 UTC | $0.348 | $1.20 ($0.02/min) |
| 1,000 hours (as 1,000 one-hour runs), from 2026-10-24 | about $348 | $1,200 |

The rival also charges $0.00005 per run start; its Quality and Max tiers (small and medium models) are $0.04 and $0.08 per minute from 2026-10-24, and this Actor has no equivalent larger model. donjuan_mime/audio-video-to-text is free today ($0.00001 per result) and from 2026-11-01 11:30 UTC costs $0.01 per result, which is per file, not per minute: it is cheaper than this Actor for files longer than about 1.7 minutes (and the first 2 minutes of a run are free here), and this Actor is cheaper for clips shorter than that, so pick on features (this one adds SRT/VTT output, RSS feeds and per-file skip and cost caps) rather than price alone.

#### Dated price changes of the actors you may be switching from (public Store API `pricingInfos`, read 2026-10-22)

| Date (UTC) | Actor | Price per audio minute (Free plan) |
|---|---|---|
| 2026-10-24 20:10 | sauliusautomatesit/media-transcriber | Basic $0.01 -> $0.02 (Quality $0.04, Max $0.08) |
| 2026-11-01 11:30 | donjuan_mime/audio-video-to-text | $0.00001 -> $0.01 per result (per file) |
| 2026-11-05 00:00 | tidydata/audio-video-transcription | $0.003 -> $0.005 |
| today | pennydata/audio-transcriber (this Actor) | $0.006 |

After 2026-11-05 tidydata/audio-video-transcription ($0.005 per minute) is cheaper than this Actor ($0.006) per minute; this Actor adds podcast RSS feeds, a per-run free allowance of 2 minutes, and a cost cap (`maxCostUsd`). Not affiliated with any of these authors.

### Accuracy and speed (measured)

Whisper base on a 2-minute 13-second public-domain recording: 33 seconds on a 14-core PC, about 4x faster than real time. Expect slower on small Apify memory sizes: pick 4096 MB or more. The 2-minute test transcript was correct except for one rare word; background noise, heavy accents and music reduce accuracy as with any speech model. Segment timestamps are from the model and can be off by a second or two. Tiny is less accurate than base.

### Limits

Files up to 1.5 GB and 120 minutes. Private network addresses are blocked. Speaker labels (diarization) are not provided.

### Related actors

- [Video Frame Extractor](https://apify.com/pennydata/video-frame-extractor): pulls still frames from the same video you transcribe the audio of.
- [Shot PDF](https://apify.com/pennydata/shot-pdf): turns web pages into screenshots or PDFs, handy next to transcripts for archiving.
- [RSS/Atom Feed Reader (bulk)](https://apify.com/pennydata/rss-atom-feed-reader-bulk): collect podcast feed entries to feed into transcription.

### Use with AI agents (MCP)

Call this actor from an AI agent (Claude, Cursor, or any MCP client) through the Apify MCP server. Add this URL as an MCP server in your client: https://mcp.apify.com?tools=pennydata/audio-transcriber
It exposes this actor as a tool the agent can run with the same input as above. An Apify API token is required. This actor was built with AI assistance.

Built with AI help (Claude).

# Actor input Schema

## `feedUrl` (type: `string`):

Podcast feed (RSS or Atom with audio enclosures). The newest episodes are transcribed, one dataset item per episode with title, pubDate and audio URL. Can be combined with the file URLs below.

## `maxEpisodes` (type: `integer`):

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

## `podcastName` (type: `string`):

Show name, e.g. Planet Money. The best match in the Apple Podcasts directory is resolved to its RSS feed and the newest episodes are transcribed. Ignored if feedUrl is set.

## `podcastUrl` (type: `string`):

A podcasts.apple.com show link (contains /id123456789). Resolved to the show's RSS feed. Ignored if feedUrl is set.

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

Direct http(s) links to audio or video files (mp3, wav, m4a, ogg, flac, mp4, webm, mov ...). Pages such as YouTube or podcast web players are not supported; link the file itself.

## `audioUrls` (type: `array`):

Alias of urls, for compatibility with other transcriber actors. Merged with urls.

## `audioUrl` (type: `string`):

Alias for a single audio or video file URL. Merged with urls.

## `url` (type: `string`):

Alias for a single audio or video file URL. Merged with urls.

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

Spoken language, or auto to let the model detect it.

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

transcribe keeps the spoken language; translate outputs English text from any supported language (Whisper translate). Same price.

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

Add words: \[{word, start, end}] (seconds) to every row. Subtitle segments are then built from the words.

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

base is more accurate; tiny is about 1.5x faster. Same price.

## `translate` (type: `boolean`):

Compatibility with media-transcriber input; true is the same as task translate.

## `mediaUrls` (type: `array`):

Compatibility with sauliusautomatesit/media-transcriber input; merged into urls.

## `source_url` (type: `string`):

Compatibility with donjuan_mime/audio-video-to-text input; one direct file URL, merged into urls.

## `rssFeedUrls` (type: `array`):

Compatibility with media-transcriber input; the first feed is used as feedUrl.

## `maxEpisodesPerFeed` (type: `integer`):

Compatibility with media-transcriber input.

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

Compatibility with media-transcriber input; capped at 120.

## `outputFormats` (type: `array`):

text always stays in the dataset. Add srt and/or vtt to also get subtitle files (from the segment timestamps) stored in the key-value store, with their URLs on each dataset item.

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

Files longer than this are skipped (not transcribed, not charged).

## `maxCostUsd` (type: `number`):

Spending guard: a file that would exceed this is skipped and the run stops. Leave empty for no cap.

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

Compatibility with lergassy/mp3-to-text input; true is the same as task translate.

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

Compatibility with lergassy/mp3-to-text input; srt, vtt or both add subtitle files, like outputFormats.

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

Accepted so lergassy/mp3-to-text input runs unchanged; not used here.

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

Accepted so lergassy/mp3-to-text input runs unchanged; not used here.

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

Accepted so lergassy/mp3-to-text input runs unchanged; not used here.

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

Accepted so lergassy/mp3-to-text input runs unchanged; not used here.

## `includeSubtitles` (type: `boolean`):

Compatibility with tidydata/audio-video-transcription input: true writes an SRT subtitle file per transcript (same as outputFormats containing srt).

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

Accepted so lergassy/mp3-to-text input runs unchanged; not used here.

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

Accepted so lergassy/mp3-to-text input runs unchanged; not used here.

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

Accepted so lergassy/mp3-to-text input runs unchanged; not used here.

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

Accepted so lergassy/mp3-to-text input runs unchanged; not used here.

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

Accepted so lergassy/mp3-to-text input runs unchanged; not used here.

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

Accepted so lergassy/mp3-to-text input runs unchanged; not used here.

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

Accepted so lergassy/mp3-to-text input runs unchanged; not used here.

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

Accepted so lergassy/mp3-to-text input runs unchanged; not used here.

## Actor input object example

```json
{
  "maxEpisodes": 3,
  "urls": [
    "https://upload.wikimedia.org/wikipedia/commons/d/d1/En-uk-hello-6.ogg"
  ],
  "language": "auto",
  "task": "transcribe",
  "wordTimestamps": false,
  "model": "base",
  "translate": false,
  "outputFormats": [
    "text"
  ],
  "maxMinutesPerFile": 60,
  "translateToEnglish": false
}
```

# Actor output Schema

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

No description

## `summary` (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 = {
    "urls": [
        "https://upload.wikimedia.org/wikipedia/commons/d/d1/En-uk-hello-6.ogg"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("pennydata/audio-transcriber").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://upload.wikimedia.org/wikipedia/commons/d/d1/En-uk-hello-6.ogg"] }

# Run the Actor and wait for it to finish
run = client.actor("pennydata/audio-transcriber").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://upload.wikimedia.org/wikipedia/commons/d/d1/En-uk-hello-6.ogg"
  ]
}' |
apify call pennydata/audio-transcriber --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,pennydata/audio-transcriber"
        }
    }
}
```

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/SogOChnbEL31XZWeO/builds/LPvbOvNPTp7gPZw5Q/openapi.json
