How we do it
This page exists so that the claims on the landing page have somewhere to be checked. It is the method, the model, the hardware, the numbers we enforce as limits, the thing we delete afterwards, and a list of the claims we deliberately do not make.
The pipeline, in the order it runs
An episode link is not an audio file, so the first job is not transcription, it is resolution. An Apple Podcasts episode page carries its own metadata as JSON-LD in the document head; the show's RSS feed comes from the iTunes lookup API; the audio URL is the <enclosure> of the feed item whose title, duration and publish date match the episode the page described. A Spotify link goes through oEmbed and the show's RSS feed. A link that is already a feed or an audio file is used as given, because pretending to be clever about the easy case is how software breaks.
Then fetch: the enclosure is streamed to disk with a byte cap enforced inside the stream callback, so an episode that lies about its length stops the request rather than filling a volume. Then transcribe: ffmpeg decodes whatever the publisher served into 16 kHz mono PCM, and whisper.cpp with Silero voice-activity detection writes the segments.
Two decisions in that sequence are worth stating plainly, because they are the ones that change the output:
- 16 kHz mono before the model, always. Feeds arrive as 44.1 kHz stereo, 48 kHz, AAC, Opus, and one as a 96 kHz lossless master. Whisper wants 16 kHz mono. Normalising first means one guarantee holds regardless of what the publisher's encoder did.
- VAD first, then chunk. Silero finds the speech; silence gets cut before the model sees it, which is where both the speed and the hallucination-resistance come from. Whisper handed four seconds of room tone will invent a sponsor read.
The model, and which one
whisper.cpp with the large-v3 model family on Metal, with smaller quantised models available for draft passes and cheap bulk runs. Not an API, because an API means somebody else's speech-to-text service in the request path and their retention policy in your privacy statement. The model choice is a trade you can see coming out of the transcript: large-v3 costs more compute and gives you the punctuation and the paragraphing that make a transcript readable; a smaller model is faster and dumber about proper nouns.
The language is the language the model was configured for. If the audio arrives in another one, the job returns LANG_UNSUPPORTED. This is the behaviour that looks like a defect until you have read a confident, fluent, entirely wrong translation of a German podcast.
Verbatim means verbatim
The text is what the model heard, at the offsets the model reported. Whisper adds punctuation and capitalisation — that is part of the model, not a post-processing pass we are hiding — and that is where the editing stops. Specifically, we do not run a spelling corrector over proper nouns, do not insert a period where the model left a comma, and do not merge two segments because the sentence reads better that way.
The reason is falsifiability. A corrected transcript is a claim about what was said. A verbatim one is a measurement of what a recogniser heard, with the timing attached, so you can jump to the audio and listen. When the model writes "HatGPT" instead of "GPT", that is visible in the transcript, which is what tells you to fix it. Every transcript ships with a marker saying it is machine output.
Limits, as numbers
- Episode length
- 4 hours of audio. Longer recordings are split into parts, transcribed one at a time, and the transcripts stitch back in order.
- Download size
- 400 MB, enforced mid-stream. The cap is checked on every chunk of the response body, so the transfer stops at the cap instead of after it.
- Workspace
- A byte cap on the scratch directory, checked before each download, so a queue of jobs cannot fill the volume on the way to being deleted.
- Redirects
- A bounded number, and a visited set. A feed that redirects to itself is
BAD_LINK, not a hang. - Feed cache
- 15 minutes per feed, because hitting a publisher's server once per episode per run is the difference between a crawler and a denial-of-service.
- Cost
- One credit per 30 minutes of audio, rounded up. No credits are taken for any failure, including the ones caused by your own paywall.
What we delete, and when
The fetched audio is removed by the same step that produces the transcript, before the job reports itself done — not by a sweeper that runs later and can be broken without anything noticing. The raw download is written to a scratch directory under a name unique to the job, so two runs for the same episode cannot read each other's file, and the delete is a plain File.rm in the transcribe function. You can check the claim in the code path rather than in this paragraph.
What is kept after the run: the transcript, the episode metadata, the job record, and the fact that the job ran. No audio, no model training, no third-party speech API.
What we do not claim
- No accuracy percentage. Word error rate is a property of a model, a language, an audio quality and a domain, measured against a reference transcript. Anyone publishing "99% accurate" for arbitrary podcasts is quoting the model card, not your episode.
- No latency promise. Transcription time depends on episode length and queue depth. The job reports progress as a fraction of elapsed audio time and streams the first lines while the model is still writing, which is the honest version of "fast".
- No perfect speaker labels. Speaker turns are inferred. Two people talking over each other in a remote interview recorded on laptop microphones come out as one turn more often than we would like, and the transcript shows the merge rather than pretending it is clean.
- No live transcription. This is a batch service on finished recordings.
Reproduce it yourself
The transcript on the landing page is the output of two commands on a 12.5-second clip. Any clip you have rights to works the same way, which is the point of running the model locally:
ffmpeg -ss 00:11:02 -t 12.5 -i episode.mp3 -ac 1 -ar 16000 clip16.wav
whisper-cli -m models/ggml-base.en.bin -f clip16.wav -l en -oj -of whout
The -oj flag is what makes the output checkable: JSON with per-segment offsets in milliseconds, so nobody has to take the timings on trust.
Changes to this page
- 2026-09-01
- First published with the landing page. Limits section reflects the enforced values in code, not the intended ones.