# Audio and Video to Transcript — timestamps, SRT, VTT (`hermeskd/audio-to-transcript`) Actor

Audio or video URLs in, the spoken words out: transcript with timestamps, SRT and WebVTT subtitles, language detected, English translation on request. Pay per minute.

- **URL**: https://apify.com/hermeskd/audio-to-transcript.md
- **Developed by:** [Logan Parker](https://apify.com/hermeskd) (community)
- **Categories:** Agents, AI, Videos
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $6.40 / 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

Give it URLs of audio or video files (MP3, M4A, WAV, FLAC, OGG, Opus, WebM, MP4, MOV); it returns the transcript with segment timestamps and, on request, SRT and VTT subtitles, detecting the language unless told. Use it on recordings, podcasts, meetings or videos the user has given you a file link for. Agents cannot listen: one call turns a media link into timed text you can quote, search or summarise, an English translation of speech in other languages on request, and pricing is per started minute of media, so an 11-second clip costs less than a cent.

### Call it from an agent

Through Apify's MCP server (`https://mcp.apify.com`), tool `call-actor`:

```json
{"actor": "hermeskd/audio-to-transcript", "input": {"urls": ["https://raw.githubusercontent.com/SYSTRAN/faster-whisper/master/tests/data/jfk.flac"], "language": "en"}, "callOptions": {"maxTotalChargeUsd": 0.05, "timeout": 3600}, "waitSecs": 30}
```

The call returns the run's status and its `defaultDatasetId`. Read the records with `get-dataset-items`:

```json
{"datasetId": "<defaultDatasetId from the call>", "limit": 50}
```

As a dedicated tool, add `https://mcp.apify.com?tools=hermeskd/audio-to-transcript` to your MCP client. A short clip usually finishes inside the 30-second wait; a long recording takes longer (see Limits and run time), so when the call returns with the run still `RUNNING`, wait for it and then read the dataset. `maxTotalChargeUsd` caps what the run can charge: a file whose minutes would pass it is not transcribed and comes back as a `charge_limit_reached` record, uncharged.

### Input

| Field | Type | Required | Default | Meaning |
|---|---|---|---|---|
| `urls` | array of strings | yes | — | Media file URLs (MP3, M4A, WAV, FLAC, OGG, Opus, WebM, MP4, MOV), http or https, 1–20 per run, ≤ 500 MB and ≤ 120 minutes each. |
| `language` | string | no | `auto` | ISO 639-1 code to force the spoken language (99 languages, e.g. `en`, `de`, `es`, `zh`; `haw` for Hawaiian); `auto` detects it from the first speech. |
| `formats` | array of `text` · `srt` · `vtt` | no | `["text"]` | What to return inline, 1–3 of: `text` (the transcript and its timed `segments`), `srt` (SubRip subtitles), `vtt` (WebVTT subtitles). |
| `translateToEnglish` | boolean | no | `false` | Return an English translation instead of the source language — text, segments and subtitles alike; `language` still names the source language. Translation is Whisper base's own: good enough to follow the gist of common European languages, rough for others. |

Every limit above is enforced in the input schema (`minimum`/`maximum`, `maxItems`) and again in code.

### Output

One record per input URL.

| Field | Meaning |
|---|---|
| `url` | The input URL. |
| `ok` | `true` when the file was transcribed; `false` with `error`. |
| `language` | The spoken language as an ISO 639-1 code: the one given, or the one detected from the first speech; `null` when no speech was found and none was given. |
| `durationSec` | The media's duration in seconds. |
| `minutesCharged` | Started minutes of media duration charged for this file (11 s is 1, 61 s is 2). |
| `text` | With `formats` `text`: the full transcript, segments joined by spaces. Media with no speech gives `ok: true` and an empty string, and is charged — the file was listened to. |
| `segments` | With `formats` `text`: the transcript as `{start, end, text}` segments in seconds from the file's start, in order, never overlapping, never past `durationSec`. |
| `srt` | With `formats` `srt`: the segments as SubRip subtitles. |
| `vtt` | With `formats` `vtt`: the segments as WebVTT subtitles. |
| `error` | `{code, message}` when `ok` is `false`. |

Example record for the call above, as this Actor returned it for the same file in its build check on 2026-10-16:

```json
{
  "url": "https://raw.githubusercontent.com/SYSTRAN/faster-whisper/master/tests/data/jfk.flac",
  "ok": true,
  "language": "en",
  "durationSec": 11.0,
  "minutesCharged": 1,
  "text": "And so my fellow Americans ask not what your country can do for you, ask what you can do for your country.",
  "segments": [
    {"start": 0.0, "end": 11.0, "text": "And so my fellow Americans ask not what your country can do for you, ask what you can do for your country."}
  ]
}
```

### Events and prices

Pay per event; platform usage is included in the price, never passed through.

| Event | Title | Charged when | FREE / BRONZE | SILVER | GOLD / PLATINUM / DIAMOND |
|---|---|---|---|---|---|
| `audio-minute-transcribed` | Audio minute transcribed | One started minute of media duration transcribed. | $0.008 | $0.0072 | $0.0064 |

Apify also charges its standard `apify-actor-start` event: $0.00005 per GB of run memory (2 events, $0.0001, at the default 2,048 MB). The minimum maximum-charge a caller can set is $0.02.

Worked example on the FREE tier: a 30-minute podcast episode and a 45-second voice note cost 30 × $0.008 + 1 × $0.008 = $0.248, plus $0.0001 for the start, $0.2481 in all. Minutes are counted from the media's duration, silence included; nothing is charged for a failed file.

### Limits and run time

- 1–20 URLs per run, processed one after another; each file up to 500 MB and 120 minutes (a longer file is refused as `too_long` before it is decoded, uncharged).
- Each file has a download budget of 30 s plus 1 s per 2 MB of its size, so files download at 2 MB/s or faster; a download that sends nothing for 30 seconds is stopped. At most 2 requests a second go to one host.
- GitHub release links (`github.com/…/releases/download/…`) are refused with `robots_disallowed`: github.com's robots.txt disallows every user-agent it does not name (`User-agent: *` → `Disallow: /`), HermesBot included, and HermesBot obeys it. Put the file on `raw.githubusercontent.com` (a file committed to a repository) or your own storage and pass that URL.
- The first audio track is transcribed; a video's picture is ignored. The file's own container is read — playlists and manifests (M3U, HLS, DASH) are not media files and are refused.
- Speech is found by voice-activity detection and transcribed in windows of up to 28 seconds of speech by Whisper base; silence costs almost no processing. Segment times are accurate to about a second; words are not timed individually.
- Typical run time: the 11-second clip above in about 5–15 seconds, a third of it loading the model. Continuous speech takes about 13 seconds of one CPU core per minute of audio (12.7 s on a 28-minute audiobook reading, 13.4 s on a dense meeting), sparse speech (a meeting with pauses) about 10 (measured 2026-10-16 and 2026-10-17); the default 2,048 MB run has half a core, so it transcribes a minute of continuous speech in about half a minute, and a recording of more than about 60 minutes of continuous speech needs more memory (4,096 MB is one core, 8,192 MB two) or a run timeout above the default 3,600 seconds. Transcription runs one thread per core of the run's memory share (at least one, at most four). A file not finished within the run's time is `timeout`, uncharged.

### Errors

Errors are records with `ok: false` and `error.code`; none is charged.

| `error.code` | Meaning |
|---|---|
| `download_failed` | The URL did not return the file (status or network error), or the download was slower than its budget. |
| `no_audio_stream` | No decodable audio: the file has no audio track (a silent video), or it is not audio or video media at all (text, HTML, a playlist) — anything ffprobe cannot open as a single media file. |
| `too_long` | Over 120 minutes. |
| `too_large` | Over 500 MB. |
| `unsupported_media` | The file has an audio track, but its codec could not be decoded. |
| `robots_disallowed` | robots.txt forbids fetching the URL. |
| `blocked_host` | The host is on the deny-list (YouTube, TikTok, Instagram and other platforms whose terms forbid automated download), or resolves to a private or local address. |
| `timeout` | The file did not finish within the run's time budget. |
| `internal_error` | Unexpected failure; message carries the class. |
| `charge_limit_reached` | The run's maximum total charge could not pay for this file's minutes; it was not transcribed. |
| `invalid_input` | The run input passed the input schema but cannot be used (for example no URL at all); the message names the field. One record per input given, or one with `url: null` when none was given; nothing is processed. |

The run fails only when every input failed with an Actor-side code (`internal_error`, `timeout`), so a broken Actor is visible. Every other run succeeds — including one where nothing was transcribed because of the caller's own inputs (bad URL, robots, deny-list, no audio, too long, invalid input, the charge limit), or where those and Actor-side failures are mixed.

### Sources and terms

The source is the caller's media. Process only media you have the right to process. Fetching follows the Hermes fetch policy: robots.txt is obeyed for the user-agent `HermesBot` (and `*`), and a disallowed URL is refused without a request; platforms whose terms forbid automated access — YouTube, TikTok, Instagram among them — and people-search sites are on a deny-list and refused without a request; private and local addresses are refused, after DNS and on every redirect; the user-agent is `HermesBot/1.0 (+https://apify.com/hermeskd)`; no login, cookies, credentials, CAPTCHA solving, evasion or proxies are used. The words spoken are returned to the caller as they are; no speaker is named and no voice is identified.

The models run inside the Actor; no audio leaves the run. Whisper base (OpenAI's weights, MIT licence) runs as the float32 ONNX export published by the sherpa-onnx project (k2-fsa, Apache-2.0), with Silero VAD (MIT) to find speech and FFmpeg to decode; each model file is checked against a pinned SHA-256 when the image is built.

### Not affiliated

This Actor is not affiliated with OpenAI, the sherpa-onnx (k2-fsa) project, Silero or the FFmpeg project. Whisper, sherpa-onnx, Silero VAD and FFmpeg are used under their open-source licences. YouTube, TikTok and Instagram are named only to say they are refused.

### Related Hermes Actors

- `pdf-to-markdown` — PDF documents to Markdown, with OCR for scanned pages.
- `document-to-markdown` — Word, PowerPoint, Excel and other office files to Markdown.
- `image-to-text` — text read from images by OCR.
- `video-frames` — frames and metadata from a video.
- `image-upscale` — image upscaling.
- `url-to-markdown` — a web page to Markdown.
- `audio-video-convert` — audio and video format conversion.

### Changelog

- 1.0 (2026-10-16) — first release: speech in 99 languages to text with segment timestamps, SRT and WebVTT subtitles, English translation; Whisper base with voice-activity detection.

# Actor input Schema

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

Audio or video file URLs (MP3, M4A, WAV, FLAC, OGG, Opus, WebM, MP4, MOV), http or https, 1–20 per run, up to 500 MB and 120 minutes each. Each URL becomes one record.

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

ISO 639-1 code to force the spoken language (e.g. en, de, es); auto detects it from the first speech.

## `formats` (type: `array`):

What to return inline: text (the transcript and its timed segments), srt (SubRip subtitles), vtt (WebVTT subtitles); 1–3 of them.

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

Return an English translation instead of the source language (text, segments and subtitles alike); language still names the source language.

## Actor input object example

```json
{
  "urls": [
    "https://raw.githubusercontent.com/SYSTRAN/faster-whisper/master/tests/data/jfk.flac"
  ],
  "language": "en",
  "formats": [
    "text"
  ],
  "translateToEnglish": false
}
```

# Actor output Schema

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

One record per input URL with ok, language, durationSec, minutesCharged, text, segments, srt, vtt and error.

# 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://raw.githubusercontent.com/SYSTRAN/faster-whisper/master/tests/data/jfk.flac"
    ],
    "language": "en"
};

// Run the Actor and wait for it to finish
const run = await client.actor("hermeskd/audio-to-transcript").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://raw.githubusercontent.com/SYSTRAN/faster-whisper/master/tests/data/jfk.flac"],
    "language": "en",
}

# Run the Actor and wait for it to finish
run = client.actor("hermeskd/audio-to-transcript").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://raw.githubusercontent.com/SYSTRAN/faster-whisper/master/tests/data/jfk.flac"
  ],
  "language": "en"
}' |
apify call hermeskd/audio-to-transcript --silent --output-dataset

```

## MCP server setup

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

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/1WLRM97wtIovcDn1P/builds/Z2G9iO75XV4zSxHL7/openapi.json
