Podcast Scraper: Shows, Episodes & RSS Feeds avatar

Podcast Scraper: Shows, Episodes & RSS Feeds

Pricing

from $0.56 / 1,000 record scrapeds

Go to Apify Store
Podcast Scraper: Shows, Episodes & RSS Feeds

Podcast Scraper: Shows, Episodes & RSS Feeds

Search Apple's podcast directory, then parse each show's RSS feed for full episode lists: titles, show notes, durations, audio URLs, season and episode numbers, and publish dates.

Pricing

from $0.56 / 1,000 record scrapeds

Rating

0.0

(0)

Developer

Arman Hossain

Arman Hossain

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

0

Monthly active users

5 days ago

Last modified

Share

Podcast Scraper: Show metadata from Apple's directory plus every episode's title, notes, duration and direct audio URL from the show's own RSS feed

Podcast Scraper finds shows in Apple's public podcast directory and then reads their RSS feeds directly, show metadata, artwork and genres, plus every episode's title, show notes, duration, season and episode numbers and the direct audio URL.

Two public sources, chained: Apple's directory for discovery, and the publisher's own RSS feed for the episode list. A discovery-only sweep is a handful of requests; turning episodes on adds one request per show.

Every cap is a run total. maxShows bounds the shows the whole run delivers across every term, ID and feed URL combined, and maxRecords bounds the rows — shows and episodes together — so a run cannot cost more than the number you set.

Agent skill: SKILL.md

https://api.apify.com/v2/key-value-stores/t7YoTxpZEJOWvw4Ug/records/podcast-directory-scraper.md

What you get

Records come in two shapes, told apart by recordType. Every record carries the show fields; episode records add the episode fields on top.

Output fieldMeaning
recordTypeshow or episode
sourceWhat produced the record, search:<term>, id:<collectionId> or feed:<url>
collectionId, appleUrlApple's ID for the show and its Apple Podcasts page (null for direct feeds)
showName, artistNameShow title and publisher
feedUrl, feedTitleRSS feed Apple points at, and the title inside that feed
genres, primaryGenreApple's genre list and the primary one
episodeCountApple's episode count for the show
artworkUrl, country, contentAdvisoryRating600 px artwork, storefront and explicit rating
title, link, author, publishedAtEpisode title / show name, canonical link, byline, ISO-8601 publish time
contentTextShow notes (episodes) or the channel description (shows) as clean plain text
episodeGuidThe feed's own identity for the episode
duration, durationSecondsitunes:duration verbatim, and parsed to seconds from HH:MM:SS, MM:SS or a bare count
audioUrl, audioLengthBytes, audioTypeThe enclosure, direct MP3 URL, byte size and MIME type
episodeNumber, seasonNumber, episodeTypeitunes:episode, itunes:season, and full / trailer / bonus
explicit, imageUrlPer-episode explicit flag (true / false / null) and episode artwork, falling back to show artwork
scrapedAtRun timestamp

A RUN_SUMMARY record in the key-value store holds per-run counts, the filters used, and any search, lookup or feed that failed.

Common use cases

  • Build a podcast search engine. Index shows by genre and episodes by title and show notes.
  • PR and guest sourcing. Find every show in a niche, with the publisher name and a contact trail.
  • Transcription pipelines. audioUrl is a direct MP3 link, ready to hand to a speech-to-text step.
  • Media monitoring. Watch a set of shows and pick up new episodes on a schedule.
  • Market research. Episode cadence, duration trends and genre mix across a category.

Quick start

Discovery only, fast and cheap, one record per show:

{
"searchTerms": ["true crime", "software engineering"],
"maxShows": 50
}

Specific shows with their recent episodes:

{
"podcastIds": [
"1200361736",
"https://podcasts.apple.com/us/podcast/serial/id917918570"
],
"includeEpisodes": true,
"maxEpisodesPerShow": 25
}

A feed you already know, skipping Apple entirely:

{
"feedUrls": ["https://feeds.simplecast.com/Sl5CSM3S"],
"includeEpisodes": true,
"maxEpisodesPerShow": 0,
"maxRecords": 500
}

Input

FieldTypeDefaultNotes
searchTermsarray[]Keywords for Apple's directory. The show cap is shared out evenly between the terms.
podcastIdsarray[]Apple collection IDs or Apple Podcasts show URLs. Looked up 50 per request.
feedUrlsarray[]Podcast RSS URLs read directly, with no Apple lookup.
countrystringUSTwo-letter storefront code. Results and ranking differ by market.
maxShowsinteger20Show records for the whole run, 1 to 500, across every term, ID and feed URL.
maxRecordsinteger1000Total rows for the whole run, shows and episodes together, 1 to 100000.
includeEpisodesbooleanfalseFetch each show's feed and emit one record per episode.
maxEpisodesPerShowinteger10Episodes per show, newest first. 0 = the whole feed, still bounded by maxRecords.

All three discovery inputs combine. IDs and feed URLs are read first and search terms share whatever is left of maxShows. With none of them set the Actor searches for technology, so an empty input still returns something useful — but an input whose every entry was unusable is refused outright rather than quietly turned into that fallback.

A value outside the stated range is an error, never a licence to fetch everything: maxShows: -5 fails the run before a single row is delivered. maxShowsPerTerm is the old name for maxShows and is still accepted, with the same run-total meaning.

Output example

An episode record:

{
"recordType": "episode",
"source": "id:917918570",
"collectionId": 917918570,
"showName": "Serial",
"artistName": "Serial Productions & The New York Times",
"feedUrl": "https://feeds.simplecast.com/PpzWFGhg",
"feedTitle": "Serial",
"genres": ["News", "Podcasts", "True Crime"],
"primaryGenre": "News",
"episodeCount": 125,
"artworkUrl": "https://is1-ssl.mzstatic.com/image/thumb/Podcasts221/v4/…/600x600bb.jpg",
"country": "USA",
"contentAdvisoryRating": "Clean",
"appleUrl": "https://podcasts.apple.com/us/podcast/serial/id917918570?uo=4",
"episodeGuid": "20ad2ce9-3088-449b-8001-2e3e739b54d8",
"title": "The Last 12 Weeks - Ep. 5",
"link": "https://serialpodcast.org",
"author": "Serial Productions & The New York Times",
"publishedAt": "2026-06-18T10:20:00.000Z",
"contentText": "Days before the execution, the defense team scrambles to respond to an unexpected ruling …",
"duration": "00:39:22",
"durationSeconds": 2362,
"audioUrl": "https://dts.podtrac.com/redirect.mp3/…/audio/128/default.mp3",
"audioLengthBytes": 37796581,
"audioType": "audio/mpeg",
"episodeNumber": 5,
"seasonNumber": 17,
"episodeType": "full",
"explicit": true,
"imageUrl": "https://image.simplecastcdn.com/images/…/3000x3000/tl12walbum_art3000x3000v2.jpg",
"scrapedAt": "2026-08-06T11:52:18.721Z"
}

Finding an Apple podcast ID

Open the show on Apple Podcasts and read the number out of the URL:

URL you seeID
podcasts.apple.com/us/podcast/the-daily/id12003617361200361736
podcasts.apple.com/gb/podcast/serial/id917918570?i=1000123917918570

Paste the whole URL if you prefer, the Actor extracts the ID itself. If you only know the show's name, put it in searchTerms instead and read the ID off the result.

API example

curl -X POST "https://api.apify.com/v2/acts/arman-bd~podcast-directory-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"searchTerms": ["history"],
"maxShows": 25,
"includeEpisodes": true,
"maxEpisodesPerShow": 5
}'

JavaScript example

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('arman-bd/podcast-directory-scraper').call({
podcastIds: ['1200361736'],
includeEpisodes: true,
maxEpisodesPerShow: 20,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const rec of items.filter((r) => r.recordType === 'episode')) {
console.log(`${rec.publishedAt}, ${rec.title} (${rec.durationSeconds}s) → ${rec.audioUrl}`);
}

Limits and behaviour

  • A single search returns at most 100 shows and cannot be paged. Use narrower terms, or supply IDs and feed URLs directly.
  • Caps bind the run, not the target. maxShows is shared across every search term rather than granted to each, and maxRecords stops the run pushing once the total is reached. Whatever was left unread is counted in RUN_SUMMARY.skipped.
  • Unusable input is named, never guessed at. A show name in podcastIds, a publisher (artist) page instead of a show, or a URL with no ID in it is listed in RUN_SUMMARY.rejectedInputs with the reason. If nothing usable is left, the run fails instead of substituting something else.
  • An ID that matches no show is listed in RUN_SUMMARY.idsNotFound; a term that matched nothing is listed in termsWithNoResults.
  • The same feed spelled several ways is one feed. http/https, www., letter case, a trailing slash and campaign parameters all collapse, so an alias list is charged once.
  • Not every show has a feed. Apple occasionally omits feedUrl. Those shows are still saved as show records; they simply cannot produce episodes.
  • Feeds vary wildly. Missing enclosures, absent itunes:duration, no season numbers, RFC-822 dates with the wrong weekday, all of it is handled defensively and yields null rather than a wrong value.
  • Episodes are newest first. That is the order feeds are published in, so maxEpisodesPerShow gives you the most recent N.
  • One failure never kills the run. A failed search, a dead feed or a private feed is logged into RUN_SUMMARY.failures and the run continues. The Actor only errors out when nothing at all was saved.
  • Public data only. No authentication, no personal data, no paywall circumvention. Audio URLs are the ones publishers put in their public feeds.

FAQ

Do I need an Apple developer account? No.

Can I get more than 100 shows for one topic? Not from one search. Split the topic into several narrower terms, or supply IDs and feed URLs directly.

Does it download the audio? No. It gives you audioUrl, audioLengthBytes and audioType so you can fetch or stream the file yourself.

Are transcripts included? Only if the publisher puts them in the show notes, which most do not. There is no structured transcript field in podcast RSS.

Why is contentText null on a show record? The channel description comes from the feed, and the feed is only fetched when includeEpisodes is on or when you passed the feed URL directly. Everything else on the show record comes from Apple.

Why did two inputs produce one show? Because they resolved to the same feed, or to the same Apple ID. Deduplication uses both, and ignores the cosmetic differences between feed URLs, so you are never charged twice for the same catalogue.

I asked for 20 shows across 4 terms and got 20, not 80. That is the contract: maxShows is what the run delivers in total, shared evenly between the terms. Raise it if you want more.

Can I integrate it with something else? Yes, Apify API, client libraries, webhooks, scheduled runs, dataset exports (JSON/CSV/Excel) or MCP. Output is structured JSON.