HomeHow we do it

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:

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

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.