# Apple Podcasts Scraper: Search, Charts, Episodes and Ratings (`deriverge/apple-podcasts-scraper`) Actor

Scrape Apple Podcasts: search results, top charts for any country and genre, podcast details with ratings, and episodes with audio URL, duration and show notes. Filter by date and keyword, new entries only. JSON, CSV, Excel and API.

- **URL**: https://apify.com/deriverge/apple-podcasts-scraper.md
- **Developed by:** [deriverge s.r.o.](https://apify.com/deriverge) (community)
- **Categories:** News, Social media, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 1,000 podcast returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

### What does Apple Podcasts Scraper do?

**Apple Podcasts Scraper** reads the Apple Podcasts directory and each show's own RSS feed, and returns both as clean rows:

- **Search** the directory for any term, up to 200 podcasts per term, in any storefront.
- **Charts**: the top shows of any country in any of 110 genres, up to 200 entries per chart, plus the top subscriber chart.
- **Details** for any podcast you name by Apple Podcasts link, numeric Apple ID or feed URL, with the average rating and the number of ratings when you ask for them.
- **Episodes** from each show's feed: title, date, duration in seconds, episode and season numbers, episode type, audio URL and type, and the show notes as plain text. The whole archive, newest first.

No browser, no proxies, no API key. Apple publishes the directory through a public interface and every podcast publishes its feed; this actor reads both the way a podcast app does.

You pay only for podcasts returned, from $0.50 per 1,000, with no start fee. The $5 of monthly credit in Apify's Free plan covers about 5,000 podcasts, so you can try it for free.

### Two kinds of rows

Every row has a `type`. Podcast rows and episode rows can be told apart in one column and joined on `podcastKey`.

| Podcast row | What you get |
|---|---|
| `name`, `author`, `appleId`, `appleUrl`, `feedUrl`, `website`, `artwork` | Identity and links from the directory and the feed. |
| `genres`, `primaryGenre`, `language`, `podcastType`, `explicit` | Classification, `episodic` or `serial`, the explicit flag. |
| `episodeCount`, `latestEpisodeAt` | How much there is and how fresh it is. |
| `ratingAverage`, `ratingCount` | Average rating and number of ratings from the show page, with `includeRatings`. |
| `chartRank`, `chartCountry`, `chartKind`, `chartGenre` | Filled for chart entries, `null` for search results. |
| `source`, `matchedQuery` | Where the row came from: `search`, `chart` or `input`, and the term it matched. |

| Episode row | What you get |
|---|---|
| `title`, `publishedAt`, `durationSeconds` | Date as a real timestamp, duration as a number whatever format the feed used. |
| `episodeNumber`, `seasonNumber`, `episodeType` | Numbers and `full`, `trailer` or `bonus`, when the feed publishes them. |
| `audioUrl`, `audioType`, `audioBytes` | The enclosure. |
| `summary`, `content` | Show notes as plain text: the first 300 characters, or the whole text with `includeContent`. |
| `podcastName`, `podcastAppleId`, `podcastKey`, `feedUrl` | The show the episode belongs to. |
| `episodeSource` | `rss` from the show's feed, `apple` from the directory when a show publishes no public feed. |

Nothing is guessed. A field the source did not publish is `null`.

### Charts that report movement

Turn on `newOnly`, give the run a watch name or save it as a task, and schedule it daily. Each run compares the chart against the previous snapshot: new entrants are returned and charged, shows already in the chart are not. The `CHANGES` record lists what entered, what dropped out and every rank move, at no charge.

### Filters that stop the noise before it is charged

- **`episodesPublishedAfter`** cuts the archive off at a date.
- **`keywords`** keeps only episodes whose title, show notes or categories mention one of your words.
- **`maxEpisodesPerPodcast`** takes the newest N. Feeds carry whole archives, some with thousands of episodes, so this is what keeps a broad run affordable.
- **`maxPodcasts`** caps how many shows a search or a set of charts may bring in. Podcasts you named are collected first and never dropped by this cap.

Episodes removed by a filter are never charged.

### How much does it cost to scrape Apple Podcasts?

You pay per result. There is no start fee and no charge for compute time or proxies.

| | Free plan | Starter | Scale | Business |
|---|---|---|---|---|
| 1,000 podcasts | $1.00 | $0.80 | $0.65 | $0.50 |
| 1,000 episodes (optional) | $0.50 | $0.40 | $0.33 | $0.25 |

For example, a batch of 1,000 podcasts costs $1.00 on the Free plan and $0.50 on the Business plan. The $5 of monthly credit in Apify's Free plan covers about 5,000 podcasts.

### How to scrape Apple Podcasts

1. Click **Try for free** (or **Start** if you are signed in) to open the actor in Apify Console.
2. Fill in **Search terms**, **Charts** or **Podcasts** (Apple Podcasts links); each works on its own or together.
3. Click **Start**. Rows appear in the **Output** tab within seconds.
4. Download the results as JSON, CSV, Excel or HTML, or read them through the API.
5. To repeat it, click **Save as a task** and add a schedule. A scheduled task keeps its own snapshot, so change and new-only modes work without any setup.

### Input

```json
{
  "queries": ["history"],
  "chartCountries": ["us", "gb"],
  "chartGenres": ["True Crime", "Business"],
  "chartLimit": 100,
  "podcasts": ["https://podcasts.apple.com/us/podcast/the-daily/id1200361736", "https://feeds.npr.org/510289/podcast.xml"],
  "country": "us",
  "includeRatings": true,
  "includeEpisodes": true,
  "maxEpisodesPerPodcast": 20,
  "episodesPublishedAfter": "2026-01-01",
  "newOnly": false
}
```

### Output

A podcast row:

```json
{
  "type": "podcast",
  "key": "podcast:1537788786",
  "source": "search",
  "appleId": "1537788786",
  "name": "The Rest Is History",
  "author": "Goalhanger",
  "appleUrl": "https://podcasts.apple.com/us/podcast/the-rest-is-history/id1537788786",
  "feedUrl": "https://feeds.megaphone.fm/GLT4787413333",
  "website": "http://therestishistory.com",
  "artwork": "https://is1-ssl.mzstatic.com/image/thumb/Podcasts211/v4/bf/89/a5/bf89a586-3f77-bf37-7ba3-b75f1bca7bfa/mza_1664785978944494824.jpg/600x600bb.jpg",
  "genres": ["History"],
  "primaryGenre": "History",
  "language": "en",
  "episodeCount": 993,
  "latestEpisodeAt": "2026-10-07T23:00:00.000Z",
  "explicit": false,
  "podcastType": "episodic",
  "description": "Take a deep dive into History's biggest moments with Tom Holland & Dominic Sandbrook. ...",
  "ratingAverage": 4.7,
  "ratingCount": 14080,
  "chartRank": null,
  "chartCountry": null,
  "chartKind": null,
  "chartGenre": null,
  "matchedQuery": "history",
  "country": "us"
}
```

An episode row:

```json
{
  "type": "episode",
  "key": "episode:podcast:1537788786:0322655e-b118-11f1-b8eb-f79d9c6ca5d1",
  "podcastKey": "podcast:1537788786",
  "podcastAppleId": "1537788786",
  "podcastName": "The Rest Is History",
  "feedTitle": "The Rest Is History",
  "feedUrl": "https://feeds.megaphone.fm/GLT4787413333",
  "title": "707. The Terror: How The French Revolution Collapsed Into Chaos (Part 1)",
  "link": null,
  "appleUrl": null,
  "publishedAt": "2026-09-20T23:05:00.000Z",
  "durationSeconds": 5133,
  "episodeNumber": 707,
  "seasonNumber": null,
  "episodeType": "full",
  "explicit": false,
  "author": "Goalhanger",
  "summary": "How did the infamous murder of Jean-Paul Marat, painted so vividly by Jacques-Louis David, ...",
  "content": null,
  "categories": [],
  "image": "https://megaphone.imgix.net/podcasts/...",
  "audioUrl": "https://pdst.fm/e/traffic.megaphone.fm/GLT6580043397.mp3",
  "audioType": "audio/mpeg",
  "audioBytes": null,
  "guid": "0322655e-b118-11f1-b8eb-f79d9c6ca5d1",
  "episodeSource": "rss"
}
```

### What it does not collect

Nothing about listeners. The author credit, the artwork and the links are what the show itself publishes. Individual reviews are not collected, only the aggregate rating and count shown on the show page. Contact details that some feeds carry in an owner tag are not extracted.

### Integrations and API

Connect the actor to **Make**, **Zapier**, **n8n**, **Google Sheets**, **Slack** or any webhook, or call it from your own code. With the Python client:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("deriverge/apple-podcasts-scraper").call(run_input={"queries": ["history"]})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(row["type"], row.get("name"))
```

The **API** tab on this page has the same call for Node.js and cURL, and AI agents can run the actor through the Apify MCP server.

### Is it legal to scrape Apple Podcasts?

The actor reads the public Apple Podcasts directory and the RSS feeds that podcasters publish for distribution. It returns metadata and links, not audio files; the shows themselves remain their creators' work.

### FAQ

#### How many episodes can I get?

The whole archive of each show, because episodes come from the show's own feed. When a show publishes no public feed (some platform exclusives) or the feed cannot be read, the actor falls back to the Apple directory, which carries the latest 200 episodes, and marks those rows `episodeSource: "apple"`.

#### Which countries and genres?

Any Apple storefront code (us, gb, de, fr, jp, au, br and so on) and any of the 110 Apple Podcasts genres and subgenres, by name or by Apple genre ID. An unknown genre name is reported in the log and the run summary instead of silently returning the overall chart.

#### Does Apple limit requests?

Apple's search interface allows about twenty requests per minute from one address. A run needs one request per search term, one per chart, one per hundred podcasts for details and one per show for ratings, so most runs never come near it. If Apple answers 403 or 429, the actor repeats the request through a fresh proxy session.

#### Can I read only charts, or only episodes?

Yes. The same engine is published as **Podcast Charts Scraper** and **Podcast Episodes Scraper** with the input reduced to that job, and as **RSS Feed Scraper** for any RSS or Atom feed.

### Support and feedback

Missing a field or found something that does not work? Open an issue in the **Issues** tab and it will be answered, usually within a day. If the actor saves you time, a short review helps other people find it.

# Actor input Schema

## `queries` (type: `array`):

Search the Apple Podcasts directory for these terms, up to 200 podcasts per term. Leave empty to skip search.

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

Apple Podcasts links, numeric Apple IDs or RSS feed URLs, in any mix, for example https://podcasts.apple.com/us/podcast/the-daily/id1200361736 or https://feeds.npr.org/510289/podcast.xml.

## `chartCountries` (type: `array`):

Two-letter storefront codes whose top charts to read, for example us, gb, de, au, jp. Leave empty to skip charts.

## `chartGenres` (type: `array`):

Genre names or Apple genre IDs, for example Business, True Crime, Technology, Daily News or 1489. Leave empty for the overall chart. Subgenres work too, for example Entrepreneurship.

## `chartKind` (type: `string`):

The top shows chart accepts a genre and goes to 200 entries. The subscriber chart ranks paid subscriptions and has no genres.

## `chartLimit` (type: `integer`):

Entries per chart, newest rank first. Top shows go to 200, the subscriber chart to 100.

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

Two-letter storefront for search, lookups and ratings, for example us, gb, de. Charts use their own country list.

## `includeRatings` (type: `boolean`):

Read the average rating and the number of ratings from each podcast's page. One extra request per podcast, no extra charge.

## `includeEpisodes` (type: `boolean`):

Read each podcast's feed and return its episodes with audio URL, duration, numbers and show notes. Charged per episode.

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

Newest first. Podcast feeds carry the whole archive, so this is what keeps a broad run affordable.

## `episodesPublishedAfter` (type: `string`):

Drop episodes older than this date (YYYY-MM-DD). Episodes without a date are kept. Dropped episodes are never charged.

## `keywords` (type: `array`):

Keep only episodes whose title, show notes or categories contain at least one of these words, case-insensitive. Dropped episodes are never charged.

## `includeContent` (type: `boolean`):

Return the complete show notes as plain text in the content field. Otherwise summary holds the first 300 characters and content is null.

## `maxPodcasts` (type: `integer`):

Cap on distinct podcasts collected from search, charts and your list together.

## `newOnly` (type: `boolean`):

Keeps a snapshot per watch name (or per saved task) and returns only rows that were not there before. Schedule it daily and you have an alert that bills only for what is new; the CHANGES record also lists what disappeared.

## `watchName` (type: `string`):

Name of the snapshot used by the new-only mode, for example "true-crime-us". Runs from a saved task get a snapshot automatically even without a name.

## `maxItems` (type: `integer`):

Hard cap on returned rows in the run, podcasts and episodes together.

## Actor input object example

```json
{
  "queries": [
    "history"
  ],
  "chartKind": "top",
  "chartLimit": 100,
  "country": "us",
  "includeRatings": false,
  "includeEpisodes": false,
  "maxEpisodesPerPodcast": 50,
  "includeContent": false,
  "maxPodcasts": 500,
  "newOnly": false,
  "maxItems": 5000
}
```

# Actor output Schema

## `rows` (type: `string`):

One row per podcast (search result, chart entry or podcast you named) and, when asked, one row per episode with audio URL, duration, numbers and show notes.

## `changes` (type: `string`):

Podcasts and episodes that appeared or disappeared, and chart rank moves, compared with the previous snapshot of the same watch name or task.

## `summary` (type: `string`):

Counts per source, how many rows each filter dropped, feeds that could not be read and notes.

# 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 = {
    "queries": [
        "history"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("deriverge/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 = { "queries": ["history"] }

# Run the Actor and wait for it to finish
run = client.actor("deriverge/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 '{
  "queries": [
    "history"
  ]
}' |
apify call deriverge/apple-podcasts-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,deriverge/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/EjwC41p2HhQjRqKQr/builds/4fqlbtfWLHX3aXfXn/openapi.json
