# Podcast RSS Feed Validator - Apple, Spotify & Podcasting 2.0 (`that_red_bird/podcast-feed-validator`) Actor

Bulk-check podcast RSS feeds against Apple Podcasts, Spotify and Podcasting 2.0 rules: required tags, GUIDs, dates, https, cover-art size, audio reachability and byte-range support. Score and fix list per feed; schedule it. Unreadable feeds are never charged.

- **URL**: https://apify.com/that_red_bird/podcast-feed-validator.md
- **Developed by:** [mohamed alaya](https://apify.com/that_red_bird) (community)
- **Categories:** Other, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 feed validateds

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

## Podcast RSS Feed Validator - Apple, Spotify & Podcasting 2.0

Checks podcast RSS feeds against the rules Apple Podcasts, Spotify and the Podcasting 2.0 namespace expect, and returns a score plus a fix list for each feed. Built for podcast hosts, networks and agencies that manage many shows and want to catch a broken feed *before* a directory rejects it. Bulk (paste your whole network) and schedule-friendly (alert on score drops).

No API key, no login, no browser. It only reads your public feeds.

### What it checks

- **Feed**: reachable, https, content type, size, response time, redirects, XML declaration and encoding, RSS 2.0 root, `itunes` and `podcast` namespaces declared.
- **Show**: title, description, language, link, owner email, author, explicit flag, Apple category, `itunes:type`, `itunes:block`.
- **Cover art**: present, https, downloadable, JPEG or PNG, square, between 1400x1400 and 3000x3000 pixels.
- **Episodes** (newest 50 by default): title, enclosure URL / type / length, https, unique GUIDs, pubDate parseable and not in the future, duration format, episode type, season / episode numbers, descriptions, duplicate titles.
- **Audio** (newest 3 by default): the file is reachable, served as audio, supports byte-range requests (Apple needs them) and its size matches the declared length. The Actor requests only the first two bytes; it does not download your audio.
- **Podcasting 2.0**: `podcast:guid` (valid UUID), `locked`, `funding`, `person`, transcripts, chapters; coverage level none / minimal / basic / advanced.

### Input examples

Validate a whole network:

```json
{ "feedUrls": ["https://example.com/show-a.xml", "https://example.com/show-b.xml"], "episodesToCheck": 3 }
```

Use Apple Podcasts links and get a flat spreadsheet of issues:

```json
{ "feedUrls": ["https://podcasts.apple.com/us/podcast/planet-money/id290783428"], "rowPerIssue": true }
```

Scheduled monitor that lists feeds under a score of 80 in the run summary:

```json
{ "feedUrls": ["https://example.com/show-a.xml"], "flagScoreBelow": 80, "episodesToCheck": 1 }
```

`{}` works: it validates two public sample feeds so you can see the report format.

### Output fields (one row per feed)

| Field | Meaning |
|---|---|
| `title`, `feedUrl`, `finalUrl`, `resolvedFrom` | The show and where the feed really lives. |
| `score` | 0-100: 100 minus 12 per error and 3 per warning. |
| `errors`, `warnings`, `infos` | Issue counts. |
| `appleReady`, `spotifyReady` | True when no *error* tagged for that directory was found. This is a pre-flight check, not a guarantee of acceptance. |
| `episodeCount`, `episodesValidated`, `episodesAudioChecked`, `newestEpisodeAt`, `daysSinceLastEpisode` | Feed contents. |
| `artwork` | URL, format, width, height, bytes. |
| `podcasting20` | Which Podcasting 2.0 channel tags exist and a coverage level. |
| `issues` | List of `{severity, rule, scope, episode, message, fix, tags}`. `tags` shows which directory cares (apple, spotify, p20, core). |

Sample row (shortened):

```json
{"type":"feed","title":"Planet Money","feedUrl":"https://feeds.npr.org/510289/podcast.xml","score":85,"errors":0,"warnings":5,"appleReady":true,"spotifyReady":true,"episodeCount":355,"artwork":{"format":"jpeg","width":1400,"height":1400},"podcasting20":{"level":"minimal"},"issues":[{"severity":"warning","rule":"explicit-missing","message":"The show has no <itunes:explicit> value."}]}
```

A feed that cannot be fetched or is not RSS becomes a `{"type":"error"}` row and is not charged.

### Use it with

- Apify schedule daily or weekly plus Slack / email integration on the summary, so a broken feed after a hosting change is caught the same day.
- Pair with a podcast-directory scraper or your hosting export that lists feed URLs.

### FAQ

**Is this Apple's or Spotify's official validator?** No. It implements the published feed requirements as best understood; each directory can reject a feed for reasons not visible in the XML (content policy, ownership, duplicates).

**Why is a rule only a warning?** Errors are reserved for things the directories list as required or that make a feed unusable. Recommendations are warnings, and Podcasting 2.0 items are informational.

**Does it measure loudness?** No. Loudness measurement needs downloading the full audio and is not included.

### Honest limits

- Only the first `maxEpisodes` items of the feed are checked; feeds with 1,000 episodes are validated on their newest part.
- The well-formedness check is heuristic (the feed is parsed leniently); a document with a subtle XML syntax error may still pass.
- Audio tests use small byte-range requests; hosts that block datacenter IPs may report false "unreachable" errors.
- Rules follow public documentation and can drift as Apple and Spotify change their requirements.

### Price

Pay per event: **`feed-validated` $0.005** per feed report, plus **`episode-checked` $0.001** per episode whose audio was tested. Example: 20 feeds with the default 3 audio tests each = 20 x $0.005 + 60 x $0.001 = **$0.16**. Unreadable feeds and issue rows are free.

# Actor input Schema

## `feedUrls` (type: `array`):

RSS feed URLs to validate, one per line. Apple Podcasts show URLs (podcasts.apple.com/.../id123) also work and are resolved to the feed. Leave empty to validate two public sample feeds.

## `maxEpisodes` (type: `integer`):

How many episodes (from the top of the feed) get the structural checks: GUID, pubDate, enclosure, duration, description and so on.

## `episodesToCheck` (type: `integer`):

For this many of the newest episodes, the Actor sends a small byte-range request to the audio URL to check it is reachable, served as audio, supports byte ranges and matches the declared size. 0 skips audio tests.

## `checkArtwork` (type: `boolean`):

Download the start of the cover art and check format (JPEG/PNG), squareness and size (1400x1400 to 3000x3000 pixels).

## `staleDays` (type: `integer`):

Add an informational note when the newest episode is older than this many days.

## `rowPerIssue` (type: `boolean`):

Besides the one report row per feed, add a flat row for every issue (good for spreadsheets). Issue rows are free.

## `flagScoreBelow` (type: `integer`):

List feeds whose score (0-100) is below this number in the run summary, for scheduled monitoring. 0 turns it off.

## `concurrency` (type: `integer`):

How many feeds are validated at the same time. Keep it modest when your feeds share one host.

## Actor input object example

```json
{
  "feedUrls": [],
  "maxEpisodes": 50,
  "episodesToCheck": 3,
  "checkArtwork": true,
  "staleDays": 90,
  "rowPerIssue": false,
  "flagScoreBelow": 0,
  "concurrency": 3
}
```

# Actor output Schema

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

No description

## `downloadCsv` (type: `string`):

No description

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

No description

## `count` (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 = {
    "feedUrls": [],
    "maxEpisodes": 50,
    "episodesToCheck": 3,
    "checkArtwork": true,
    "staleDays": 90,
    "rowPerIssue": false,
    "flagScoreBelow": 0,
    "concurrency": 3
};

// Run the Actor and wait for it to finish
const run = await client.actor("that_red_bird/podcast-feed-validator").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 = {
    "feedUrls": [],
    "maxEpisodes": 50,
    "episodesToCheck": 3,
    "checkArtwork": True,
    "staleDays": 90,
    "rowPerIssue": False,
    "flagScoreBelow": 0,
    "concurrency": 3,
}

# Run the Actor and wait for it to finish
run = client.actor("that_red_bird/podcast-feed-validator").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 '{
  "feedUrls": [],
  "maxEpisodes": 50,
  "episodesToCheck": 3,
  "checkArtwork": true,
  "staleDays": 90,
  "rowPerIssue": false,
  "flagScoreBelow": 0,
  "concurrency": 3
}' |
apify call that_red_bird/podcast-feed-validator --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,that_red_bird/podcast-feed-validator"
        }
    }
}
```

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/cJMBl79l5Nn35Ae60/builds/y5ApI2R3gy5Zj5DIN/openapi.json
