Podcast Transcript Scraper ποΈ With Speaker Labels
Pricing
from $0.48 / 1,000 transcript returneds
Podcast Transcript Scraper ποΈ With Speaker Labels
Download podcast transcripts published by the shows themselves, with millisecond timestamps and speaker labels. Pass a podcast name or RSS feed; get full text, timed segments, speaker count and episode metadata as JSON or CSV. Episodes with no published transcript are reported, never charged.
Pricing
from $0.48 / 1,000 transcript returneds
Rating
0.0
(0)
Developer
Yaniv van der Stigchel
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
19 hours ago
Last modified
Categories
Share
Transcripts that podcast publishers provide themselves, in the open Podcasting 2.0 standard. Millisecond timestamps and speaker labels β the two things video captions almost never have.
What it does
- Download podcast transcripts in bulk
- Get timestamped segments with speaker labels
- Build a searchable archive of a show's back catalogue
- Export podcast transcripts to JSON or CSV
- Feed spoken-word text into a RAG pipeline or a language model
Why these are better than video captions
Publishers ship these deliberately, produced properly rather than guessed from audio:
00:00:00,240 --> 00:00:02,880Speaker 1: Please note that this show discusses...
Two things come free that automatic video captions do not give you:
- Speaker labels. Diarisation is expensive to compute and here it is already
done.
hasSpeakerLabelsandspeakerCountare on every row. - Real punctuation. These are edited files, not a stream of words.
The honest number
Roughly half of podcast feeds publish a transcript. Measured across 30 feeds
sampled from eight genres on 4 September 2026, 14 of 30 β 46.7% β carried the
<podcast:transcript> tag, at 114 to 171 tagged episodes per show.
The other half publish none, and no tool can extract text that was never
created. Those episodes come back as no-transcript-published and are never
charged. Anything claiming complete coverage of all podcasts is either running
speech recognition β a different and far more expensive product β or is wrong.
Sample size is 30, so treat 46.7% as a first reading rather than a settled figure.
You only pay for transcripts
errorCode | Meaning | Charged |
|---|---|---|
β (success: true) | A transcript | Yes |
no-transcript-published | Publisher provides none | No |
transcript-unreachable | Link published but the file would not fetch | No |
transcript-empty | File fetched but had no readable cues | No |
feed-unreachable | Feed did not respond or is not valid RSS | No |
show-not-found | No podcast matched that name | No |
no-episodes | Feed is valid but empty | No |
Input
| Field | Required | Description |
|---|---|---|
shows | yes | Podcast names or RSS feed URLs |
maxEpisodesPerShow | no | Newest first. Default 50. Your cost ceiling |
language | no | Two-letter code. A filter, not a preference |
includeSegments | no | Adds timed cues. Default false |
maxConcurrency | no | 1β15, default 5 |
{"shows": ["This Podcast Will Kill You", "https://feeds.megaphone.fm/vergecast"],"maxEpisodesPerShow": 100,"includeSegments": true}
A feed URL skips the directory lookup, so it is faster than a name.
Output
Every row has the same fields whether it succeeded or failed, so you can select columns without branching.
| Field | Type | Description |
|---|---|---|
success | boolean | True when the row carries a transcript |
sourceShow | string | The name or feed you supplied |
showTitle | string | The show's own title |
episodeTitle | string | Episode title |
episodeGuid | string | Feed's unique id β deduplicates across runs |
episodeUrl | string | Episode page, where published |
publishedAt | string | Publication date |
seasonNumber | integer | Where published |
episodeNumber | integer | Where published |
durationSeconds | number | From the feed, or the transcript's own span |
transcriptFormat | string | MIME type the publisher provided |
language | string | As the publisher declared it |
hasSpeakerLabels | boolean | True when speakers are named |
speakerCount | integer | Distinct speakers labelled |
segmentCount | integer | Timed cues β always reported |
fullText | string | Whole transcript as plain text |
segments | array | Timed cues with speakers; null unless requested |
errorCode | string | Null on success |
errorMessage | string | Human-readable explanation |
{"success": true,"sourceShow": "This Podcast Will Kill You","episodeTitle": "Ep 141: Scurvy","durationSeconds": 3723,"transcriptFormat": "application/srt","hasSpeakerLabels": true,"speakerCount": 3,"segmentCount": 842,"fullText": "Hi. My name's Elan. I'm a nurse practitioner...","errorCode": null,"errorMessage": null}
Use it from an AI agent (MCP)
This Actor is callable as a tool through the Apify MCP server, so Claude, ChatGPT, Cursor and VS Code can run it directly.
Add the server to your MCP client:
{"mcpServers": {"apify": {"url": "https://mcp.apify.com","headers": {"Authorization": "Bearer <YOUR_APIFY_TOKEN>"}}}}
Then ask for what you want in plain language β for example βget the transcripts for this podcastβ β and the agent calls cleanfeed/podcast-transcript-downloader with the right input. Every output field is described in the dataset schema, so the agent knows what it is getting back before it runs anything.
Call it from code
Python
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("cleanfeed/podcast-transcript-downloader").call(run_input={"shows": ["This Podcast Will Kill You"],})for item in client.dataset(run["defaultDatasetId"]).iterate_items():if item["success"]:print(item)
JavaScript
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('cleanfeed/podcast-transcript-downloader').call({shows: ["This Podcast Will Kill You"],});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items.filter((i) => i.success));
cURL
curl -X POST "https://api.apify.com/v2/acts/cleanfeed~podcast-transcript-downloader/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \-H 'Content-Type: application/json' \-d '{"shows": ["This Podcast Will Kill You"]}'
Related actors
| If you need | Use |
|---|---|
| Transcripts from YouTube videos instead | YouTube Transcript Scraper |
| Transcripts for a whole YouTube channel | YouTube Channel Transcript Scraper |
Limitations
- Roughly half of feeds publish no transcript. Measured across 30 feeds in eight genres on 4 September 2026, 14 of 30 carried the
<podcast:transcript>tag. The rest returnerrorCode: no-transcript-publishedand are never charged. Sample size is 30, so treat 46.7% as a first reading. - No speech-to-text fallback. Transcripts are read, not generated. Recognition costs real money per hour of audio and would change the price by a large multiple.
- Transcripts are the publisher's work and remain theirs. Quote with attribution; do not republish an episode's transcript as your own content.
- Quality varies with who produced it. Some are professionally edited, some are lightly-corrected machine output. The format and speaker count are reported so you can judge; the text is never rewritten.
- Speaker labels are as the publisher wrote them, often "Speaker 1" rather than a real name, with no published mapping.
- Transcript links point at the publisher's host, not ours. A dead link is reported as
transcript-unreachablerather than hidden.
FAQ
Why does this show return nothing?
Most likely the publisher does not include <podcast:transcript> in its feed. Around half do not. You get one row per episode saying so, at no charge.
Can you transcribe the audio when there is no transcript?
Not in this Actor. Speech recognition costs real money per hour of audio and would change the price by a large multiple. Shipping it silently inside a per-transcript price would be dishonest about what you are paying for.
What is the difference from a YouTube transcript scraper?
The source. YouTube gives automatic captions, which have no speaker labels and unreliable punctuation. These are files the publisher produced, usually with both.
Does it work for any podcast?
Any podcast with a public RSS feed, which is nearly all of them. Shows exclusive to one platform with no public feed cannot be reached.
Do I get speaker names?
You get the labels the publisher wrote. Many use Speaker 1 and Speaker 2 rather than real names, and no mapping is published. hasSpeakerLabels and speakerCount tell you what you have before you read the text.
Can I pass an RSS feed directly?
Yes, and it is faster β a feed URL skips the podcast-directory lookup entirely.
Notes
Only transcripts that publishers link from their own RSS feeds are downloaded,
via the open Podcasting 2.0 <podcast:transcript> tag. No audio is processed
and no transcript is generated β if a publisher provides none, that is reported.