# Apple Podcasts Scraper – Episodes, Reviews & Podcast Publishers (`fetchsmith/apple-podcasts-scraper`) Actor

Scrape Apple Podcasts: every episode (title, date, duration, description, audio URL, RSS feed), listener reviews with star ratings, keyword search, the top-shows or trending-episodes chart per storefront, or every show a publisher runs. HTTP-only, no login, pay per result. iTunes podcast data API.

- **URL**: https://apify.com/fetchsmith/apple-podcasts-scraper.md
- **Developed by:** [Fetch Smith](https://apify.com/fetchsmith) (community)
- **Categories:** Social media, Lead generation, Marketing
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.00 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Apple Podcasts Scraper — Episodes, Reviews, Search, Charts & Podcast Publishers

Scrape Apple Podcasts without a browser or login: **every episode** of a show (title, release date, duration, description, direct audio URL, RSS feed), **listener reviews** with star ratings, **search Apple's podcast catalogue** by keyword, pull **today's top-charts** for any storefront, or list **every show a publisher runs**. Export to JSON, CSV or Excel. Pay only per row returned.

Episodes, reviews and search live in **one Actor**, so you can go from "podcasts about AI" to "every 1-star review those shows got" in a single run — and at **$0.001 per row with no per-run start fee**, it costs a fraction of comparable Apple Podcasts Actors (commonly $0.0025–$0.004 per row, several of them with a start fee on top).

### Use cases

- **Podcast guest / sponsor prospecting** — search a topic, get every matching show with its host, genre, episode count and RSS feed.
- **Audience research** — pull listener reviews for you and your competitors and mine them for complaints, praise and topic requests.
- **Content and SEO research** — episode titles + descriptions for a whole niche, ready for keyword analysis.
- **Media monitoring** — schedule the Actor to catch new episodes or new reviews for the shows you track.
- **AI/LLM pipelines** — episode descriptions and reviews as clean JSON, plus `episodeUrl` (the direct audio file) for transcription.
- **Trend tracking** — pull the top-N chart for any storefront on a schedule to see which shows are rising or falling.
- **Network mapping** — give a publisher's Apple Podcasts artist ID and get every show they run (e.g. a media company's whole podcast slate) in one call.

### Input

| Field | Type | Description |
|---|---|---|
| `dataType` | string | `episodes` (default), `reviews`, `podcasts` (show records), `charts` (today's top shows or trending episodes, see `chartType` — no `podcasts`/`searchTerms` needed), or `publisher` (every show by a publisher/artist) |
| `podcasts` | array | Apple Podcasts show URLs/IDs, e.g. `https://podcasts.apple.com/us/podcast/lex-fridman-podcast/id1434243584`. For `dataType: "publisher"`, give the publisher's artist URL/ID instead, e.g. `https://podcasts.apple.com/us/artist/the-new-york-times/121664449`. **You can also paste a direct RSS/podcast feed URL** for any show — including ones not indexed by Apple at all — but only with `dataType: "episodes"`, since the feed itself has no Apple ID for reviews/search/charts |
| `searchTerms` | array | Find shows by keyword instead of, or as well as, giving URLs |
| `searchLimit` | integer | Shows to take per search term (default 10, max 200) |
| `chartType` | string | Charts only: `shows` (default — Apple's Top Shows chart) or `episodes` (Apple's separate **Trending Episodes** chart: one row per episode, with audio URL, duration and release date) |
| `chartCount` | integer | Charts only: how many chart entries to fetch (default 50). Apple caps the overall charts (Top Shows, Trending Episodes) at **100** and a genre chart at 200; higher values are clamped with a warning |
| `chartGenre` | string | Charts only: restrict the Top Shows chart to one category (`comedy`, `trueCrime`, `news`, `business`, ... 19 total) instead of the overall top chart. Leave empty for the overall chart. Ignored for `chartType: "episodes"` — Apple publishes no per-genre episode chart |
| `maxPodcastsPerPublisher` | integer | Publisher only: how many shows to return per publisher (default 200, max 200) |
| `country` | string | Storefront code — `us` (default), `gb`, `de`, `jp`, ... Reviews, availability and charts differ per storefront |
| `maxEpisodesPerPodcast` | integer | Up to 200 most recent episodes per show (Apple's limit) — up to 20,000 with `useRssForFullArchive` (default 100). On Apple's API it counts episodes **fetched** (it is the API's own limit, so the other filters narrow within them); on RSS the whole feed arrives in one request, so it counts episodes **kept** after filtering and a date window can match anywhere in the archive |
| `useRssForFullArchive` | boolean | Episodes only: fetch the show's own RSS feed instead of Apple's lookup API to get the **complete episode archive**, not just the most recent ~200 (default `false`; verified live: a real feed returned 502 episodes vs. Apple's 200-episode ceiling for the same show). Also unlocks `episodeType`, `showNotesHtml`, `audioFileSize`, `transcriptUrl` and `chaptersUrl` — fields Apple's own API never exposes. Falls back to Apple's lookup API for any show without a usable feed |
| `maxReviewsPerPodcast` | integer | Up to 500 reviews per show per storefront (Apple's limit), though **in practice Apple's feed usually serves fewer** — see FAQ. Counts reviews **scanned**, before `minRating`/`maxRating`/`keyword` filtering |
| `sort` | string | Reviews only: `mostRecent` (default) or `mostHelpful` |
| `includePodcastInfo` | boolean | Attach show name, host, genre, RSS feed and episode count to every row (default `true`) |
| `maxResults` | integer | Overall cap across all shows — also caps what you pay |
| `minRating` / `maxRating` | integer | Reviews only: keep reviews rated within 1-5 |
| `keyword` | string | Reviews only: keep reviews whose title or text contains this word/phrase |
| `minReleaseDate` / `maxReleaseDate` | string | Episodes only: keep episodes released in this window (`YYYY-MM-DD` or full ISO). This narrows *within* the `maxEpisodesPerPodcast` most-recent episodes Apple returns, not further back into a show's archive — enable `useRssForFullArchive` to search the whole archive instead, where `maxEpisodesPerPodcast` caps matches kept rather than episodes walked, so a window years back still returns rows. If a whole-archive walk matches nothing, the run log names the feed's real first/last episode dates so you can widen the window to fit |
| `minDurationSeconds` | integer | Episodes only: drop episodes shorter than this (e.g. exclude trailers/ads). **Apple omits duration for ~half of episodes on some shows regardless of actual length** (measured on a real 20-episode sample) — episodes with unknown duration are always kept, never assumed short |
| `explicitFilter` | string | Episodes only: `all` (default), `clean` (exclude Explicit-flagged), or `explicitOnly`. **The flag is read from whichever source the run uses** — Apple's Store rating by default, or the feed's own `<itunes:explicit>` tag (per episode, else show-level) when `useRssForFullArchive` is on. Real feeds disagree with Apple: the Lex Fridman feed carries no per-episode tag at all and declares the show `false` at channel level, while Apple rates those same episodes Explicit, so `explicitOnly` returns rows via Apple and none via RSS (measured 2026-09-18). The run log warns when this applies |
| `webhookUrl` | string | Optional http(s) URL to POST a small JSON completion summary to (items pushed, dataType, dataset ID, and — if `watchLabel` is set — `watchSeeding`/`watchSkippedCount`/`baselineTruncated`/`baselineTruncatedTotal`) — a convenience ping without setting up an Apify platform webhook. Best-effort: a failed or slow webhook is logged as a warning and never affects the run or your bill |
| `watchLabel` | string | Episodes only: name a saved watch (e.g. `"daily-check"`) and this run returns **only episodes not delivered under that label before**, instead of every episode every time. The first run for a label is a free baseline (0 rows, 0 charged) that records what already exists; run it again later — on a schedule — to get only what's new. Changing any other filter starts a fresh baseline instead of re-delivering previously-excluded episodes as "new". An episode **older than the deepest point the baseline reached** is also never billed as new: if a later run gets further back into a show's archive than the baseline did (Apple's lookup caps at 200 episodes, and an RSS full-archive fetch can fail), those older episodes are recognised as pre-existing — not delivered, not charged — and the run says how many. Episodes published after the baseline are always newer than that date, so real alerts are never suppressed |

Filtering happens **before** you're charged — you never pay for rows a filter removed.

#### Example: get notified only about new episodes (run on a schedule)

```json
{
  "podcasts": ["https://podcasts.apple.com/us/podcast/lex-fridman-podcast/id1434243584"],
  "dataType": "episodes",
  "watchLabel": "lex-daily",
  "webhookUrl": "https://your-server.example.com/new-episode"
}
```

The first run seeds the baseline (0 rows, 0 charged). Every run after that returns only episodes published since the previous run — pair with `webhookUrl` to get pinged the moment a new episode lands, without polling the full archive yourself.

#### Example: every 1- and 2-star review for the top 5 "meditation" podcasts

```json
{
  "searchTerms": ["meditation"],
  "searchLimit": 5,
  "dataType": "reviews",
  "maxRating": 2,
  "maxReviewsPerPodcast": 200
}
```

### Output

**`dataType: "episodes"`** — one item per episode:

```json
{
  "type": "episode",
  "collectionId": 1434243584,
  "podcastName": "Lex Fridman Podcast",
  "episodeId": 1000786117598,
  "title": "#501 – DHH: Future of Programming, AI, Agentic Engineering...",
  "episodeNumber": null,
  "seasonNumber": null,
  "releaseDate": "2026-08-26T21:47:04Z",
  "durationMs": 19317000,
  "durationMinutes": 322,
  "description": "DHH is the creator of Ruby on Rails...",
  "episodeUrl": "https://media.blubrry.com/.../lex_ai_dhh_2.mp3",
  "episodeFileExtension": "mp3",
  "episodeGuid": "https://lexfridman.com/?p=6506",
  "explicit": true,
  "artworkUrl": "https://is1-ssl.mzstatic.com/image/thumb/...",
  "episodePageUrl": "https://podcasts.apple.com/us/podcast/...",
  "feedUrl": "https://lexfridman.com/feed/podcast/",
  "episodeType": null,
  "showNotesHtml": null,
  "audioFileSize": null,
  "keywords": null,
  "transcriptUrl": null,
  "chaptersUrl": null,
  "source": "itunes",
  "artistName": "Lex Fridman",
  "primaryGenre": "Technology",
  "episodeCount": 502
}
```

`episodeType`, `showNotesHtml`, `audioFileSize`, `transcriptUrl` and `chaptersUrl` are only populated when `useRssForFullArchive` is on and the show's feed provides them (`source` reads `"rss"` instead of `"itunes"` for those rows) — Apple's own lookup API has no equivalent fields. `chaptersUrl` reads the Podcast 2.0 `<podcast:chapters>` tag, a pointer to the episode's JSON chapter-marker file (timestamps + titles for each segment), when the feed publishes one.

**`dataType: "reviews"`** — one item per review:

```json
{
  "type": "review",
  "collectionId": 1434243584,
  "reviewId": "14514424096",
  "title": "Amazing breadth",
  "content": "I've listened to Lex for many years...",
  "rating": 5,
  "author": "FatBoyTig",
  "updatedAt": "2026-09-05T10:26:18-07:00",
  "voteSum": 0,
  "voteCount": 0,
  "country": "us",
  "podcastName": "Lex Fridman Podcast",
  "artistName": "Lex Fridman",
  "primaryGenre": "Technology"
}
```

**`dataType: "podcasts"`** — one item per show: `collectionId`, `podcastName`, `artistName`, `podcastUrl`, `feedUrl`, `primaryGenre`, `genres`, `episodeCount`, `latestReleaseDate`, `explicit`, `contentAdvisoryRating`, `artworkUrl`, and `searchTerm` when it came from a search. If a show matches more than one of your search terms (common with overlapping terms, e.g. "joe rogan" and "jre" both matching The Joe Rogan Experience), it's returned — and charged — once, under whichever term matched first.

**`dataType: "charts"`** — with the default `chartType: "shows"`, the same shape as `podcasts`, plus `chartRank` (1 = #1 in the storefront). Set `includePodcastInfo:false` to skip the per-show detail lookup and get just the raw chart fields (name, artist, genre, artwork, URL) faster.

**`dataType: "charts"` + `chartType: "episodes"`** — the same shape as `episodes` (title, `releaseDate`, `durationMinutes`, `description`, direct `episodeUrl` audio file, `episodeGuid`, `feedUrl`, show metadata), plus `chartRank`. The episode filters apply here too (`explicitFilter`, `minDurationSeconds`, `minReleaseDate`/`maxReleaseDate`), and filtered-out entries are never charged, so ranks can legitimately have gaps.

**`dataType: "publisher"`** — same shape as `podcasts`, plus `publisherId` (the artist ID you gave). One item per show the publisher runs.

### FAQ

**What country codes does `country` take?** Apple storefronts are two-letter ISO-3166-1 **alpha-2** codes — the UK is `gb`, not `uk`, and there is no `usa`/`uk`/`eng`. A wrong code used to look like a show that doesn't exist (Apple answers `uk` with `HTTP 400` carrying valid JSON and no results, and the review feed with an empty body); now a recognisable wrong code fails the run in about a second, before the first request and before any charge, and names the code you should have used.
**How do I find a publisher's artist ID?** It's the trailing number on their "See All" / artist page on podcasts.apple.com (e.g. `.../artist/the-new-york-times/121664449`), or run `dataType: "podcasts"` for any one of their shows first — the search/lookup result carries `artistId` even though this Actor doesn't surface it by default on `podcasts` rows (open a request if you need it added).
**Does `keyword` handle accented words correctly?** Yes, as of v0.1.16 — `keyword` and the text it's matched against are Unicode-normalized before comparing, so an accented word (e.g. "café") matches regardless of which of Unicode's two equivalent representations (composed vs. decomposed) you typed it in.

**Do I need an Apple account or API key?** No. Everything comes from Apple's public podcast endpoints. No login, no browser, no proxy required.

**Why did I get zero results?** The run's status message says exactly why. The usual causes: the show isn't available in the `country` storefront you asked for (try `us`), Apple has no reviews for that show in that storefront (reviews are per-storefront — a show can have hundreds in `us` and none in `de`), or the ID wasn't an Apple Podcasts ID.

**Why did I get fewer reviews than `maxReviewsPerPodcast`, or exactly zero with `minRating`/`maxRating`/`keyword` set?** `maxReviewsPerPodcast` is a scan-depth cap, not a results cap — it's how many of the show's most-recent reviews get read from Apple's feed before your rating/keyword filter is applied, not how many matching reviews exist. A rare keyword can sit past the reviews you scanned. Example: The Joe Rogan Experience + `keyword:"propaganda"` returns 0 kept reviews at `maxReviewsPerPodcast:50` (only page 1 scanned) but 2 at `maxReviewsPerPodcast:100` (page 2 scanned) — the run log and status message call this out by name when it happens, telling you to raise `maxReviewsPerPodcast`. That advice only applies when **your** cap ended the scan; when Apple's feed ended it (next FAQ), raising the cap changes nothing and the run says so instead.

**Why did I get 50 reviews for a show with thousands?** Because Apple stopped serving, not because the show ran out. Apple's public review RSS is served from a patchy index: measured 2026-09-22, the walk commonly ends on a full page long before the documented 500-review/10-page ceiling — Crime Junkie in the `gb` storefront returned exactly 50 (one full page, nothing on page 2) at `maxReviewsPerPodcast: 500`. **No input value reaches the reviews behind that wall**, so raising `maxReviewsPerPodcast` is not the fix; ask for other `country` storefronts (reviews are per-storefront), or run on a schedule and deduplicate by `reviewId` to build a deeper archive over time.

**How do I know whether Apple cut the feed off or the show simply has no more reviews?** The run tells you, as of v0.1.37. Whenever the last page Apple served came back **full** while the Actor was still willing to take more, the run's status message and log name those shows and say which page the feed quit at. A run that ends on a **partial** page genuinely exhausted what Apple serves for that show and storefront — so a *missing* note is itself the "you got everything available" signal. Either way you are only charged for rows actually delivered.

**How many episodes can I get?** Apple's endpoint exposes up to 200 of the most recent episodes per show. For the complete back catalogue, set `useRssForFullArchive: true` — the Actor fetches the show's own RSS feed directly instead (verified live on a real show: 502 episodes vs. Apple's 200-episode cap for the same ID).

**Can I scrape a show that isn't on Apple Podcasts, or that I can't find by search?** Yes — paste its RSS feed URL directly into `podcasts` instead of an Apple URL/ID, with `dataType: "episodes"`. The Actor reads the feed itself, so no Apple lookup happens at all (verified live: a non-Apple-searched NPR feed returns full episode rows this way). Reviews, search and charts still need a real Apple ID, since that data only exists on Apple's side.

**How fast is it?** HTTP-only, no headless browser: a show's 200 episodes come from a single request, and reviews page 50 at a time. Runs cost a few seconds of compute plus the per-result fee.

**Can I chart individual episodes, not just shows?** Yes — `dataType: "charts"` with `chartType: "episodes"` returns Apple's **Trending Episodes** chart, which is a different chart from Top Shows, not a view of it (on the 2026-09-17 US chart, the #1 *show* was The Daily while the #1 *episode* was one specific Daily episode, and ranks 2-12 were episodes of eleven different shows). Each entry is enriched into a full episode row — audio URL, duration, release date, description — via one cached lookup per show. Two limits worth knowing: Apple's episode chart has no genre breakdown (`chartGenre` is ignored, with a warning), and Apple-exclusive/subscriber-only shows publish no episode list at all, so those entries arrive with `source: "chart"` and the chart's own title/artwork instead of full episode detail rather than being dropped (measured 19 of 20 entries fully enriched on a real US chart).

**Can I get genre-specific charts?** Yes — set `chartGenre` (e.g. `comedy`, `trueCrime`, `business`) to get that category's own top chart instead of the overall one, using Apple's own per-genre chart feed. 19 genres are supported; an unrecognized value is ignored with a warning rather than silently returning the wrong chart.

**What happens if Apple's endpoint has a transient blip mid-run?** Every lookup, search, review-feed and RSS request is retried up to 3 times on a connection-level failure (measured at roughly 1 fresh connection in 4 for HTTP/2 faults across this fleet, 2026-09-21) before it is given up on and named in the log. A single blip no longer costs you a whole show's episodes, a page of reviews, or — with `useRssForFullArchive: true` — a silent downgrade back to Apple's 200-episode cap. A real HTTP error from Apple (e.g. a wrong storefront code) still fails immediately with the explanation instead of being retried.

**Is this legal?** It only reads public, unauthenticated Apple endpoints — the same data any visitor sees on podcasts.apple.com. No personal data beyond the public reviewer nicknames Apple itself publishes.

**Baseline size cap.** The recorded `watchLabel` baseline holds at most 60,000 episode ids per label; once a label's cumulative baseline grows past that, the oldest ids are dropped to bound the record's size. A dropped id is treated as "new" again on a later run and re-charged, even though you already paid for it. This only bites a label tracking a very high cumulative volume of episodes over many runs — narrowing the input (fewer podcasts/searchTerms, or a `minReleaseDate`) keeps a baseline well under the cap. A run that actually drops ids says so explicitly in its log and status message, and reports the exact counts as `baselineTruncated`/`baselineTruncatedTotal` in the `webhookUrl` payload.

### Pricing

Pay per result: **$0.001 per row** (episode, review or podcast) returned to your dataset. **No Actor-start fee** — an empty or filtered-out run costs you nothing. Set `maxResults` to cap any run.

The closest Store competitor covering the same ground (search, show details, reviews, charts, episode archives, publisher lookup) is `sourabhbgp` (41 users, `sourabhbgp/apple-podcast-scraper`) at $0.003/result — 3x our price. Checked against its live input schema rather than its listing copy: it **does** ship a webhook (`webhookUrl` POSTs every record as it is collected — a different design from our end-of-run summary ping, not a worse one) and it **does** read episodes straight from a show's RSS feed, so neither of those is a gap. What its schema has no input for is a duration filter, an explicit-content filter, or a new-episode-only watch mode — its `trackDeltas` persists snapshots for chart *rank* changes only, not episodes. Verified live 2026-10-01.

The fastest-growing rival in the niche is `logiover` (53 users, 15 of them in the last 30 days, `logiover/apple-podcasts-episode-scraper`), and it is episodes-only — no reviews, no charts, no publisher lookup. On episodes it matches this Actor almost field for field (`useRssForFullArchive`, `minDurationSeconds`, an explicit filter, a release-date window), including Podcast 2.0 `chapters` — this Actor now parses `<podcast:chapters>` too (`chaptersUrl`, RSS path only). It charges an Actor-start fee plus $0.0025/result on the free plan — 2.5x our flat $0.001 with no start fee — and has no watch mode and no completion webhook. Verified live 2026-10-01.

***

Built by [FetchSmith](https://fetchsmith.com) — fast, HTTP-only scrapers with honest pricing. Questions or a field you need? Email support@fetchsmith.com.

### Related guides

Engineering write-ups behind this Actor:

- [Apple Podcasts has a public JSON API — four endpoints, no key, and one that doesn't exist](https://fetchsmith.com/blog/apple-podcasts-public-json-api)
- [Four ways an invisible character makes a scraper return zero rows](https://fetchsmith.com/blog/invisible-characters-return-zero-rows)
- [We nearly charged our own buyers twice for rows they'd already paid for](https://fetchsmith.com/blog/watch-baseline-eviction-rebilling) — a capped watch-mode baseline can silently evict old-but-current ids on a high-volume run, re-delivering (and re-billing) rows already paid for.
- [Eight ways an "only new since last run" watch mode silently stops working](https://fetchsmith.com/blog/incremental-api-watch-mode-four-traps) — the fleet-wide survey of watch-mode failure shapes across all nineteen incremental Actors, this one included.
- [Substack, Apple Podcasts, Google News and Hacker News — four free APIs where the first response isn't the finished product](https://fetchsmith.com/blog/public-content-apis-hidden-second-step) — how this Actor's second-step trap compares to the other three content platforms we scrape.

More tools: [fetchsmith.com/tools](https://fetchsmith.com/tools)

Source code: https://github.com/Fetchsmith/fetchsmith/tree/main/actors/apple-podcasts-scraper

# Actor input Schema

## `dataType` (type: `string`):

episodes = every episode of the given shows; reviews = listener reviews and star ratings; podcasts = the show records themselves (use with Search terms to discover shows); charts = today's top podcasts in a storefront (no podcasts/search terms needed); publisher = every show published by a given publisher/artist.

## `podcasts` (type: `array`):

Apple Podcasts show URLs/IDs, e.g. https://podcasts.apple.com/us/podcast/lex-fridman-podcast/id1434243584 or 1434243584. Leading/trailing spaces are ignored. For dataType 'publisher', give the publisher's Apple Podcasts artist URL/ID instead, e.g. https://podcasts.apple.com/us/artist/the-new-york-times/121664449. You can also paste a direct RSS/podcast feed URL for any show, including ones not on Apple Podcasts — works with dataType 'episodes' only, since the feed has no Apple ID for reviews/search/charts.

## `searchTerms` (type: `array`):

Find shows by keyword instead of (or as well as) giving URLs. Each match is then scraped according to 'What to scrape'.

## `chartType` (type: `string`):

Charts only: which Apple chart to pull. "Top Shows" ranks podcasts; "Trending Episodes" is Apple's separate chart of individual episodes (one row per episode, with audio URL, duration and release date). Trending Episodes has no genre breakdown, so "Chart genre" is ignored for it.

## `chartCount` (type: `integer`):

Charts only: how many chart entries to fetch for the storefront. Apple caps the overall charts (Top Shows and Trending Episodes) at 100; a genre chart goes up to 200. Anything higher is clamped, with a warning.

## `chartGenre` (type: `string`):

Charts only: restrict the Top Shows chart to one Apple Podcasts category instead of the overall top chart. Leave empty for the overall chart. Ignored when "Chart" is Trending Episodes (Apple publishes no per-genre episode chart).

## `searchLimit` (type: `integer`):

How many shows to take from each search term (max 200).

## `country` (type: `string`):

Two-letter ISO-3166-1 alpha-2 storefront code (us, gb, de, ...) — the UK is "gb", not "uk". Availability, charts and reviews differ per storefront.

## `maxEpisodesPerPodcast` (type: `integer`):

Apple's episode endpoint exposes up to 200 of the most recent episodes per show. With 'Use RSS for full archive' enabled, this can go up to 20,000 since RSS has no such cap. What it counts differs by source, because only one of them can be re-read: on Apple's API it is the number of recent episodes FETCHED (the API's own limit), so a date/duration/explicit filter narrows within them and cannot reach older ones; on RSS the whole feed arrives in one request, so it caps the episodes KEPT after filtering and a date window is free to match anywhere in the archive.

## `maxPodcastsPerPublisher` (type: `integer`):

dataType 'publisher' only: how many shows to return per publisher (Apple's lookup endpoint returns up to 200).

## `maxReviewsPerPodcast` (type: `integer`):

Apple exposes up to 500 reviews (10 pages of 50) per show per storefront. This cap counts reviews scanned before minRating/maxRating/keyword filtering, not results kept — see README FAQ.

## `sort` (type: `string`):

Only applies to reviews.

## `includePodcastInfo` (type: `boolean`):

Attach show name, host, genre, RSS feed URL and episode count to each episode/review row.

## `maxResults` (type: `integer`):

Overall cap across all shows — also caps what you pay for.

## `minRating` (type: `integer`):

Reviews only: keep reviews rated >= this (1-5).

## `maxRating` (type: `integer`):

Reviews only: keep reviews rated <= this (1-5).

## `keyword` (type: `string`):

Reviews only: keep reviews whose title or text contains this word/phrase (case-insensitive). Leading/trailing spaces are ignored. Accented letters are Unicode-normalized before matching, so "café" matches regardless of which Unicode form (composed or decomposed) you typed it in.

## `minReleaseDate` (type: `string`):

Episodes only: keep episodes released on or after this date (YYYY-MM-DD or full ISO). Note: Apple's episode lookup only returns the most recent 'Max episodes per podcast' episodes, so this narrows within that recent window and cannot reach further back into a show's archive than Apple already returned — enable 'Use RSS for full archive' to search further back.

## `maxReleaseDate` (type: `string`):

Episodes only: keep episodes released on or before this date (YYYY-MM-DD or full ISO). Same recent-window caveat as 'Episodes released on/after'.

## `useRssForFullArchive` (type: `boolean`):

Episodes only: fetch each show's own RSS feed instead of Apple's lookup API, which returns only the most recent ~200 episodes. RSS feeds have no such cap (a real feed tested returned 2,977 episodes) and also carry fields Apple's API never exposes: episode type (full/trailer/bonus), full HTML show notes, audio file size and a transcript URL when the publisher provides one (Podcasting 2.0). Costs one extra request per show; Apple's lookup API is used as a fallback for any show without a usable feed. Default off to keep existing run behavior unchanged.

## `minDurationSeconds` (type: `integer`):

Episodes only: drop episodes shorter than this — useful for excluding trailers, ads or short teasers. Apple omits the duration for a large share of episodes on some shows regardless of their real length; those episodes are always kept, never assumed short (the run log reports how many). Leave empty to keep all lengths.

## `explicitFilter` (type: `string`):

Episodes only: 'all' keeps everything (default), 'clean' excludes episodes flagged Explicit, 'explicitOnly' keeps only episodes flagged Explicit. The flag comes from whichever source the run uses: Apple's Store rating by default, or the show's own <itunes:explicit> tag when 'Use RSS for full archive' is on (falling back to the show-level tag for episodes that carry none). The two can disagree for the same episode, so a filter that returns nothing under one source may return rows under the other.

## `webhookUrl` (type: `string`):

Optional. An http(s) URL to POST a small JSON summary to when the run finishes — items pushed, dataType, and the run's dataset ID so you can fetch the results. A convenience for callers who want a completion ping without setting up an Apify platform webhook (which needs separate Console/API configuration per Task, not per run). Best-effort: a failed or slow webhook is logged as a warning and never fails the run or affects charging — it fires after every item has already been pushed and charged. Leave empty to skip.

## `watchLabel` (type: `string`):

Optional, dataType "episodes" only. Name a saved watch (e.g. "my-daily-check") and this run returns ONLY episodes not delivered under that same label and filter set before, instead of every episode every time. The first run for a label is a free baseline: it records which episodes already exist and returns zero rows. Run it again later — on a schedule, typically — to get only what's new. The baseline is kept in your own Apify account (a named key-value store), keyed by label plus a fingerprint of your other filters, so changing a filter starts a fresh baseline instead of dumping previously-excluded episodes as "new". Ignored for dataType other than "episodes".

## Actor input object example

```json
{
  "dataType": "episodes",
  "podcasts": [
    "https://podcasts.apple.com/us/podcast/lex-fridman-podcast/id1434243584"
  ],
  "searchTerms": [],
  "chartType": "shows",
  "chartCount": 50,
  "chartGenre": "",
  "searchLimit": 10,
  "country": "us",
  "maxEpisodesPerPodcast": 100,
  "maxPodcastsPerPublisher": 200,
  "maxReviewsPerPodcast": 200,
  "sort": "mostRecent",
  "includePodcastInfo": true,
  "maxResults": 2000,
  "useRssForFullArchive": false,
  "explicitFilter": "all"
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "podcasts": [
        "https://podcasts.apple.com/us/podcast/lex-fridman-podcast/id1434243584"
    ],
    "searchTerms": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("fetchsmith/apple-podcasts-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "podcasts": ["https://podcasts.apple.com/us/podcast/lex-fridman-podcast/id1434243584"],
    "searchTerms": [],
}

# Run the Actor and wait for it to finish
run = client.actor("fetchsmith/apple-podcasts-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "podcasts": [
    "https://podcasts.apple.com/us/podcast/lex-fridman-podcast/id1434243584"
  ],
  "searchTerms": []
}' |
apify call fetchsmith/apple-podcasts-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,fetchsmith/apple-podcasts-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/sit99YHSd0o1JhJZd/builds/xfzOQQ8uESaNcMWg4/openapi.json
