DEV Community

PennyData
PennyData

Posted on Edited on

How to transcribe MP3 files to text in bulk with one API call

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
  }'
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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" }
  ]
}
Enter fullscreen mode Exit fullscreen mode

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 base model is more accurate than tiny.
  • Only transcribe audio you have the right to process.

I publish the Apify actor used above (Audio Transcriber) and earn a fee when it runs. Written with AI assistance (Claude).

Top comments (0)