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.

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:

Accepted links
Apple Podcasts episode and show links, Spotify episode and show links, an RSS feed URL, or a direct .mp3 / .m4a / .wav file.
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.cpp with 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.

sample · podcasts.apple.com/us/podcast/id1528594034 RESOLVED 00:00:00 / 38:07
  1. 00:00:12HostWelcome back. Today: how sound gets folded into memory.
  2. 00:00:26GuestThe thing nobody warns you about is that it is the voice, not the words.
  3. 00:00:41HostSo a stranger reading a book you loved at nine —
  4. 00:00:52Guest— becomes that book. Ten years on, you hear the tape and you are nine again.
  5. 00:01:14HostIs that nostalgia, or is it a retrieval cue?
  6. 00:01:29GuestBoth. The experiment that separates them is ugly, and I will get to it.
  7. 00:01:58GuestThey played people a clip and tested recall with the air-conditioning still running.
  8. 00:02:16HostAnd the hum helped.
  9. 00:02:21GuestThe hum helped enormously. Context is not noise; context is the handle.
  10. 00:02:47HostWhich is why a transcript without timecodes is only half the file.
  11. 00:03:05GuestThe timecode is the handle on the handle.

2 speakers · 4 chapters · 38:07 episode

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.

  1. 00:00:00,000Speaker 1Now, I thought you were going to bring up another story which happened last night, which
  2. 00:00:04,800Speaker 1is so you and I last night are having drinks with, let's say, a very fancy technology person.
  3. 00:00:11,280Speaker 2Yeah.
  4. 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.

  1. 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.

  2. 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.

  3. 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.

The tape path A link enters at the left as a tag, threads through three heads — resolve, fetch and transcribe — and leaves at the right as a transcript stack, above a timecode ruler carrying the episode's chapter marks. link 01 Resolve what kind of link is this? apple · spotify · rss · mp3 02 Fetch audio and metadata, politely byte cap · resumable 03 Transcribe speakers, chapters, timecodes verbatim · whisper.cpp Transcript json · txt · srt · vtt · md 00:00 05:00 10:00 15:00 20:00 25:00 30:00 38:07
The dash is the tape actually moving. The diamonds on the ruler are chapters — the same marks the transcript carries, at the same timecodes, which is what makes them clickable in a player.

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.

What each transcript format is, and what it is for
FormatShapeUse it for
.jsonSegments, offsets in ms, speaker, language, durationThe source of truth: your app, your site, a search index, an LLM context window.
.txtOne line per segment, plain textReading, diffing, grep, pasting into a doc.
.srtIndex, 00:00:04,800 --> timestamps, comma millisecondsVideo editors and caption tools; burned-in subtitles.
.vttWEBVTT header, dot milliseconds, cue stylingThe <track> element of a web player, YouTube uploads.
.mdHeadings per chapter, speaker in bold, timecode anchorsPublication: 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.

The complete list of failure codes, what each means, and what to do about it
CodeWhy it happensWhat you do
DRM_LOCKEDThe 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_EPISODEA 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_ONLYYou sent the show's page, which has no audio on it.Open an episode and copy that link, or pick from the candidates.
BAD_LINKNo scheme, a host that does not resolve, or a redirect that loops.Add https:// and check the link resolves in a browser.
NOT_FOUND404, 410, or an episode that is no longer in the show's feed.Find the current episode link; old show pages rot constantly.
NO_AUDIOThe feed item exists and has no enclosure — a trailer, a subscriber-only drop.Nothing. This one is the publisher's, not yours.
AUTH_REQUIRED401 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_LONGAbove 4 hours of audio, or above 400 MB.Split the recording; transcripts stitch back in order.
NO_MODELNo speech model is loaded for this job.Try again; this is our side of the wire, not yours.
LANG_UNSUPPORTEDThe audio is in a language the loaded model is not for.Request that language; nothing is guessed in the meantime.
RATE_LIMITEDToo many runs from one key inside the window.Back off and retry; the response carries when to come back.
NO_CREDITSThe 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.

See a transcript being written