Disclosure: I publish the Apify actor used below (Audio Transcriber) and earn a fee when it runs. This post was written with AI help (Claude). It is a how-to for one specific tool; the current price is on the actor page.
You have a folder's worth of MP3 links (interviews, calls, lectures) and want one text transcript per file, without installing Whisper or paying per-seat for a transcription app. This sends a list of direct file URLs to one API call and gives back one row per file.
What it takes
A list of direct links to the audio files (mp3, wav, m4a, ogg, flac, mp4, webm, mov). Not YouTube or other page links, only the media file itself. Whisper runs inside the actor, so there is no OpenAI key; you only need your Apify token.
curl
curl -s -X POST \
"https://api.apify.com/v2/acts/pennydata~audio-transcriber/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"urls": [
"https://example.com/interview-01.mp3",
"https://example.com/interview-02.mp3"
],
"language": "auto",
"outputFormats": ["text"],
"maxMinutesPerFile": 60,
"maxCostUsd": 1
}'
The sync endpoint waits for the run and returns the dataset rows as JSON. It has a time limit, so for long files or big batches use the async client below instead. Replace the example URLs with your own.
Node.js (many files, saved as .txt)
import { ApifyClient } from 'apify-client';
import { writeFileSync } from 'node:fs';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const urls = [
'https://example.com/interview-01.mp3',
'https://example.com/interview-02.mp3',
];
const run = await client.actor('pennydata/audio-transcriber').call({
urls,
language: 'auto',
outputFormats: ['text'],
maxMinutesPerFile: 60,
maxCostUsd: 1,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const row of items) {
if (row.status !== 'ok') { console.log(row.status, row.url, row.error ?? ''); continue; }
const name = new URL(row.url).pathname.split('/').pop().replace(/\.[^.]+$/, '');
writeFileSync(`${name}.txt`, row.text + '\n');
console.log(name, `${Math.round(row.durationSeconds)}s`, row.language);
}
Input options
| Field | Meaning |
|---|---|
urls |
Direct links to media files |
language |
auto (detect) or a code such as en, es, de
|
model |
base (default, more accurate) or tiny (about 1.5x faster) |
outputFormats |
text always; add srt and/or vtt for subtitle files in the key-value store |
maxMinutesPerFile |
Longer files are skipped and not charged (default 60) |
maxCostUsd |
Spending cap: a file that would exceed it is skipped and the run stops |
What comes back
One row per file. This is a real row from a local run of the actor's code on a public Wikimedia mp3 (a short German language-lesson clip; that run used a translate option not covered here, so the text is English), with the text and segments cut short:
{
"url": "https://upload.wikimedia.org/wikipedia/commons/f/fb/Deutsch_056_JM_v2.mp3",
"status": "ok",
"language": "auto-detected",
"durationSeconds": 237.15,
"billedMinutes": 2,
"text": "56 I would like to have a bread with butter I want to speak with Buddha. ...",
"segments": [
{ "start": 0, "end": 8.04, "text": "56 I would like" },
{ "start": 8.04, "end": 19.42, "text": "to have a bread with" }
]
}
Failed files come back as status: "failed" with an error, skipped ones as status: "skipped"; neither is charged. A SUMMARY record in the run's key-value store lists files, minutes billed and cost.
Cost
Pay per audio minute, rounded up per file, with a free allowance per run. Check the actor page for the current rate, and set maxCostUsd before a large batch.
Limits and what I did not test
- I did not run these exact curl and Node scripts against the live actor; the example URLs are placeholders. The row above is from a local run, so check field names on the actor's Output tab.
- Files up to 1.5 GB and 120 minutes. No speaker labels.
- Whisper is good on clear speech and worse with music, heavy accents or crosstalk. Timestamps can be off by a second or two. The
basemodel is more accurate thantiny. - Only transcribe audio you have the right to process.
Written with AI (Claude)













