# Audio & Video Transcriber (Whisper) (`plum_spear/aztec-audio-video-transcriber`) Actor

Transcribe audio and video files from direct URLs into text, timestamped segments, SRT or VTT with Whisper. Optional high-precision mode for Portuguese and noisy audio. Pay per minute, no YouTube or social platform downloads.

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

## Pricing

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

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)

Transcribe audio and video files into text, timestamped segments, SRT or VTT. Give it direct URLs to media files and get one dataset item per file. By default it runs Whisper (`faster-whisper`, `base` model) inside the Actor container, so your audio is processed within the Apify run and is not sent to any third-party transcription API. An optional **high precision** mode (`quality: "high"`) transcribes with Whisper large-v3-turbo on Groq instead: it is much more accurate, especially in Portuguese and in noisy audio, but **your audio is sent to Groq** (see Privacy).

Built and maintained by **Az Digital Consulting LU**.

### What it does

- Takes a list of **direct URLs to audio or video files** (mp3, wav, m4a, aac, flac, ogg, opus, mp4, webm, mkv, mov).
- Detects the spoken language automatically, or uses the language you set.
- Returns the full text, segments with start and end times, and optionally SRT and VTT subtitle files.
- Processes files one by one. A bad URL or unreadable file produces an error item and never stops the run.
- Long files are processed in 10-minute blocks, so memory stays flat.
- Two quality levels: `standard` (default, audio stays in the run) and `high` (more accurate, audio is sent to Groq).

### What it does not do

- **It does not download from video and social platforms.** URLs of common platforms and their media CDNs (YouTube, Instagram, TikTok, Facebook, X/Twitter, Twitch, Vimeo, Dailymotion, Spotify, SoundCloud, Reddit, LinkedIn, Snapchat, Pinterest, Netflix and similar) are refused with `platform_not_supported`, and the check is repeated on every redirect. The list is not exhaustive: the Actor only fetches files that a plain HTTP request returns as media, and you are responsible for what you submit.
- No speaker identification, no translation, no word-level timestamps.
- Live radio and other endless streams are refused (`live_stream`, `download_too_slow` or `download_timeout`).
- Files larger than 500 MB are refused, and each download must finish within 10 minutes.

You are responsible for having the right to transcribe the files you submit.

### Input

| Field | Type | Description |
|---|---|---|
| `mediaUrls` | array of strings | Direct links to media files. To transcribe a file you uploaded to an Apify key-value store, use that record's public URL (including its signature). |
| `language` | string | ISO 639-1 code such as `en`, `pt`, `es`, `de`. Default `auto`. |
| `quality` | string | `standard` (default): Whisper base inside this Actor, the audio never leaves the run. `high`: Whisper large-v3-turbo on Groq, much more accurate, **the audio is sent to Groq**. Billed with its own per-minute event. |
| `outputFormats` | array | Any of `text`, `segments`, `srt`, `vtt`. Default `["text", "segments"]`. |
| `maxDurationMinutes` | integer | Limit of audio transcribed per file. A file whose header already shows a longer duration is refused without charge. If the real audio turns out longer, only the first `maxDurationMinutes` are transcribed and charged, and the item is marked `partial`. Default 120, maximum 600. |

```json
{
  "mediaUrls": ["https://upload.wikimedia.org/wikipedia/commons/a/a1/Gettysburg_by_Britton.ogg"],
  "language": "auto",
  "outputFormats": ["text", "segments", "srt"],
  "maxDurationMinutes": 120
}
```

Portuguese interview in high precision (the audio is sent to Groq):

```json
{
  "mediaUrls": ["https://example.com/entrevista.mp3"],
  "language": "pt",
  "quality": "high",
  "outputFormats": ["text", "srt"]
}
```

### Output

One dataset item per input URL.

```json
{
  "url": "https://example.com/interview.mp3",
  "status": "ok",
  "language": "en",
  "durationSeconds": 640.5,
  "billedMinutes": 11,
  "model": "base",
  "quality": "standard",
  "processingSeconds": 85.49,
  "text": "Full transcript...",
  "segments": [{ "start": 0.0, "end": 5.36, "text": "First phrase." }]
}
```

`status` is `ok` (whole file transcribed), `partial` (transcribed up to `maxDurationMinutes` or your maximum cost per run; `errorCode` and `error` say which limit stopped it) or `failed`. Items may carry `warnings`, for example `damaged_audio` when unreadable packets were skipped and the rest was transcribed.

With `quality: "high"` the item also has `billedMinutesHigh` (minutes charged at the high-precision price) and `model` is `whisper-large-v3-turbo`. If Groq could not transcribe some blocks, those blocks were transcribed with the base model and charged at the standard price: `model` becomes `mixed` (or `base` if every block fell back), `fallbackBlocks` counts them and `fallbackReason` gives the first cause (`rate_limited`, `timeout`, `network`, `server_error`, `too_large`, `unauthorized`, `bad_response` or `http_<status>`). Billing always matches the engine that actually produced each block.

Failed items carry `status: "failed"`, an `errorCode` and a readable `error`:

| `errorCode` | Meaning |
|---|---|
| `platform_not_supported` | The URL belongs to a platform this Actor does not download from. |
| `invalid_url` | Not an http(s) URL, or it points to a non-public network address. |
| `not_media` | The URL returned a web page or JSON instead of a media file. |
| `http_error`, `unreachable` | The server answered with an error or could not be reached. |
| `too_large`, `too_long` | The file exceeds 500 MB or its header shows more than `maxDurationMinutes`. |
| `download_timeout`, `download_too_slow`, `live_stream` | The download took over 10 minutes, the server sent data slower than 50 KB/s, or the URL is a live radio stream. |
| `undecodable`, `no_audio` | The file cannot be read or has no audio track. |
| `invalid_language` | The `language` code is not one Whisper supports. |
| `charge_limit` | Not enough budget left under your maximum cost per run. |
| `high_quality_unavailable` | `quality: "high"` was requested but the high-precision engine is not available on this Actor right now. Nothing is charged; use `standard`. |
| `interrupted` | (on `partial` items) The run was aborted gracefully before the file finished; the transcript covers the minutes already charged. |
| `invalid_input` | `mediaUrls` is missing or empty. |
| `internal_error` | Unexpected failure on our side. If it happens before any audio is transcribed, nothing is charged for that file. If it happens midway, the minutes already transcribed are charged and delivered as a `partial` item. |

If no file in a run can be transcribed, the dataset has one `failed` item per URL and the run status message names the first reason; the same list is saved in the `OUTPUT` record of the key-value store. The run ends as succeeded when the cause is the input (bad URL, unsupported file) and as failed only on `internal_error`.

### Pricing

Pay per event:

- **Standard: US$ 0.006 per minute of audio transcribed** (event `audio-minute`), rounded up per file with 1 second of tolerance (a 10 second clip counts as 1 minute, a 4 minute file counts as 4, a 10 minute 40 second file counts as 11).
- **High precision: US$ 0.010 per minute** (event `audio-minute-high`), same rounding. It is charged only for blocks that Groq actually transcribed. Blocks that fell back to the base model, and blocks in which no speech is detected (these are not sent to Groq), are charged as standard.
- A small Actor start fee that Apify applies to every pay-per-event Actor.
- Files that are refused, skipped or fail before any audio is transcribed are **not** charged. Minutes already transcribed are charged even if a later internal error stops the file (delivered as `partial`).

Billing follows the audio actually decoded and transcribed, not the duration declared in the file header. Files are processed in blocks of up to 10 minutes and each block is charged after it is transcribed, so the maximum cost per run you set in Apify and `maxDurationMinutes` both stop the file at the limit: the audio up to the limit is delivered (`partial`) and only that audio is charged. `billedMinutes` shows what was charged for the file. In high mode the cost limit is checked at the high price, so a run can stop earlier than strictly necessary when some blocks would have fallen back to the cheaper standard price.

After every charged block the Actor saves the transcript so far in the key-value store record `PARTIAL-<file number>` (removed when the file finishes). If you abort the run gracefully, the transcript of the minutes already charged is also delivered as a `partial` dataset item with `errorCode: "interrupted"`. If you abort immediately or the run times out, nothing more can be written: the dataset has no item for the file in progress, but its transcript up to the last charged block is in the `PARTIAL-<file number>` record. Prefer graceful aborts, split very long files across runs, or raise the run timeout.

If the platform migrates or restarts the run, files already finished are not repeated and minutes already charged for a file are not charged again.

### Accuracy and speed

Word error rate (WER, lower is better) measured on public audio, same files through both quality levels. Portuguese set: 40 read sentences from the public FLEURS benchmark (9.4 min), and a LibriVox audiobook compared with the published text (its spelling is older than the spoken Portuguese, which inflates both numbers equally). English set: a LibriVox audiobook (4 min) with and without pink noise at about 5 dB signal-to-noise.

| Audio | standard (base) | high (large-v3-turbo) |
|---|---|---|
| Portuguese, read sentences, clean (FLEURS) | 13.4% | 3.9% |
| Portuguese, same sentences with noise at 5 dB (local run) | 49.6% | 11.1% |
| Portuguese audiobook, 20 min | 29.5% | 9.6% |
| English audiobook, clean, 4 min (local run) | 3.8% | 2.3% |
| English audiobook with noise at 5 dB, 4 min | 11.6% | 2.8% |

The base-model figures vary by a few points between runs of the same file (13.4% on the platform and 16.4% in a local run for the clean Portuguese sentences). These are read speech and audiobooks, a favourable case. We did not measure spontaneous conversation, phone-quality audio, overlapping speakers or accents; expect higher error rates there with both levels, and review the text before relying on it. The `base` model is clearly weaker outside clean English; that is the reason `high` exists. With `language` set to `auto`, the language is detected on the first 10 minutes and kept for the whole file, so files that switch language are transcribed poorly.

Speed: with `standard`, a 10 minute English file took about 85 seconds and a 69 minute podcast about 7 minutes. With `high`, a 9.4 minute file took about 24 seconds and a 20 minute file about 30 seconds. Your timings will vary. High precision depends on Groq's capacity: when Groq refuses a block (for example a rate limit), that block is transcribed on this Actor's CPU instead, which is slower.

### Call it from the API

```bash
curl -X POST "https://api.apify.com/v2/acts/plum_spear~aztec-audio-video-transcriber/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"mediaUrls": ["https://example.com/episode.mp3"], "outputFormats": ["text", "srt"]}'
```

Limit the spend of a single run with `maxTotalChargeUsd`:

```bash
curl -X POST "https://api.apify.com/v2/acts/plum_spear~aztec-audio-video-transcriber/runs?token=$APIFY_TOKEN&maxTotalChargeUsd=0.50" \
  -H "Content-Type: application/json" \
  -d '{"mediaUrls": ["https://example.com/episode.mp3"]}'
```

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("plum_spear/aztec-audio-video-transcriber").call(
    run_input={"mediaUrls": ["https://example.com/episode.mp3"], "language": "en"}
)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["status"], item.get("text") or item.get("error"))
```

### Use it from an AI agent (MCP)

Apify exposes this Actor as a tool through the Apify MCP server. Add the Actor to the server URL and authenticate with your Apify token:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=plum_spear/aztec-audio-video-transcriber",
      "headers": { "Authorization": "Bearer <APIFY_TOKEN>" }
    }
  }
}
```

The agent calls the tool with the same JSON as the input above and receives the dataset items. Set `maxDurationMinutes` and a maximum cost per run so an agent cannot start an unexpectedly large job.

### Privacy

Only public network addresses are fetched: the host name is resolved once per request, checked, and the connection is made to that address. Files are downloaded to the run's temporary storage and deleted when the file is done. The URL you submit is echoed in the output item.

With `quality: "standard"` (default) the audio is transcribed in the container and nothing is sent to an external transcription service.

With `quality: "high"` the audio, in blocks of up to 10 minutes, is sent over HTTPS to the Groq API (United States) and transcribed there with whisper-large-v3-turbo. Groq's own terms and privacy policy apply to that audio, and we do not control it. Do not use `high` for confidential, regulated or personal-data audio you are not allowed to share with a third party. Blocks of a `high` run that fall back to the base model stay inside the container.

# Actor input Schema

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

Direct links to audio or video files (mp3, wav, m4a, aac, flac, ogg, opus, mp4, webm, mkv, mov). Each link must return the file itself, not a web page. To transcribe a file you uploaded to an Apify key-value store, use the record's public URL (with its signature). YouTube, Instagram, TikTok and similar platforms are refused.

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

ISO 639-1 code of the spoken language (for example 'en', 'pt', 'es', 'de'). Use 'auto' to detect it from the audio.

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

'standard' runs Whisper base inside this Actor: your audio never leaves the run. 'high' sends the audio to Groq (United States) to be transcribed with whisper-large-v3-turbo, which is clearly more accurate, especially in Portuguese and in noisy audio. With 'high', your audio is processed by a third party; do not use it for confidential material. If Groq is unavailable for a block, that block is transcribed on this Actor's CPU and billed as standard (the item reports it). 'high' is billed with its own per-minute event.

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

Fields to include in each dataset item: 'text' (full transcript), 'segments' (list with start, end and text per phrase), 'srt' and 'vtt' (subtitle files as strings).

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

Audio beyond this length is not transcribed or charged. Files whose header already shows a longer duration are refused. Protects you from unexpectedly long inputs.

## Actor input object example

```json
{
  "mediaUrls": [
    "https://upload.wikimedia.org/wikipedia/commons/a/a1/Gettysburg_by_Britton.ogg"
  ],
  "language": "auto",
  "quality": "standard",
  "outputFormats": [
    "text",
    "segments"
  ],
  "maxDurationMinutes": 120
}
```

# Actor output Schema

## `dataset` (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 = {
    "mediaUrls": [
        "https://upload.wikimedia.org/wikipedia/commons/a/a1/Gettysburg_by_Britton.ogg"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("plum_spear/aztec-audio-video-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://upload.wikimedia.org/wikipedia/commons/a/a1/Gettysburg_by_Britton.ogg"] }

# Run the Actor and wait for it to finish
run = client.actor("plum_spear/aztec-audio-video-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://upload.wikimedia.org/wikipedia/commons/a/a1/Gettysburg_by_Britton.ogg"
  ]
}' |
apify call plum_spear/aztec-audio-video-transcriber --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,plum_spear/aztec-audio-video-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/0ij9tKH7BwHLcMm6K/builds/DQmcSUFtdFj4p4vd2/openapi.json
