Audio to Text & RAG Chunks: Offline Whisper, SRT, Timestamps
Pricing
from $30.00 / 1,000 audio minute transcribed (accurate)s
Audio to Text & RAG Chunks: Offline Whisper, SRT, Timestamps
Transcribe MP3, WAV, FLAC and OGG audio offline with Whisper. Timestamped RAG chunks, SRT and VTT subtitles, language detection. No API key needed.
Pricing
from $30.00 / 1,000 audio minute transcribed (accurate)s
Rating
0.0
(0)
Developer
Bongo Seakhoa
Maintained by CommunityActor stats
1
Bookmarked
2
Total users
1
Monthly active users
22 days ago
Last modified
Categories
Share
Transcribe MP3, WAV, FLAC and Ogg Vorbis audio with OpenAI's Whisper model, run on CPU inside the Actor. Every file gives you a transcript with [hh:mm:ss] paragraph stamps, SRT and WebVTT subtitles, a segments JSON with optional word timings, and RAG-ready chunks that carry their start and end time in seconds, so a search hit can point to the moment it was said. Give file links, a key-value store of uploads, or a podcast RSS feed. No API key and no outside AI service: the audio never leaves your run.
What it outputs
- Transcript row per file in the dataset: status, duration, detected language with its probability, minutes billed and the events charged, the full plain text and links to the files below.
- Markdown transcript (
.md): paragraphs with[hh:mm:ss]stamps, split at pauses. - Subtitles: SRT (
.srt) and WebVTT (.vtt), one cue per Whisper segment, split where the speaker pauses for a second or more. - Segments JSON (
-segments.json): start, end and text of every segment, plus per-word start, end and probability whenwordTimestampsis on. - RAG chunks: one dataset row per chunk, cut on segment boundaries, with
time_start_s,time_end_s, character offsets into the Markdown transcript and a stablechunk_id. - Estimate mode: duration, billable minutes and the projected price of each file, without transcribing or charging anything.
- Honest failures. Broken, unsupported, oversized or unreachable files get a status row with an error code and are never charged.
- No AI service. Everything runs inside the Actor's container: Whisper base or tiny (CTranslate2 int8 on CPU), Silero voice activity detection and the miniaudio decoders. Nothing is sent to a language model or a third-party API.
How to use it
- Paste direct links to audio files (MP3, WAV, FLAC or Ogg Vorbis) into Audio file URLs, or give a podcast RSS feed to transcribe its newest episodes.
- Or pick a key-value store that holds your files (upload them in Apify Console under Storage), optionally naming the record keys to use. Without keys, the run reads the records the store holds when the run starts; records added later are not included.
- Choose the model (
accurateorfast) and the outputs, or leave the defaults, then start the run. - Open the Output tab. The Transcripts view has one row per file with links to its files; the Chunks view lists the RAG chunks with their times.
Starting the Actor with no input transcribes a 16-second bundled synthetic sample so you can see the output format first. The sample is charged none of this Actor's events; Apify's standard Actor start fee still applies, as it does to every run.
From code or an AI agent
Run it through the Apify API and get the dataset rows back in one call (set APIFY_TOKEN to your API token from Console > Settings > API & Integrations):
$curl -X POST "https://api.apify.com/v2/acts/bongo-seakhoa~audio-to-text-rag-chunks/run-sync-get-dataset-items" -H "Authorization: Bearer $APIFY_TOKEN" -H "Content-Type: application/json" -d '{"sources": [{"url": "https://example.com/episode-12.mp3"}]}'
The synchronous endpoint waits at most 5 minutes, which fits a few minutes of audio. For longer files, start the run and wait for it with the Python client (pip install apify-client):
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("bongo-seakhoa/audio-to-text-rag-chunks").call(run_input={"sources": [{"url": "https://example.com/episode-12.mp3"}]})for item in client.dataset(run["defaultDatasetId"]).iterate_items():if item["record_type"] == "chunk":print(item["time_start_s"], item["text"][:80])
AI agents can call the Actor as a tool through Apify's MCP server (https://mcp.apify.com) by adding bongo-seakhoa/audio-to-text-rag-chunks to the server's Actors.
Input
{"sources": [{ "url": "https://example.com/episode-12.mp3" }],"rssFeedUrl": "https://example.com/podcast/feed.xml","rssMaxEpisodes": 3,"model": "accurate","language": "auto","wordTimestamps": false,"outputs": ["transcript", "segments", "srt", "vtt", "chunks"],"chunkSize": 1500,"chunkOverlap": 150,"maxMinutesPerFile": 180,"mode": "transcribe"}
| Field | Default | What it does |
|---|---|---|
sources | [] | Direct links to audio files, at most 500. Redirects are followed and every hop is checked. Linking a remote text file of URLs (requestsFromUrl) is not supported: paste the URLs. |
keyValueStoreId | none | A key-value store with audio files. Without keyValueStoreKeys, every record whose key ends in .mp3, .wav, .flac, .ogg or .oga, or whose content type is audio, is used (up to 1,000). |
keyValueStoreKeys | [] | Record keys to use from that store. |
rssFeedUrl | none | A public podcast RSS 2.0 feed. Its newest episodes by publication date are transcribed from their audio enclosures. |
rssMaxEpisodes | 5 | How many of the newest episodes to take (1-100). |
model | accurate | accurate = Whisper base, fast = Whisper tiny (several times faster, less accurate, cheaper per minute). |
language | auto | auto detects the language from the first speech; or force one of Whisper's 99 language codes (en, de, es ...). |
wordTimestamps | false | Adds per-word times to the segments JSON. |
vad | true | Skips silence with Silero voice activity detection before transcribing. Billing is by audio duration either way. |
mode | transcribe | estimate downloads each file, reads only its duration and shows minutes and projected price, charging none of this Actor's events. |
outputs | all five | Any of transcript, segments, srt, vtt, chunks. The plain text is always in the transcript row. |
chunkSize | 1500 | Maximum characters per chunk (200-20,000). |
chunkOverlap | 150 | Characters of whole trailing segments repeated in the next chunk, at most half of chunkSize. |
maxMinutesPerFile | 180 | Longer files are cut here and marked partial (1-600). |
maxFileSizeMb | 500 | Larger files fail with FILE_TOO_LARGE (1-2,000, and never more than a quarter of the run's memory: 512 MB at the default 2 GB). |
perFileTimeoutSecs | derived | Empty: 2 minutes plus 3 times the expected transcription time of that file, at most 7,200 s. Set 60-7,200 to override. |
Output
The dataset holds two kinds of rows, told apart by record_type. Filter on it when you read the dataset through the API. The examples below are rows of the bundled sample, trimmed where marked with ....
A transcript row (record_type: "transcript"):
{"record_type": "transcript","document_id": "cd841e3f564be4a6","document_index": 0,"source": "bundled-sample:sample.mp3","source_type": "sample","status": "ok","error_code": null,"file_name": "sample.mp3","format": "mp3","model": "accurate","duration_s": 16.452,"minutes_billed": 0,"language": "en","language_probability": 0.9891,"text": "The community library opens at 9 in the morning on weekdays and closes at 6 in the evening. Visitors can borrow up to 6 books at a time. ...","transcript_url": "https://api.apify.com/v2/key-value-stores/.../records/file0001-cd841e3f564be4a6.md","srt_url": "https://api.apify.com/v2/key-value-stores/.../records/file0001-cd841e3f564be4a6.srt","vtt_url": "https://api.apify.com/v2/key-value-stores/.../records/file0001-cd841e3f564be4a6.vtt","segments_url": "https://api.apify.com/v2/key-value-stores/.../records/file0001-cd841e3f564be4a6-segments.json","segment_count": 3,"chunk_count": 1,"warnings": [],"charged_events": {},"elapsed_ms": 5593,"realtime_factor": 0.34}
A chunk row (record_type: "chunk"):
{"record_type": "chunk","document_id": "cd841e3f564be4a6","chunk_id": "14c0ad9be8f7fde2c5057026","chunk_index": 0,"text": "[00:00:00] The community library opens at 9 in the morning on weekdays and closes at 6 in the evening. ...","char_start": 0,"char_end": 290,"heading_path": [],"page_start": null,"page_end": null,"approx_tokens": 73,"time_start_s": 0.0,"time_end_s": 16.452,"source": "bundled-sample:sample.mp3","document_index": 0}
The SRT file of the same sample starts:
100:00:00,000 --> 00:00:06,400The community library opens at 9 in the morning on weekdays and closes at 6 in the evening. Visitors can
Every chunk row has document_id, chunk_id, chunk_index, text, char_start, char_end, heading_path, page_start, page_end and approx_tokens, the same fields as the other Actors listed below, so their chunks can share one vector index. Audio has no pages, so page_start and page_end are always null; the position in the recording is in time_start_s and time_end_s. markdown[char_start:char_end] of the transcript file is exactly the chunk's text.
minutes_billed is what a file is billed for in minutes on a pay-per-event run (0 for the sample, estimate mode and failed files). charged_events lists the events actually charged for the row, with their counts, and is the authoritative record.
Row status is ok, partial (some audio was not transcribed: the warnings say why), failed (nothing transcribed; error_code says why) or skipped (not started: CHARGE_LIMIT when your maximum charge was reached, TIMEOUT when the run was about to time out). If Apify restarts a run while a file is being charged, that file is not processed again: its row carries a BILLING_UNCERTAIN_AFTER_RESTART warning, and its charged_events may not list every charge made before the restart. A restart just after a file's chunk rows were saved can repeat those rows with the same chunk_id (the file is charged once), so dedupe chunks by chunk_id. The run's key-value store also gets a SUMMARY record with the counts for the whole run.
Error codes: UNSUPPORTED_FORMAT (with the name of what was found and a conversion hint), BLOCKED_HOST, BLOCKED_DESTINATION, RSS_PARSE_ERROR, DOWNLOAD_FAILED, FILE_TOO_LARGE, AUDIO_DECODE_ERROR, INVALID_INPUT, TIMEOUT, OUT_OF_MEMORY, WORKER_CRASHED, INTERNAL_ERROR. Warning codes: CHARGE_LIMIT_MINUTES, TIME_LIMIT, TRUNCATED_AT_MAX_MINUTES and DECODE_INCOMPLETE (these make a file partial), and NO_SPEECH_DETECTED, LOW_LANGUAGE_CONFIDENCE, POSSIBLE_REPETITION.
Pricing
This Actor is priced per event: you pay for what was transcribed.
| Event | Price | When it is charged |
|---|---|---|
audio-file-processed | $0.005 | Once per file that was decoded and transcribed (status ok or partial). |
audio-minute-accurate | $0.03 | Per started minute of audio transcribed in that file with the accurate model. |
audio-minute-fast | $0.02 | Per started minute of audio transcribed in that file with the fast model. |
Minutes are counted per file and rounded up: a 10-second clip is 1 minute plus the file fee, and a 45 min 20 s episode is 46 minutes. A file cut by your maximum charge, maxMinutesPerFile or the time limit is billed only for the minutes actually transcribed. A file that decodes but holds no speech (silence, music only) is still transcribed and billed; its row says NO_SPEECH_DETECTED.
Apify adds the Actor start fee (apify-actor-start) to every run: $0.00005 per GB of run memory, so $0.0001 at the default 2 GB. Chunks, files and dataset rows are not charged separately: this Actor does not use the apify-default-dataset-item event.
Worked examples, at the default 2 GB:
- A 45-minute podcast episode on
accurate: 1 x $0.005 + 45 x $0.03 + $0.0001 start fee = $1.3551. - The same episode on
fast: 1 x $0.005 + 45 x $0.02 + $0.0001 = $0.9051. - Ten 30-second voice notes on
accuratein one run: 10 x $0.005 + 10 x $0.03 + $0.0001 = $0.3501 (each file is at least 1 minute).
Never charged by this Actor: files that fail (unsupported, too large, unreachable, undecodable), files skipped at your maximum charge or the run timeout, the bundled sample, and estimate mode. Only Apify's start fee applies to those runs.
Your maximum charge is respected. Before each file the Actor checks what is left of your run's maximum total charge, transcribes only the minutes it can charge for, and keeps one minute's price unspent below your limit. A long file is cut at the affordable minute and marked partial with CHARGE_LIMIT_MINUTES; a file that cannot be afforded at all, and every file after it, is listed as skipped with CHARGE_LIMIT and is not charged. The run normally finishes as Succeeded. Rerun with a higher limit for the rest.
Limits: read before you buy
- Formats: only MP3, WAV, FLAC and Ogg Vorbis are accepted, detected from the file's bytes, not its name. M4A/AAC (iPhone Voice Memos, Zoom recordings), Opus (WhatsApp voice notes), WebM and video files are refused with
UNSUPPORTED_FORMAT, because the only decoders for them are under GPL or LGPL licences. Convert them first, for exampleffmpeg -i input.m4a -ac 1 -ar 16000 output.mp3. A WAV whose recorder stopped before writing its header sizes fails withAUDIO_DECODE_ERRORand a hint to re-save it. - No social or streaming links: YouTube, TikTok, Instagram, Vimeo, X/Twitter, Facebook, Twitch, SoundCloud and Spotify links (and their short links and media CDNs) are refused by design with
BLOCKED_HOST, also when a redirect leads there. Only process audio you have the rights to, and that includes podcast feeds. - Accuracy: Whisper base and tiny are the small Whisper models. They trail the larger ones, especially outside English, with accents, crosstalk, noise and specialist terms. Whisper can invent or repeat text on music, silence or noise (
POSSIBLE_REPETITIONflags the obvious cases). Review transcripts before relying on them. - No speaker labels and no translation: there is no diarization (who said what) and the transcript stays in the spoken language.
- Speed: measured on Apify at the default 2 GB on synthetic speech clips of up to 4.4 minutes (clean, over music and in noise),
accuratetook about 20-26 s of processing per audio minute andfastabout 8-20 s (slowest in noise), plus about 10 s per file to start the model. From those figures, a 1-hour file would take roughly 20-26 minutes onaccurate; that is an estimate, since no longer file was measured. Real recordings with accents, crosstalk or noise can take longer. 4 GB is about 1.5 times faster; 1 GB is about half as fast. The Actor's default run timeout is 1 hour, which fits roughly 2 hours of audio onaccurate; raise the run timeout for bigger batches. When the run or a file's time limit is reached, the transcript so far is kept, markedpartial(TIME_LIMIT) and billed only for the minutes done, and files not yet started are listed asskippedand not charged. - Word timestamps are approximate (about 0.2-0.5 s).
- Language detection looks at the first speech in the file. Short or noisy clips can be misdetected (
LOW_LANGUAGE_CONFIDENCE); setlanguagewhen you know it. - Files run one after another, not in parallel. The run's disk is at least twice its memory.
- Podcast feeds: public RSS 2.0 only (no login, no Atom). Feeds with DTDs or entity declarations are refused for safety.
- Links: only public http(s) addresses are fetched, and every redirect is checked the same way. Links to private, local or cloud-metadata addresses, and links carrying a user name or password, fail with
BLOCKED_DESTINATIONand are not charged. - Per run: at most 500 listed URLs, up to 1,000 records read from a key-value store and up to 100 feed episodes; files up to
maxFileSizeMbbut never more than a quarter of the run's memory.
FAQ
Is my audio sent to an AI service? No. Whisper runs inside the Actor's own container on CPU, with model weights baked into the image. Nothing is sent to OpenAI or any other API.
Why is a file partial? Some of its audio was not transcribed: your maximum charge (CHARGE_LIMIT_MINUTES), the time limit (TIME_LIMIT), maxMinutesPerFile (TRUNCATED_AT_MAX_MINUTES) or a file that is damaged or cut off towards the end (DECODE_INCOMPLETE: the decoder stopped early, or the audio ends well before the length its header declares). You pay only for the minutes transcribed.
Can I transcribe a YouTube video or a Spotify episode? No, by design. Use the podcast's public RSS feed or a direct file link instead, for audio you have the rights to.
My file is M4A or Opus. Convert it to MP3, WAV or FLAC first (see Limits). The Actor refuses those formats rather than ship GPL or LGPL decoders.
How do I check the price before a big batch? Run with mode: "estimate". Each file's row shows its duration, the minutes that would be billed and the projected price, and nothing is charged except Apify's start fee.
Can I read the output from code? Yes. See "From code or an AI agent" above: read the dataset items and filter on record_type, or download the files from the run's key-value store.
Something does not work. Open an issue on the Actor's Issues tab with the run link and, if you can share it, a file that shows the problem. Issues are read on a best-effort basis; there is no guaranteed response time.
Privacy and data retention
The Actor reads only the URLs, feed and key-value store records you give it. Your audio is processed inside your run and is not sent anywhere else. The log contains counts, status and error codes, never file content, transcripts or URLs. The Actor keeps nothing after the run ends: the outputs live in your run's dataset and key-value store under your Apify account, and Apify's storage retention applies to them.
More Actors from the same developer
These Actors share one output format for chunks, so their results can go into the same index:
- PDF to Markdown & RAG Chunks: PDFs, tables to CSV, offline OCR.
About the sample and the tests
The bundled sample and every test file used to develop this Actor are synthetic: generated for the purpose and labelled synthetic (see sample/PROVENANCE.md in the source). The sample is computer-generated speech (CMU Flite) reading a three-sentence script written for it. No third-party recordings were used.
Licences
The Actor is proprietary software by Bongo Seakhoa. It is built on open-source components under permissive licences (faster-whisper, CTranslate2, the OpenAI Whisper weights as converted by Systran, Silero VAD, miniaudio and ONNX Runtime under MIT; Hugging Face tokenizers under Apache-2.0, among others). certifi (MPL-2.0) is included unmodified. The CTranslate2 Linux wheel also contains Intel oneMKL under the Intel Simplified Software License and the GCC runtime library libgomp under GPL-3.0 with the GCC Runtime Library Exception, both unmodified. No FFmpeg, PyAV or libsndfile is used. The full list and licence texts are in the THIRD_PARTY_NOTICES file shipped with the Actor image.