# Audio & Video Transcription (Whisper): Podcast, MP3 to SRT (`tidewell/media-transcriber`) Actor

Transcribe MP3, MP4, WAV and other audio/video files, podcast RSS feeds and Apple Podcasts links with Whisper. Get text, timestamped segments, SRT/VTT subtitles and word timestamps in 99 languages. Pay per audio minute.

- **URL**: https://apify.com/tidewell/media-transcriber.md
- **Developed by:** [Tidewell Software](https://apify.com/tidewell) (community)
- **Categories:** AI, Videos, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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 Transcription with Whisper: podcasts, MP3, MP4 to text and SRT

Transcribe **audio and video files, podcast RSS feeds and Apple Podcasts links** to text with OpenAI's open-source **Whisper** model. Each file gives you **plain text, timestamped segments, SRT and VTT subtitles** and, if you want them, **word-level timestamps**, in 99 languages with automatic language detection.

- **Any direct media link.** MP3, M4A, WAV, FLAC, OGG, OPUS, AAC, MP4, MOV, WEBM, MKV, AVI and more. Video files are fine: only the audio track is read.
- **Podcasts built in.** Paste an RSS feed and choose how many episodes, with title and date filters. Apple Podcasts show and episode links are resolved to the show's public feed. Episode title, date and podcast name are added to every row.
- **Subtitles that read well.** SRT and VTT cues are cut from word timings at sentence and clause boundaries, at most 42 characters per line and 2 lines, which is the broadcast standard (you can change both).
- **Private by design.** Transcription runs inside the Actor with [faster-whisper](https://github.com/SYSTRAN/faster-whisper). Your audio is not sent to OpenAI or any other third-party API.
- **Pay per audio minute.** From **$0.004 per minute**. Failed downloads, broken files and files with no speech are free.

#### What can you use it for?

| Use case | How |
|---|---|
| **Podcast show notes, blog posts and SEO** | Give the feed URL, set `maxEpisodesPerFeed`, then use `text` |
| **Subtitles for videos and reels** | Give the MP4/MOV link or upload it, then download `srt_url` or `vtt_url` |
| **Searchable archives** of interviews, lectures and meetings | Use `segments` (sentence + start/end seconds) to jump to the moment |
| **AI agents, RAG and LLM pipelines** | Feed `text` or `segments` to Claude, GPT or a vector store |
| **Translate any language to English** | `task: translate` |
| **n8n / Make / Zapier automations** | Call the Actor with a file URL and read the dataset row |

#### Input

```json
{
  "mediaUrls": [
    "https://feeds.example.com/my-podcast.rss",
    "https://cdn.example.com/interview.mp3",
    "https://example.com/videos/webinar.mp4"
  ],
  "maxEpisodesPerFeed": 3,
  "model": "small",
  "language": "auto",
  "wordTimestamps": false
}
```

| Field | Default | What it does |
|---|---|---|
| `mediaUrls` | | Direct audio/video file links, podcast RSS/Atom feeds, Apple Podcasts links, or web pages that embed a media file (`<audio>`, `<video>`, `og:audio`). Google Drive and Dropbox share links work |
| `uploadedFiles` | | Upload files from your computer in the Console |
| `maxEpisodesPerFeed` | 1 | Episodes per feed, newest first. 0 = all that match |
| `episodeTitleFilter` | | Only episodes whose title contains this text. Use `\|` for alternatives |
| `episodesPublishedAfter`, `episodesPublishedBefore` | | `YYYY-MM-DD`, or relative like `30 days` |
| `model` | `base` | `tiny`, `base`, `small`, `medium` or `large-v3-turbo` (see below) |
| `language` | `auto` | Spoken language, or auto-detect |
| `task` | `transcribe` | `translate` gives an English translation |
| `wordTimestamps` | off | Every word with start, end and confidence. No extra cost |
| `initialPrompt` | | Names and jargon to steer spelling, e.g. `"Kubernetes, Grafana, Dr. Nkemelu"` |
| `vadFilter` | on | Skip silence and music (faster, avoids made-up text in silent parts) |
| `subtitleMaxLineLength`, `subtitleMaxLines` | 42 / 2 | Subtitle layout |
| `maxDurationMinutes` | whole file | Transcribe only the first N minutes of each file |
| `maxFileSizeMb`, `downloadTimeoutSecs` | 2000 / 900 | Limits |
| `httpHeaders` | | Headers for private file servers or private feeds |

#### Which model?

Measured on Apify at the default 4 GB of memory (1 CPU core), on a 5.4-minute LibriVox recording:

| Model | Speed | Accuracy | Price per minute |
|---|---|---|---|
| `tiny` | about 20x realtime | Rough drafts, keyword search | $0.004 |
| `base` (default) | about 8x realtime | Good for clear English speech | $0.004 |
| `small` | about 3x realtime | Clearly better on names, accents and other languages | $0.009 |
| `medium` | about 1x realtime (2x at 8 GB) | High accuracy | $0.025 |
| `large-v3-turbo` | about 1x realtime (2x at 8 GB) | High accuracy, best for many languages | $0.025 |

A 1-hour podcast takes about 8 minutes with `base` and about 20 minutes with `small` at 4 GB. **More memory makes runs faster**: Apify gives one CPU core per 4 GB, and the Actor uses all of them. For `medium` and `large-v3-turbo`, use 8-16 GB. The price per minute is the same at any memory.

#### Output

One dataset row per file. The TXT, SRT, VTT and JSON files are also saved to the run's key-value store and linked from the row.

```json
{
  "media_id": "media-0001",
  "status": "succeeded",
  "source_url": "https://feeds.example.com/my-podcast.rss",
  "source_type": "rss",
  "media_url": "https://cdn.example.com/episodes/ep42.mp3",
  "title": "Ep 42: Pricing your first product",
  "podcast_title": "The Indie Hour",
  "episode_published_at": "2026-11-03T06:00:00+00:00",
  "duration_secs": 2843.2,
  "transcribed_secs": 2843.2,
  "billed_minutes": 48,
  "model": "small",
  "language": "en",
  "language_probability": 0.998,
  "text": "Welcome back to the Indie Hour. Today we're talking about pricing...\n\nSo the first thing I did was...",
  "word_count": 7412,
  "segments": [
    { "id": 0, "start": 0.42, "end": 3.1, "text": "Welcome back to the Indie Hour." },
    { "id": 1, "start": 3.48, "end": 7.92, "text": "Today we're talking about pricing your first product." }
  ],
  "srt_url": "https://api.apify.com/v2/key-value-stores/.../records/media-0001-ep42.srt",
  "vtt_url": "https://api.apify.com/v2/key-value-stores/.../records/media-0001-ep42.vtt",
  "text_url": "https://api.apify.com/v2/key-value-stores/.../records/media-0001-ep42.txt",
  "json_url": "https://api.apify.com/v2/key-value-stores/.../records/media-0001-ep42.json",
  "processing_secs": 951.3,
  "speed_x": 3.0,
  "errors": [],
  "warnings": []
}
```

With `wordTimestamps` on, rows also have `words`: `[{"start": 0.42, "end": 0.71, "word": " Welcome", "probability": 0.98}, ...]`.

SRT example:

```
1
00:00:00,420 --> 00:00:03,100
Welcome back to the Indie Hour.

2
00:00:03,480 --> 00:00:07,920
Today we're talking about pricing
your first product.
```

Failed files get `status: "failed"` and an error code, such as `download_failed`, `unsupported_platform`, `not_media`, `no_media_found`, `no_audio_stream`, `decode_failed`, `no_episodes` or `file_too_large`. Files skipped because the run hit your cost limit get `status: "skipped"`. Neither is charged.

The dataset has four views: **Overview**, **Transcripts**, **Segments** (one row per sentence) and **Status & errors**.

#### Use from the API, n8n or Make

```bash
curl -X POST "https://api.apify.com/v2/acts/tidewell~media-transcriber/run-sync-get-dataset-items?token=<APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"mediaUrls": ["https://example.com/interview.mp3"], "model": "small"}'
```

Synchronous calls wait up to 5 minutes, which is enough for about 30 minutes of audio with `base`. For longer files, start the run asynchronously and read the dataset when it finishes. In n8n or Make, use the Apify node with "Run Actor" and "Get dataset items".

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("tidewell/media-transcriber").call(run_input={
    "mediaUrls": ["https://feeds.example.com/my-podcast.rss"], "maxEpisodesPerFeed": 5, "model": "small",
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    if row["status"] == "succeeded":
        print(row["title"], row["srt_url"])
```

#### Pricing

Pay per event, per minute of audio transcribed. A file is billed `seconds transcribed / 60`, rounded up to the next minute.

| Event | Price | When |
|---|---|---|
| Audio minute (tiny or base) | **$0.004** | Per minute transcribed with `tiny` or `base` |
| Audio minute (small) | **$0.009** | Per minute transcribed with `small` |
| Audio minute (medium or large-v3-turbo) | **$0.025** | Per minute transcribed with `medium` or `large-v3-turbo` |
| Actor start | $0.0005 per GB of memory | Once per run ($0.002 at the default 4 GB) |

| Example | Cost |
|---|---|
| 1-hour podcast episode, `base` | $0.24 |
| 1-hour podcast episode, `small` | $0.54 |
| 10 x 45-minute episodes, `small` | $4.05 |
| 100 one-minute clips, `base` (one run) | $0.40 |

Failed downloads, broken or unsupported files and files with no speech cost nothing. The Actor respects your **maximum cost per run**: before each file it works out how many minutes your limit still pays for and transcribes only that much, so it never does work it cannot bill.

#### FAQ

**Can I transcribe YouTube, TikTok, Instagram, Spotify or Vimeo links?** No. Downloading from those platforms breaks their terms of service, and Spotify audio is DRM-protected, so the Actor refuses those links with `unsupported_platform` (not charged). If you own the video, download it and upload the file, or use a direct file link from your own storage. For YouTube captions, a YouTube transcript Actor that reads the official captions is the better tool.

**Which podcast links work?** Any public RSS or Atom feed with audio/video enclosures (Buzzsprout, Libsyn, Anchor/Spotify for Podcasters RSS, Megaphone, Transistor, Podbean, Simplecast and more), and Apple Podcasts show or episode links, which are resolved through Apple's public lookup API to the show's own feed. Spotify-exclusive shows have no public feed.

**Does it identify speakers (diarization)?** Not yet. You get timestamps per sentence and per word, not speaker labels.

**How accurate is it?** It is Whisper. `base` is good on clear speech in English and the big European languages. Use `small` or larger for accents, names, noisy audio, phone calls and other languages. Adding names and jargon to `initialPrompt` helps with spelling.

**What about music, silence or ads with no speech?** Voice activity detection skips them, so they take little time and produce no made-up text. A file with no speech at all is not charged.

**How long can files be?** There is no fixed limit. Audio is decoded in 10-minute windows, so memory stays low even for multi-hour recordings. Very long files simply take longer; use more memory to speed them up. Files over 2 GB need `maxFileSizeMb`.

**Is my audio stored or shared?** Files are downloaded to the Actor's temporary disk and deleted after transcription. Transcripts are saved only to your own run's dataset and key-value store, under your retention settings. Nothing is sent to OpenAI or other APIs.

**Are the transcripts mine to use?** The transcript is yours, but the rights in the audio belong to whoever owns it. Only transcribe media you own or have permission to use.

#### Keywords

audio to text, speech to text, transcribe mp3, mp3 to text, video to text, mp4 to text, podcast transcription, podcast transcript, rss to text, whisper, whisper api, faster-whisper, openai whisper, srt subtitles, vtt subtitles, subtitle generator, word timestamps, transcription api, transcribe audio, audio transcription, wav to text, m4a to text, translate audio to english

# Actor input Schema

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

Direct links to audio or video files (MP3, M4A, WAV, FLAC, OGG, OPUS, AAC, MP4, MOV, WEBM, MKV and more), podcast RSS feeds, Apple Podcasts show or episode links, or web pages that embed an audio/video file. Google Drive and Dropbox share links work. YouTube, TikTok, Instagram, Spotify and similar platform pages are not supported.

## `uploadedFiles` (type: `array`):

Upload files from your computer. They are stored in a key-value store and transcribed like media URLs.

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

How many episodes to transcribe from each podcast feed, newest first, after the filters below. 0 = all matching episodes.

## `episodeTitleFilter` (type: `string`):

Only episodes whose title contains this text (case-insensitive). Separate alternatives with |, e.g. "interview|Q\&A".

## `episodesPublishedAfter` (type: `string`):

Only episodes published on or after this date: YYYY-MM-DD, or relative like "30 days" or "2 weeks".

## `episodesPublishedBefore` (type: `string`):

Only episodes published before this date (YYYY-MM-DD).

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

Bigger models are more accurate (names, accents, noisy audio, non-English) but slower and priced higher per minute. base is a good default for clear speech; small is noticeably better for most languages.

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

Spoken language. Auto-detect works well; set it when you know it, which avoids rare detection mistakes on short or noisy clips.

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

transcribe: text in the spoken language. translate: English translation of any language (Whisper's built-in speech translation).

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

Add every word with its start, end and confidence to the dataset row and the JSON file. Free (subtitles always use word timing internally).

## `initialPrompt` (type: `string`):

Names, jargon or spelling to steer the transcript, e.g. "Kubernetes, Grafana, Dr. Nkemelu". Keep it short.

## `vadFilter` (type: `boolean`):

Silero voice activity detection skips silence and non-speech, which is faster and avoids made-up text in silent parts. Turn off only if quiet speech is being skipped.

## `subtitleMaxLineLength` (type: `integer`):

Maximum characters per subtitle line in the SRT/VTT files. 42 is the broadcast standard.

## `subtitleMaxLines` (type: `integer`):

Maximum lines per subtitle cue.

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

Transcribe at most the first N minutes of each file (you pay only for what is transcribed). Empty = whole file.

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

Files larger than this are skipped (not charged). Video files are large: about 1 GB per hour of HD video is common.

## `downloadTimeoutSecs` (type: `integer`):

Maximum time to download one file.

## `httpHeaders` (type: `object`):

Headers sent with every download, e.g. {"Authorization": "Bearer ..."} for private file servers or private podcast feeds.

## Actor input object example

```json
{
  "mediaUrls": [
    "https://archive.org/download/life_poem_21_dickinson/life_21_dickinson_ad_64kb.mp3"
  ],
  "maxEpisodesPerFeed": 1,
  "model": "base",
  "language": "auto",
  "task": "transcribe",
  "wordTimestamps": false,
  "vadFilter": true,
  "subtitleMaxLineLength": 42,
  "subtitleMaxLines": 2,
  "maxFileSizeMb": 2000,
  "downloadTimeoutSecs": 900
}
```

# Actor output Schema

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

One row per audio/video file: text, timestamped segments, language, duration and file links.

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

Transcript and subtitle files in the key-value store.

# 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 = {
    "mediaUrls": [
        "https://archive.org/download/life_poem_21_dickinson/life_21_dickinson_ad_64kb.mp3"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("tidewell/media-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 = { "mediaUrls": ["https://archive.org/download/life_poem_21_dickinson/life_21_dickinson_ad_64kb.mp3"] }

# Run the Actor and wait for it to finish
run = client.actor("tidewell/media-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 '{
  "mediaUrls": [
    "https://archive.org/download/life_poem_21_dickinson/life_21_dickinson_ad_64kb.mp3"
  ]
}' |
apify call tidewell/media-transcriber --silent --output-dataset

```

## MCP server setup

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