# Spotify Podcast Episodes Monitor - New Episodes & Ratings (`neverempty/spotify-podcast-episodes-monitor`) Actor

Latest episodes and show details for Spotify podcasts: title, release time, duration and link of each episode in the public show page, plus publisher, rating average, rating count and episode total. Monitoring mode returns only new episodes and rating changes.

- **URL**: https://apify.com/neverempty/spotify-podcast-episodes-monitor.md
- **Developed by:** [NeverEmpty](https://apify.com/neverempty) (community)
- **Categories:** Social media, Automation
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.40 / 1,000 episode 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

## Spotify Podcast Episodes Monitor

The latest episodes and the show details of Spotify podcasts, read from the public show page: for each **episode** its title, release time (to the minute), duration, the start of its description and its link; for each **show** its name, publisher, **rating average and rating count**, total number of episodes, language and topics. With monitoring on, a run returns **only the episodes that are new since the last run and the shows whose rating count changed** - and a show with nothing new costs one small check instead of a row.

Paste shows as page URLs, `spotify:show:` URIs or 22-character show IDs, mixed in one list.

**This Actor reads the public show page only.** That page carries the most recent episodes of a show (12 when this was measured), not its whole back catalogue. Every show row says how many episodes were in the page and how many the show has in total. No audio, no transcripts, no follower or play counts: those are not in the page.

### What you get

Two kinds of rows, told apart by `type`.

`type: "show"` - one per show:

| Field | Meaning |
|---|---|
| `showName`, `publisher` | As Spotify writes them in the show page |
| `ratingAverage` | Average star rating, rounded to two decimals. `null` when the show has no ratings or Spotify does not display an average |
| `ratingCount` | Number of ratings, the integer in the page data |
| `totalEpisodes` | Number of episodes the show has, as stated in the page |
| `episodesOnPage`, `episodesCoverage` | How many episodes were in the public page, as a number and as a sentence ("12 of 2759 episodes were in the public show page") |
| `latestEpisodeName`, `latestEpisodeReleasedAt` | The first episode in the page |
| `description`, `language`, `topics`, `isExplicit`, `mediaType` | As stated in the page. `language` is what the publisher declared |
| `showId`, `showUrl`, `input` | The show that was read and the line you gave for it |
| `pageCountry` | Country of the connection the page was built for |
| `ratingCountChange`, `totalEpisodesChange`, `newEpisodesInPage`, `changedFields`, `previous...` | Monitoring only: what moved since the last reported figures |

`type: "episode"` - one per episode found in the show page:

| Field | Meaning |
|---|---|
| `episodeName` | Episode title |
| `releasedAt`, `releaseDatePrecision` | Release time as an ISO timestamp (UTC) and the precision Spotify states for it |
| `durationSeconds`, `durationMs` | Length of the episode |
| `descriptionStart`, `descriptionLength` | The first 500 characters of the episode description, and the length of the full description |
| `episodeUrl`, `episodeId` | Link and ID of the episode |
| `position` | Place in the show page (1 = first, which is the newest in every show measured) |
| `isExplicit`, `isPaywalled`, `mediaTypes` | As stated in the page |
| `showName`, `showId`, `showUrl` | The show the episode was listed under |
| `isNew` | Monitoring only: `true` for an episode that was not there at the previous run |

A value that is not in the page is `null`, never 0 or a guess.

### Only the episodes in the public page

Spotify puts the most recent episodes of a show into its public page and loads older ones only inside its own player. This Actor does not use the player's internal API, a login or an API token, so it returns the episodes in the page: enough to follow what a show publishes, not to export an archive of hundreds of episodes.

### Why every row names the show it read

The show ID in each row is checked against the ID in the page data before anything is taken from it, and every episode row carries the show name stated in the page. If Spotify answers with a different page, the line comes back as a row that explains what happened and is not charged.

### Input

- `shows` - show URLs (`https://open.spotify.com/show/4rOoJ6Egrf8K2IrywzwOMk`), `spotify:show:` URIs or show IDs. Links with `intl-xx/` or `?si=` are fine. Episode, artist and playlist links are not read (for artists and playlists see [Spotify Monthly Listeners Tracker](https://apify.com/neverempty/spotify-monthly-listeners-tracker) and [Spotify Playlist Tracker](https://apify.com/neverempty/spotify-playlist-tracker)).
- `maxEpisodesPerShow` - with monitoring off, how many of the episodes in the page to return per show (0 = show row only).
- `maxShows` - with monitoring off, stop after this many shows.
- `monitoringMode`, `watch`, `minRatingCountChange` - see below.
- `resetMonitoringState` - forget every remembered show on your account, so each one is treated as a first check again.
- `useProxy` - on by default: if Spotify answers HTTP 403 or 429, the page is requested once more through an Apify residential proxy (at most three times in a run). Turn it off to never use a proxy.

### Monitoring mode

Turn on `monitoringMode` and run the Actor on a schedule with the same shows.

1. **First check of a show**: the episode IDs in its page, its rating count and its episode total are remembered. Nothing is returned for it and nothing is charged; you get a `first-check` note row.
2. **Later runs**: each show is read again and compared.
   - An episode that was not there before comes back as an `episode` row with `isNew: true`.
   - A `show` row comes back when the rating count moved by at least `minRatingCountChange` (for shows with 1,000 ratings or more: by at least 3, because the public figure of a large show can differ by 1 from one read to the next), or when the episode total moved by a number that the new episodes found in the page do not explain (episodes were removed, or more were published than the page shows). `changedFields` says which: `rating`, `episode-count` or both.
   - A show with nothing to return is charged as one check and produces no row. If no show had anything, the dataset holds one `no-change` note.

`watch` narrows this: **episodes-only** ignores rating changes (the rating count of a large show moves every day), **ratings-only** does not return new episodes.

What is remembered is what was actually delivered. If the maximum charge of a run is reached before every new episode fits, the ones that did not fit are not remembered and come back in a later run.

Three things to know:

- The page shows only the most recent episodes. If a show publishes more episodes between two runs than the page holds, the older ones of those are not seen as episode rows; the show row then reports the gap (`totalEpisodesChange` larger than `newEpisodesInPage`). Run often enough for the shows you watch.
- An episode counts as new when its ID was not seen before and it was released after the oldest episode of the previously remembered page. An old episode that reappears because a newer one was removed is therefore not reported as new; an episode published with a release date older than that, or without a readable release date, is not reported either. Shows whose page already lists every episode are exempt from this date rule.
- Shows are remembered one by one on your account, not per list. Adding a show does not reset the others. Do not run two schedules with the same show at the same moment: the remembered state has no locking.

### What is charged

- `episode-returned` - each episode row.
- `show-returned` - each show row.
- `show-checked` - in monitoring mode, each show that was compared and had nothing to return.

Not charged: first monitoring checks, and every note row (see below).

### Rows that explain instead of failing

Rows with `type: "note"` are never charged. Their `status` says what happened:

| `status` | Meaning |
|---|---|
| `no-such-show` | Spotify answered 404 for this ID (removed, or not a podcast show) |
| `not-available` | Spotify answered with its "not currently available" page: the show is not offered, at least not in the country the page was read from |
| `invalid-input` | The line is not a show link, URI or ID (the note says what it looks like) |
| `duplicate-input` | The same show was already given on an earlier line |
| `unreadable` | The page came back without the show data, or in a form that could not be read |
| `blocked` | Spotify refused the request; the run stops asking and lists what it did not read |
| `robots-disallowed` | Spotify's robots.txt disallows the page, so it was not requested |
| `first-check` | Monitoring: the show was remembered as a starting point |
| `no-change` | Monitoring: nothing was new in any show that was compared |
| `not-read` | Lines the run did not get to, and why |
| `budget-reached` | The run's maximum total charge was reached before every ready row could be delivered |

### Limits

- **Episodes**: only those in the public show page (see above). Not a full archive.
- **No audio, no transcripts, no follower counts, no play counts.** They are not in the public page, and audio is deliberately not touched.
- **Ratings** are Spotify's own listener ratings as stated in the page. They are not Apple Podcasts ratings.
- **Country**: Spotify builds the page for the country of the connection (`pageCountry`). A show that is not offered there comes back as `not-available`.
- Only the public show page (`open.spotify.com/show/<id>`) is requested, one page per show, about one request every two seconds, with a User-Agent that identifies this Actor. Spotify's robots.txt is read at the start of every run and each page is checked against it. No login, no Spotify API token, no embed pages. If Spotify stops serving the data in that page, rows come back as `unreadable` rather than as guesses.
- This Actor is not affiliated with, endorsed by or sponsored by Spotify. Spotify's terms of use restrict automated collection from its service; decide for yourself whether your use is permitted before you run it.

### Thanks for using this Actor

We build these tools for people who run them every day, and we improve them from what users tell us.

- **Missing a field, or need another filter?** Tell us in the **Issues** tab. If the data is there, we add it.
- **Found a bug or a wrong value?** Post the run ID in the **Issues** tab. Wrong data is the thing we fix first.

If this Actor saved you time, a short review helps other people find it.

# Actor input Schema

## `shows` (type: `array`):

Show page URLs (https://open.spotify.com/show/4rOoJ6Egrf8K2IrywzwOMk), spotify:show: URIs, or the 22-character show IDs. Only open.spotify.com/show/<id> pages are requested, one request per show. Episode, artist, playlist and embed links are not read and come back as an "invalid-input" row that is not charged; an ID Spotify has no show page for comes back as a "no-such-show" row that is not charged. Show names are not searched: use the link from the Share menu in Spotify.

## `maxEpisodesPerShow` (type: `integer`):

With monitoring off, how many of the episodes found in the show page are returned per show, newest first. The public show page carries the most recent episodes only (12 at the time of writing), so a larger number does not return more. 0 = return the show row only. Monitoring mode ignores this and returns every new episode it finds.

## `maxShows` (type: `integer`):

With monitoring off, the run stops after this many show rows and lists the lines it did not read in a row that is not charged. Monitoring mode ignores this and checks every show you listed.

## `monitoringMode` (type: `boolean`):

Off = return every show you listed with the episodes in its page, charged per show row and per episode row. On = the first run remembers each show (the episode IDs in its page, its rating count and its episode total) and returns nothing for it, free of charge. Later runs return an episode row for each episode that was not there before, and a show row when the rating count changed or when the episode total moved by a number the new episodes in the page do not explain. A show that was compared and had nothing to return is charged as one check (show-checked) instead. Shows are remembered one by one, not per list, so adding a show does not reset the others. Because the page carries only the most recent episodes, run often enough that fewer new episodes than that appear between two runs. Do not put the same show in two schedules that run at the same moment.

## `watch` (type: `string`):

New episodes and ratings = both are returned. New episodes only = a rating change alone returns nothing (useful for large shows, whose rating count moves every day). Ratings only = new episodes are not returned.

## `minRatingCountChange` (type: `integer`):

A show row is returned for a rating change only when the rating count differs from the last reported count by at least this many. Smaller differences are not reported and not remembered, so they add up until the threshold is reached. For shows with 1,000 ratings or more the smallest reported change is 3 even when this is lower, because the public figure of a large show can differ by 1 from one read to the next.

## `resetMonitoringState` (type: `boolean`):

Clears everything remembered for this Actor on your account, so the next monitoring run treats each show as a first check again. This affects all your monitoring runs, because shows are remembered one by one rather than per list.

## `useProxy` (type: `boolean`):

Requests go directly to Spotify. If Spotify answers HTTP 403 or 429, the page is requested one more time through an Apify residential proxy, at most three times in a run; if it is refused again, or a fourth page is refused, the run stops asking and lists what it did not read. A show that Spotify does not offer in the country of the request (its "not currently available" page) is reported as such and is not retried. Turn this off to never use a proxy.

## Actor input object example

```json
{
  "shows": [
    "https://open.spotify.com/show/4rOoJ6Egrf8K2IrywzwOMk",
    "2MAi0BvDc6GTFvKFPXnkCL"
  ],
  "maxEpisodesPerShow": 12,
  "maxShows": 100,
  "monitoringMode": false,
  "watch": "episodes-and-ratings",
  "minRatingCountChange": 1,
  "resetMonitoringState": false,
  "useProxy": true
}
```

# Actor output Schema

## `results` (type: `string`):

One "show" row per Spotify podcast show (name, publisher, rating average and count, episode total, how many episodes were in the public page) and one "episode" row per episode found in that page (title, release time, duration, start of the description, link). In monitoring mode only new episodes and shows whose rating count or episode total changed are returned. IDs Spotify has no show page for, shows Spotify says are not currently available, lines that are not show links, pages that could not be read, first monitoring checks and runs where nothing was new come back as "note" rows that are not charged.

# 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 = {
    "shows": [
        "https://open.spotify.com/show/4rOoJ6Egrf8K2IrywzwOMk",
        "2MAi0BvDc6GTFvKFPXnkCL"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("neverempty/spotify-podcast-episodes-monitor").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 = { "shows": [
        "https://open.spotify.com/show/4rOoJ6Egrf8K2IrywzwOMk",
        "2MAi0BvDc6GTFvKFPXnkCL",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("neverempty/spotify-podcast-episodes-monitor").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 '{
  "shows": [
    "https://open.spotify.com/show/4rOoJ6Egrf8K2IrywzwOMk",
    "2MAi0BvDc6GTFvKFPXnkCL"
  ]
}' |
apify call neverempty/spotify-podcast-episodes-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,neverempty/spotify-podcast-episodes-monitor"
        }
    }
}
```

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/zFu2461yLS5rFZpbZ/builds/4Og0NcAvWeym1TpqH/openapi.json
