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

Transcribe audio, video and podcast episodes (file links or RSS feeds) to text, SRT and VTT with Whisper, privately on Apify. Optional speaker labels (who spoke when). 99 languages, timestamps. $0.006 per audio minute (+$0.013 with speakers); failed files free.

- **URL**: https://apify.com/webstack-labs/audio-transcriber.md
- **Developed by:** [Webstack Labs](https://apify.com/webstack-labs) (community)
- **Categories:** AI, Videos, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $6.00 / 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.

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 Transcriber: Whisper Speech to Text, SRT, VTT

**Speech to text and audio to text** for audio, video and **podcast episodes**: text and subtitles. Paste links to MP3, WAV, M4A, MP4 or
other media files, or a **podcast RSS feed**, and get the full transcript, timed segments and ready-to-use
**TXT, SRT and VTT** files. It runs OpenAI's open-source **Whisper** model directly on Apify, so your audio is **not sent to any third-party
AI API** (no OpenAI, Groq or Deepgram account needed).

- 99 languages, automatic language detection, or set the language yourself
- **Translate** speech in any language into English text
- Timed segments, optional **word-level timestamps** with confidence scores
- **Speaker labels (who spoke when)**: optional speaker diarization labels every segment Speaker 1,
  Speaker 2, ... and adds a dialogue transcript, speaking time per speaker and speaker names in the
  SRT and VTT files, all with open models on Apify (no pyannote or Hugging Face token needed)
- **Podcast RSS feeds**: give the feed link, get the newest episodes transcribed with show and episode
  title, date and link on each row (schedule it daily to transcribe every new episode)
- Many files in one run; long files up to 4 hours each
- Pay per audio minute; **failed files are free**
- Empty input = a free short sample (with `speakerLabels`, a two-voice sample), so you can see the output before paying

### Who uses it

- Podcasters and video editors who need subtitles (SRT/VTT) and show notes
- Podcast monitoring and research: transcribe the latest episodes of many shows from their RSS feeds
- Researchers and journalists transcribing interviews (with speaker labels for Q\&A)
- Meeting notes and call analysis: who said what, and how long each person spoke
- Teams turning meeting and webinar recordings into searchable text for AI tools and RAG pipelines
- No-code automations (Make, Zapier, n8n) that need speech-to-text from a file link

### Input

```json
{
  "audioUrls": [
    "https://example.com/podcast/episode-12.mp3",
    "https://example.com/videos/webinar.mp4"
  ],
  "podcastFeedUrls": ["https://feeds.example.com/my-show.xml"],
  "maxEpisodesPerFeed": 3,
  "language": "",
  "task": "transcribe",
  "model": "base",
  "wordTimestamps": false,
  "speakerLabels": true,
  "numSpeakers": 0
}
```

- **audioUrls**: direct links to files (anything FFmpeg can read: mp3, wav, m4a, aac, ogg, opus, flac,
  webm, mp4, mov, mkv, ...). Files in your own Apify key-value store or cloud storage work through a
  public or signed link.
- **podcastFeedUrls**: RSS or Atom feeds of podcasts (the feed link, not the Apple Podcasts or Spotify
  page). The newest **maxEpisodesPerFeed** episodes (default 3) of each feed are transcribed. Each row then
  also has `podcastTitle`, `episodeTitle`, `episodePublishedAt`, `episodeUrl`, `episodeGuid` and `feedUrl`.
  Owner names and e-mail addresses in feeds are never collected. Feeds and episode files are checked
  against robots.txt like any other link. Use transcripts of other people's shows in line with their
  rights (for example for research, search or accessibility), not to republish them.
- **language**: a code such as `en`, `ja`, `de`, `es`, or empty to detect it.
- **task**: `transcribe` (same language) or `translate` (to English).
- **model**: `base` (default, more accurate) or `tiny` (faster). Same price.
- **speakerLabels** (default off): find the speakers and label each segment (`segments[].speaker`,
  also on every word with word timestamps). Rows get `speakerCount`, `speakers` (speaking seconds,
  turns, first time heard) and `dialogue` (`Speaker 1: ...` paragraphs); the TXT file becomes the dialogue,
  SRT lines start with `[Speaker 1]`, VTT uses voice tags `<v Speaker 1>`. **numSpeakers**: the number
  of people if you know it (for example 2 for an interview), 0 = automatic. Extra $0.013 per audio minute.
- **maxMinutesPerFile** (default 120, up to 240) and **maxFileSizeMb** (default 500) protect your budget.

Pages of YouTube, TikTok, Instagram, Spotify and similar platforms are refused: their terms do not
allow downloading, and they are not media files.

### Output

One dataset row per file:

```json
{
  "url": "https://example.com/podcast/episode-12.mp3",
  "fileName": "episode-12.mp3",
  "durationSecs": 1834.2,
  "billedMinutes": 31,
  "language": "en",
  "languageProbability": 0.99,
  "text": "Welcome back to the show. Today we talk about ...",
  "wordCount": 5120,
  "segments": [
    {"start": 0.0, "end": 3.2, "text": "Welcome back to the show."},
    {"start": 3.2, "end": 7.9, "text": "Today we talk about ..."}
  ],
  "txtUrl": "https://api.apify.com/v2/key-value-stores/.../records/transcript-00001-episode-12.txt",
  "srtUrl": "https://api.apify.com/v2/key-value-stores/.../records/transcript-00001-episode-12.srt",
  "vttUrl": "https://api.apify.com/v2/key-value-stores/.../records/transcript-00001-episode-12.vtt",
  "truncated": false,
  "processingSecs": 402.5,
  "realTimeFactor": 0.219,
  "error": null
}
```

With `speakerLabels: true` each segment also has `"speaker": "Speaker 1"`, and the row adds:

```json
{
  "speakerCount": 2,
  "speakers": [
    {"speaker": "Speaker 1", "speakingSecs": 1010.4, "turns": 58, "firstHeardAt": 0.0},
    {"speaker": "Speaker 2", "speakingSecs": 772.9, "turns": 57, "firstHeardAt": 3.4}
  ],
  "dialogue": "Speaker 1: Welcome back to the show.\n\nSpeaker 2: Thanks for having me. ...",
  "speakerBilledMinutes": 31,
  "speakerError": null
}
```

Failed files come back as rows with `error` (for example `http_error`, `not_a_media_file`,
`file_too_large`, `not_audio`, `blocked_by_robots_txt`; for feeds `not_a_feed`, `feed_http_error`,
`feed_without_media`) and are not charged. A `SUMMARY` record in the
key-value store has the totals.

### Pricing

**$0.006 per started minute of audio, per file** ($0.36 per hour), plus Apify's small start fee.
A 61-second file is 2 minutes. Files longer than `maxMinutesPerFile` are cut and billed only up to the
cut. **Speaker labels** (optional): **+$0.013 per started minute** (so $0.019 per minute with speakers),
charged only when turned on and only if the speakers were found (a file whose speaker step fails keeps
its transcript and is charged as transcription only). Failed files and the built-in sample are free. You can also set a maximum cost per run in Apify.

### Use it from AI agents (MCP)

The Actor is available to AI agents (Claude, ChatGPT, Cursor and others) through Apify's MCP server
(`mcp.apify.com`) and the API. Minimal input (a direct link to an audio or video file):

```json
{"audioUrls": ["https://github.com/ggml-org/whisper.cpp/raw/master/samples/jfk.wav"]}
```

Output row: `{"fileName": "jfk.wav", "durationSecs": 11, "billedMinutes": 1, "language": "en",
"text": "And so my fellow Americans, ask not what your country can do for you, ask what you can do for your country.",
"srtUrl": "https://api.apify.com/v2/key-value-stores/.../records/transcript-00001-jfk.srt", "error": null, ...}`.
Cost of this example: 1 minute × $0.006 = $0.006 plus the start fee. Read `text` (or `segments` for
timestamps); every row has the same fields, and files that fail come back as free rows with an `error` code.
An empty input transcribes a short built-in sample for free. For an interview or meeting, add
`"speakerLabels": true` (and `"numSpeakers": 2` if you know it) and read `dialogue` or `segments[].speaker`.

### How it compares

Popular transcription Actors on Apify Store, with the price each one lists for its main event on
2026-11-05 (check their pages for changes and for other prices):

| Actor | Listed price | Unit |
|---|---|---|
| **This Actor** | **$0.006** | audio minute (transcript, SRT, VTT, segments) |
| This Actor with speaker labels | $0.019 | audio minute ($0.006 + $0.013, only when turned on) |
| steadyfetch/media-transcriber | $0.006 | audio minute |
| tidytools/audio-transcriber | $0.006 | audio minute |
| kaz_kakyo/audio-transcriber | $0.01 | audio minute |
| memo23/video-audio-transcriber | $0.0008 | second of media ($0.048 per minute) |
| tinplatelabs/transcriber (with speaker diarization) | $0.055 | audio minute, medium model |

What you get here for the base price: TXT, SRT and VTT files, timed segments, optional word timestamps,
translation to English, podcast RSS feeds, many files per run, failed files free, and audio that never
leaves Apify (no third-party AI API). Speaker diarization (who spoke when) is an add-on you pay for only
when you use it. Choose a larger-model Actor if you need the highest accuracy on noisy audio: this one
runs Whisper tiny/base on CPU.

### How it works

1. Each link is checked against the site's robots.txt (on by default) and downloaded once, with a size
   limit, using an honest User-Agent.
2. The audio is decoded with FFmpeg and transcribed with
   [faster-whisper](https://github.com/SYSTRAN/faster-whisper) (CTranslate2, int8, CPU) using the
   Whisper `base` or `tiny` model, with voice activity detection to skip silence.
3. With speaker labels, [sherpa-onnx](https://github.com/k2-fsa/sherpa-onnx) finds the speech turns with the
   pyannote segmentation-3.0 model, embeds each turn with a 3D-Speaker CAM++ model and groups the turns
   into speakers; each Whisper segment gets the speaker who talks most during it.
4. The transcript, segments and subtitle files are saved; the downloaded file is deleted.

Speed: the default 4 GB memory gives one CPU core. Each row shows `realTimeFactor` (processing time
divided by audio length); `tiny` and more memory (8 GB = two cores) are faster for long files.

### FAQ

**How accurate is it?** Whisper base is good for clear speech in common languages; noisy recordings,
strong accents, music and overlapping speakers lower accuracy. Setting the language helps.

**Does it identify speakers?** Yes, with `speakerLabels: true`: it tells speakers apart and labels them
Speaker 1, Speaker 2, ... (in order of first appearance). It does not know people's names; rename them
in your own tool. Overlapping speech gets the speaker who talks most in that segment. Setting
`numSpeakers` helps when you know it.

**Can I transcribe a YouTube video?** No. Use a file you own or may download.

**How do I find a podcast's RSS feed?** Most podcast websites link it ("RSS"), and podcast hosts
(Libsyn, Buzzsprout, Megaphone, Transistor, Podbean, ...) show it on the show page. Directories such as
Podcast Index list the feed of almost every show.

**Can I get only new episodes every day?** Schedule the Actor with `maxEpisodesPerFeed: 1` (or the number
of episodes a show publishes between runs) and use `episodeGuid` to skip ones you already have.

**Is my audio stored?** The downloaded file is deleted after transcription. Transcripts stay in your
run's storage under your Apify account.

### Limitations

- CPU only; very long files take a while (see Speed above).
- Models: tiny and base. Larger models are too slow on CPU for this price.
- Links must be direct file links that can be downloaded without logging in.

### Credits and licences

- OpenAI Whisper models: MIT licence.
- faster-whisper (SYSTRAN) and CTranslate2: MIT licence. FFmpeg (Debian package): LGPL/GPL.
- Speaker labels: sherpa-onnx (k2-fsa, Apache-2.0), pyannote segmentation-3.0 (MIT, converted to ONNX by
  sherpa-onnx), 3D-Speaker CAM++ speaker embedding model (Alibaba DAMO Academy, Apache-2.0).
- The demo sample is generated with espeak-ng at build time.

# Actor input Schema

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

One direct file link per line: MP3, WAV, M4A, AAC, OGG, OPUS, FLAC, WEBM, MP4, MOV, MKV and more (anything FFmpeg reads). Podcast episode links from an RSS feed work. Pages of YouTube, TikTok, Instagram, Spotify and similar platforms are not supported (their terms do not allow downloading). Empty = free short sample.

## `podcastFeedUrls` (type: `array`):

Optional. RSS or Atom feed links of podcasts (for example https://feeds.example.com/show.xml). The newest episodes of each feed are transcribed (see Episodes per feed), and each row gets the show and episode title, date and link. Pages of Apple Podcasts, Spotify or YouTube are not feeds and are refused. Feeds that cannot be read come back as free rows.

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

Spoken language as a code (en, ja, de, es, fr, pt, zh, ...), or empty to detect it from the first 30 seconds. Setting it improves accuracy for short or mixed files.

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

transcribe = text in the spoken language; translate = English text from any language.

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

base (default) is more accurate; tiny is about twice as fast. Same price per minute.

## `speakerLabels` (type: `boolean`):

Find the different speakers (speaker diarization) and label every segment Speaker 1, Speaker 2, ... Adds a dialogue transcript, speaking time per speaker, and speaker names in SRT and VTT. Runs privately with open models (pyannote segmentation, 3D-Speaker). Extra $0.013 per audio minute.

## `numSpeakers` (type: `integer`):

How many people speak, if you know it (for example 2 for an interview). 0 = detect automatically. Setting it turns speaker labels on.

## `speakerThreshold` (type: `string`):

How different two voices must be to count as different speakers when numSpeakers is 0. Higher = fewer speakers. Default 1.1.

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

Add start and end times and a confidence score for every word (in segments\[].words).

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

Add the timed segments (start, end, text) to each dataset row. TXT, SRT and VTT files are always saved and linked.

## `includeSubtitlesInDataset` (type: `boolean`):

Also put the full SRT and VTT subtitles into each dataset row (handy for API and no-code tools). They are always available as files.

## `skipSilence` (type: `boolean`):

Skip long silent parts with voice activity detection (faster, fewer made-up words in silence). Billing uses the full file length either way.

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

How many of the newest episodes to transcribe from each podcast feed (1-200). Each episode is billed by its length like any other file.

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

Longer files are cut at this length (marked truncated: true) and billed only up to it (1-240).

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

Files larger than this are skipped for free (1-2000).

## `respectRobotsTxt` (type: `boolean`):

Skip files that the site's robots.txt closes to automated access (returned as free rows). Turn off only for files you own or may download.

## `beamSize` (type: `integer`):

Whisper decoding beam size (1-5). 1 (default) is fast with nearly the same words; 5 searches more and is slower. Same price per minute.

## Actor input object example

```json
{
  "audioUrls": [
    "https://example.com/media/interview.mp3"
  ],
  "language": "",
  "task": "transcribe",
  "model": "base",
  "speakerLabels": false,
  "numSpeakers": 0,
  "wordTimestamps": false,
  "includeSegments": true,
  "includeSubtitlesInDataset": false,
  "skipSilence": true,
  "maxEpisodesPerFeed": 3,
  "maxMinutesPerFile": 120,
  "maxFileSizeMb": 500,
  "respectRobotsTxt": true,
  "beamSize": 1
}
```

# Actor output Schema

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

No description

## `files` (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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("webstack-labs/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 = {}

# Run the Actor and wait for it to finish
run = client.actor("webstack-labs/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 '{}' |
apify call webstack-labs/audio-transcriber --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,webstack-labs/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/TmNu7Zjtz3cBXgXip/builds/NNWNQNU6ygR2QzlrN/openapi.json
