Podcast episodes,
transcribed to light.
16 kHz mono · verbatim · millisecond timecodes
Tapeclerk is podcast transcription for people who publish audio. Paste an Apple Podcasts, Spotify or RSS link — or a direct audio file — and Tapeclerk finds that episode's own audio, transcribes it with Whisper running on our own hardware, and hands back a timestamped transcript with speaker turns, chapter marks and show notes. One link in, one transcript out.
RESOLVED Apple, Spotify or a direct RSS / mp3 link. This box classifies the link in your browser and jumps to the run; it does not upload anything.
What you get when a podcast episode is transcribed
Podcast transcription is turning the audio of an episode into text that keeps the timing. Text without timing is a paragraph; text with timing is a document you can search, quote, caption and link to the second. Every transcript Tapeclerk returns carries the same five things:
- Segments with timecodes — start and end offset of every spoken passage, to the millisecond, in the order they were spoken.
- Speaker turns — who is talking, so a two-person interview reads as an interview rather than a wall.
- Chapter marks — the passage boundaries an editor would cut, at the timecodes a player can jump to.
- Show notes with verbatim quotes — lines lifted out of the transcript word for word, never rewritten.
- The same output in five formats — JSON, plain text, SRT, VTT and Markdown, from one set of segments.
- Accepted links
- Apple Podcasts episode and show links, Spotify episode and show links, an RSS feed URL, or a direct
.mp3/.m4a/.wavfile. - Audio handling
- Whatever the publisher serves is decoded and resampled to 16 kHz mono PCM before the model sees it, so one guarantee about the input holds for every feed on the internet. Downloads are capped at 400 MB and stopped mid-stream at the cap, not after it.
- Engine
whisper.cppwith Silero voice-activity detection, on our own machines. No third-party speech API is in the request path.- Languages
- Whichever language the loaded model is for. If the audio comes back in a language that model is not configured for, the answer is
LANG_UNSUPPORTED— not a confident paragraph of the wrong language. - Episode limits
- 4 hours of audio per episode. Longer recordings are split into parts and the transcripts stitch back in order.
- Cost
- One credit per 30 minutes of audio, rounded up — a 63-minute episode costs 3 credits. A run that does not produce a transcript costs nothing.
- Retention
- The fetched audio is deleted by the step that writes the transcript. Nothing is used to train a model.
A transcript, written top to bottom
This is the interface a job shows: the wire code first, then the first lines while the model is still writing, then the chapter count when it files. The dialogue below is sample text — the verbatim model output is in the next section, with the command that produced it.
- 00:00:12HostWelcome back. Today: how sound gets folded into memory.
- 00:00:26GuestThe thing nobody warns you about is that it is the voice, not the words.
- 00:00:41HostSo a stranger reading a book you loved at nine —
- 00:00:52Guest— becomes that book. Ten years on, you hear the tape and you are nine again.
- 00:01:14HostIs that nostalgia, or is it a retrieval cue?
- 00:01:29GuestBoth. The experiment that separates them is ugly, and I will get to it.
- 00:01:58GuestThey played people a clip and tested recall with the air-conditioning still running.
- 00:02:16HostAnd the hum helped.
- 00:02:21GuestThe hum helped enormously. Context is not noise; context is the handle.
- 00:02:47HostWhich is why a transcript without timecodes is only half the file.
- 00:03:05GuestThe timecode is the handle on the handle.
Real output, verbatim
A landing page that shows a mock-up proves nothing, so here is the actual transcript of the clip this pipeline was built and tested against: 12.5 seconds taken from the Hard Fork episode used to probe the resolver, run through the command underneath it. Nothing has been tidied.
- 00:00:00,000Speaker 1Now, I thought you were going to bring up another story which happened last night, which
- 00:00:04,800Speaker 1is so you and I last night are having drinks with, let's say, a very fancy technology person.
- 00:00:11,280Speaker 2Yeah.
- 00:00:12,280Speaker 2A legend.
whisper.cpp base.en · detected language en · 4 segments · monotonic offsets 0 → 12 480 ms
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
Two things worth noticing. The first line ends mid-sentence, because the model stopped where the audio ran out — a transcript that finished the sentence would be inventing it. And "a very fancy technology person" is exactly what was said: no capitalisation cleverness, no corrected name, no dropped hesitation.
How the podcast transcription works
One machine, three heads. Every link is threaded through the same path, and the honest version of "how it works" is a schematic rather than a screenshot.
-
Resolve — what kind of link is this?
An Apple episode link is not an audio URL, and neither is a Spotify one. Tapeclerk classifies the link first, then goes and gets the episode's own enclosure: for Apple, the title, show and duration come off the episode page's JSON-LD, the show's feed comes from the iTunes lookup API, and the audio comes from the RSS item whose title, duration and publish date match the episode. An RSS or mp3 link skips all of that and is used as given.
-
Fetch — audio and metadata, politely
The enclosure is streamed, not buffered, with a byte cap that stops the request mid-stream rather than after the fact. Requests identify themselves as a browser where a platform requires it, follow a bounded number of redirects, and treat a feed's 15-minute cache as the only thing worth remembering. Anything that answers 401 or 403 is reported as
AUTH_REQUIRED, not retried into the ground. -
Transcribe — speakers, chapters, timecodes
Audio is decoded to 16 kHz mono, run through Whisper with Silero voice-activity detection, and long episodes are chunked and run one at a time so peak memory stays flat on a two-hour recording. The segments come out with the offsets the model reported; the render step is what turns them into JSON, text, SRT, VTT and Markdown.
Podcast transcript formats, and which one you want
Five renderings of the same segments. One set of timings, so an SRT and a VTT cut from the same run never drift apart by a millisecond.
| Format | Shape | Use it for |
|---|---|---|
| .json | Segments, offsets in ms, speaker, language, duration | The source of truth: your app, your site, a search index, an LLM context window. |
| .txt | One line per segment, plain text | Reading, diffing, grep, pasting into a doc. |
| .srt | Index, 00:00:04,800 --> timestamps, comma milliseconds | Video editors and caption tools; burned-in subtitles. |
| .vtt | WEBVTT header, dot milliseconds, cue styling | The <track> element of a web player, YouTube uploads. |
| .md | Headings per chapter, speaker in bold, timecode anchors | Publication: an episode page, a newsletter, an archive. |
SRT or VTT for a podcast?
SRT if a human is going to open it in an editor, VTT if a browser is going to load it. SRT is the oldest and most widely imported caption format: index, timing line, two lines of text, blank line. VTT is its successor for the web — a header line, dot rather than comma before the milliseconds, and cue settings such as position and styling, which SRT cannot express. If you publish the episode on a site with a <video> or <audio> element, VTT is the one the player will read.
Podcast transcription errors, named
Most transcription services fail with a shrug: error, 500, "something went wrong", credits debited. Podcast links fail for specific and knowable reasons, so this is the entire failure surface, with the fix that goes with each one. The code on the left is the literal string the API returns and the UI prints.
| Code | Why it happens | What you do |
|---|---|---|
| DRM_LOCKED | The only copy of this episode sits behind a subscriber paywall. | Paste the RSS or file URL you are entitled to, or skip the episode. |
| AMBIGUOUS_EPISODE | A show link, or a feed with two items that match on title, duration and date. | Pick one of the returned candidates; the response carries them. |
| METADATA_ONLY | You sent the show's page, which has no audio on it. | Open an episode and copy that link, or pick from the candidates. |
| BAD_LINK | No scheme, a host that does not resolve, or a redirect that loops. | Add https:// and check the link resolves in a browser. |
| NOT_FOUND | 404, 410, or an episode that is no longer in the show's feed. | Find the current episode link; old show pages rot constantly. |
| NO_AUDIO | The feed item exists and has no enclosure — a trailer, a subscriber-only drop. | Nothing. This one is the publisher's, not yours. |
| AUTH_REQUIRED | 401 or 403: a private feed, or a platform that dislikes the request. | Paste the direct file URL, or your own feed URL with credentials. |
| TOO_LONG | Above 4 hours of audio, or above 400 MB. | Split the recording; transcripts stitch back in order. |
| NO_MODEL | No speech model is loaded for this job. | Try again; this is our side of the wire, not yours. |
| LANG_UNSUPPORTED | The audio is in a language the loaded model is not for. | Request that language; nothing is guessed in the meantime. |
| RATE_LIMITED | Too many runs from one key inside the window. | Back off and retry; the response carries when to come back. |
| NO_CREDITS | The account is out of credits. | Top up. Nothing was consumed to tell you this. |
No credits are debited for any of them. A run costs nothing unless a transcript came out of it, which is the only honest way to bill for a service whose most common failure is somebody else's paywall.
Who the transcript is for
Podcasters
Publish the transcript with the episode, pull five clips an hour with the timecodes already attached, and cut the show notes from lines people actually said.
Content and SEO teams
Turn a back catalogue into indexable pages and answer-engine-visible text, and keep the wording verbatim so a quotation survives being quoted.
Developers
One call, one link, JSON out. Failures arrive as named codes rather than a message string you have to pattern-match for three years.
Accessibility and archives
Captions in VTT for the player, a text record for the archive, and speaker turns kept so a conversation reads as a conversation.
The API in one request
The pipeline is one Elixir module today and is being wired to HTTP as this is written. The resolve step already behaves the way the public endpoint will, and the contract codes above are fixed — that is why they are on this page before the URL is.
curl -X POST https://tapeclerk.com/v1/transcriptions \
-H "authorization: Bearer $TAPECLERK_KEY" \
-H "content-type: application/json" \
-d '{"url":"https://podcasts.apple.com/us/podcast/id1528594034?i=1000786488728"}'
HTTP/1.1 202 Accepted
{"job_id":"tc_9f2c…","code":"RESOLVED",
"episode":{"show":"Hard Fork","title":"Meta Shifts the Blame …","duration_s":3776},
"credits_cost":3,"status_url":"/v1/jobs/tc_9f2c…"}
202 and a job, never a promise about how long the run takes. Poll the status URL for progress as a fraction of elapsed audio time, and the transcript itself in any of the five formats when the job files. Draft schema; the path may move a segment before it is public, the codes will not.
What happens to the audio
The fetched file is deleted by the same step that writes the transcript, in the same function, before the job reports done. The workspace has a byte cap, so a run cannot fill a disk on its way to being deleted. Audio is not used to train anything, and no speech API is in the path — the model runs on a machine we control.
The longer version, including which code deletes the file and how to check, is on how we do it.
Questions people actually ask
How do I transcribe a podcast episode from a link?
Paste the episode link. Tapeclerk accepts an Apple Podcasts episode or show link, a Spotify episode or show link, an RSS feed URL, or a direct .mp3, .m4a or .wav file. It resolves the link to the episode's own audio, fetches it, transcribes it and returns a timestamped transcript. Nothing to upload, and no player to record off.
Is podcast transcription accurate enough to publish?
The transcript is verbatim, with the timecodes the model reported, and it is labelled machine output rather than a human transcript. Proper nouns, and the boundary where two speakers talk over each other, are the two things worth checking before you publish. A transcript that silently "fixes" a name is worse than one that leaves it odd.
What is the difference between SRT and VTT subtitles?
SRT is plain text — an index number, a timestamp with a comma before the milliseconds, and the caption lines. VTT is WebVTT: a WEBVTT header, dot rather than comma before the milliseconds, and cue settings that carry styling and position. Both come out of the same segments here, so their timings never differ.
Does publishing transcripts help podcast SEO?
A transcript is the only way a search engine or an answer engine can see what was said in an episode. It gives the episode page text to rank on, gives a listener something to skim, and gives an answer engine a passage it can quote with a link. It is not a ranking trick on its own — the transcript has to sit on an indexable page for the episode, not behind a player.
Can you transcribe an Apple Podcasts or Spotify link directly?
Yes. For Apple, metadata comes off the episode page's JSON-LD and the audio comes from the show's RSS enclosure, matched by title, duration and publish date. For Spotify it comes from the public oEmbed metadata and the show's feed. Where a platform does not expose playable audio, you get AMBIGUOUS_EPISODE or METADATA_ONLY with the candidates it could match — not a guess at an episode.
How long does it take to transcribe a one hour episode?
It depends on the length of the episode and what else is in the queue, so there is no number here to be disappointed by. What the job does instead is report progress as a fraction of elapsed audio time and stream the first lines while the model is still writing, so the useful signal is the first line appearing.
Do you keep the audio after transcription?
No. The fetched audio is deleted as soon as the transcript is written — by the same step that produces the transcript, not by a cleanup job that runs later. Audio is never used to train a model.
What happens when a link cannot be transcribed?
One of a fixed set of codes: DRM_LOCKED, AMBIGUOUS_EPISODE, METADATA_ONLY, BAD_LINK, NOT_FOUND, NO_AUDIO, AUTH_REQUIRED, TOO_LONG, NO_MODEL, LANG_UNSUPPORTED, RATE_LIMITED, NO_CREDITS. Each arrives with a sentence a listener can read and the fix that goes with it, and none of them costs a credit.
Paste a link. Read the tape.
Apple Podcasts, Spotify, RSS or a file. Speakers, timecodes, chapters and show notes — verbatim, and deleted behind us.