# Podcast RSS Feed Scraper — Episodes & Transcripts (`owlabee/podcast-rss-scraper`) Actor

Scrape podcast RSS feeds, Apple Podcasts URLs or search terms into show and episode records (title, pubDate, duration, audio URL, chapters, Podcasting 2.0 transcripts) with optional transcript text. Pay per episode ($0.0005), show ($0.001) and transcript ($0.005).

- **URL**: https://apify.com/owlabee/podcast-rss-scraper.md
- **Developed by:** [Owlabee](https://apify.com/owlabee) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 1,000 episode scrapeds

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 Scraper — Episodes & Transcripts

### What it does

Podcast RSS Feed Scraper turns any podcast feed into clean, structured JSON. Give it RSS/Atom feed URLs, Apple Podcasts show URLs or plain search terms (`search:the daily`), and it returns one **show** record per feed (title, description, author, owner email when published, artwork, categories, explicit flag, episode count, website, Podcasting 2.0 `podcast:guid`, funding links, locked flag) and one **episode** record per item (GUID, title, show notes as plain text, ISO publish date, duration in seconds, season/episode numbers, episode type, audio URL/type/size, artwork, link, chapters URL, `<podcast:transcript>` links, person count).

It parses the RSS 2.0, iTunes and Podcasting 2.0 namespaces with a fast XML parser (no browser, no proxies), handles the odd feed (non-standard namespace prefixes, missing GUIDs, `MM:SS` vs `HH:MM:SS` vs seconds durations, HTML show notes) and resolves Apple Podcasts links and search terms through Apple's public iTunes Lookup/Search API, so you never need to hunt for the feed URL yourself.

Turn on **Fetch transcripts** to download each episode's transcript file (plain text, WebVTT, SRT, JSON or HTML, max 2 MB) and get `transcriptText` with cue timestamps stripped and speakers labelled, plus a `transcriptWords` count — ready for search, summarization, RAG pipelines or LLM analysis. Turn on **Only new episodes since the last run** to use it as a monitor: the Actor remembers which episode GUIDs it has seen per feed and returns only new ones, optionally POSTing them to a webhook.

Built for podcast analytics and discovery tools, newsrooms and research teams tracking shows, marketers monitoring competitor and sponsor mentions, and AI agents that need episode metadata and transcripts on demand. Results are available as JSON, CSV, Excel, through the API, webhooks and the Apify MCP server.

### How to use

1. Enter your **Feeds** (one per line): feed URLs, Apple Podcasts URLs (`https://podcasts.apple.com/us/podcast/…/id1200361736`), bare Apple IDs or `search:<show name>`.
2. Optionally set **Max episodes per feed**, turn on **Fetch transcripts** or **Only new episodes since the last run**, and add a **Webhook URL**.
3. Click **Start**. Show and episode records appear in the dataset as each feed is parsed; download them or connect via API/MCP. For monitoring, schedule the Actor and keep `onlyNewSinceLastRun` on.

### Input

| Field | Type | Required | Description |
|---|---|---|---|
| `feeds` | string\[] | yes | Feed URLs, Apple Podcasts URLs, Apple IDs or `search:<term>` entries, one per line. A show web page with an RSS autodiscovery link also works |
| `maxEpisodesPerFeed` | integer | no | Newest-first cap on episode records per feed (default 100) |
| `includeShowRecord` | boolean | no | Write one `recordType: "show"` item per feed (default `true`) |
| `fetchTranscripts` | boolean | no | Download the best available `<podcast:transcript>` per episode and attach `transcriptText`/`transcriptWords` (default `false`) |
| `onlyNewSinceLastRun` | boolean | no | Return only episodes not seen in earlier runs; the first run is a baseline (default `false`) |
| `stateStoreName` | string | no | Key-value store holding the seen-episode GUIDs per feed (default `podcast-rss-scraper-state`) |
| `maxConcurrency` | integer | no | Parallel transcript downloads, 1–10 (default 5); same-host requests are spaced ≥ 500 ms |
| `webhookUrl` | string | no | POST target for the run summary and, with `onlyNewSinceLastRun`, the list of new episodes |

### Output example

A show record:

```json
{
  "recordType": "show",
  "feedUrl": "https://podnews.net/rss",
  "title": "Podnews Daily - podcast industry and podcasting news",
  "description": "Daily news for people in podcasting - from Apple Podcasts to Spotify and YouTube; …",
  "language": "en-gb",
  "author": "Podnews LLC",
  "ownerEmail": "editor+dailyrss@podnews.net",
  "imageUrl": "https://podnews.net/static/podnews-2000x2000.png",
  "categories": ["News > Daily News", "Technology", "podcasting", "radio"],
  "explicit": false,
  "episodeCount": 151,
  "episodesReturned": 10,
  "lastBuildDate": "2026-10-16T11:00:15.000Z",
  "latestEpisodeAt": "2026-10-16T10:09:41.000Z",
  "websiteUrl": "https://podnews.net",
  "podcastGuid": "9b024349-ccf0-5f69-a609-6b82873eab3c",
  "funding": [{ "url": "https://podnews.net/donate", "message": "Support us" }],
  "showType": "episodic",
  "copyright": "2026 Podnews LLC",
  "generator": "Podnews LLC",
  "feedFormat": "rss",
  "input": "https://podnews.net/rss",
  "resolvedVia": "direct",
  "fetchedAt": "2026-10-16T13:13:06.512Z"
}
```

An episode record (with `fetchTranscripts: true`; the `transcript*` fields are absent otherwise):

```json
{
  "recordType": "episode",
  "feedUrl": "https://podnews.net/rss",
  "showTitle": "Podnews Daily - podcast industry and podcasting news",
  "episodeGuid": "https://podnews.net/update/pave-disney-hulu-clues",
  "title": "PAVE Studios shows head to Disney+ and Hulu",
  "description": "Shows include Clues with Kaelyn Moore.\n\nSponsored by SoundStack. …",
  "pubDate": "2026-10-16T10:09:41.000Z",
  "durationSeconds": 291,
  "episode": 2450,
  "episodeType": "full",
  "explicit": false,
  "audioUrl": "https://op3.dev/e,pg=9b024349-ccf0-5f69-a609-6b82873eab3c/podnews.net/audio/podnews261016.mp3",
  "audioType": "audio/mpeg",
  "audioBytes": 4684006,
  "imageUrl": "https://podnews.net/audio/podnews261016.jpeg",
  "link": "https://podnews.net/update/pave-disney-hulu-clues",
  "transcripts": [{ "url": "https://podnews.net/audio/podnews261016.mp3.vtt", "type": "text/vtt", "rel": "captions" }],
  "personsCount": 0,
  "transcriptUrl": "https://podnews.net/audio/podnews261016.mp3.vtt",
  "transcriptType": "text/vtt",
  "transcriptFormat": "vtt",
  "transcriptText": "James Cridland: From our free daily newsletter, the latest from podnews.net with Soundstack. Another two podcasts are making it to a streaming service: this time from PAVE Studios. …",
  "transcriptWords": 730,
  "fetchedAt": "2026-10-16T13:13:06.512Z"
}
```

Optional episode fields (`season`, `chaptersUrl`, `imageUrl`, `link`, `durationSeconds`, …) are present only when the feed provides them. Shows resolved through Apple carry `appleId`, `appleUrl` and, for `search:` inputs, `searchAlternatives` (the other top matches with their feed URLs). With `onlyNewSinceLastRun` each episode carries `isNew: true` and the show record carries `newEpisodes` and `baseline`.

Error items look like `{ "recordType": "error", "input": "…", "error": "FEED_FAILED", "errorMessage": "HTTP 404 for …" }` (codes: `FEED_FAILED`, `APPLE_ID_NOT_FOUND`, `APPLE_NO_FEED_URL`, `APPLE_LOOKUP_FAILED`, `SEARCH_NO_RESULTS`, `ITUNES_SEARCH_FAILED`, `GUID_RESOLUTION_UNAVAILABLE`, `INVALID_INPUT`) and are charged at the show rate, because the work to discover the problem was done. A transcript that cannot be downloaded leaves the episode record intact with a `transcriptError` field.

### Pricing

Pay per event. You only pay for what the Actor produces; set a **maximum cost per run** in the run options to cap spend.

| Event | Price | Notes |
|---|---|---|
| Episode scraped | $0.0005 | Per episode record |
| Show scraped | $0.001 | Per show record, including error items for feeds that could not be resolved or fetched |
| Transcript fetched | $0.005 | Per transcript download attempted (on top of the episode event); unreachable transcripts are recorded with `transcriptError` and charged too |
| Actor start | $0.00005 | Apify platform event |

**Example:** 1,000 episodes ≈ **$0.50**; 100 transcripts ≈ **$0.50** (plus $0.05 for their 100 episode records). A daily monitor of 20 shows with `onlyNewSinceLastRun` that finds 5 new episodes a day ≈ $0.0225/day (20 show records + 5 episodes). Episodes without a `<podcast:transcript>` tag never incur the transcript event.

### Limits and fair use

- Feeds are fetched with plain HTTP from Apify datacenter IPs, up to 30 MB each (the largest public feeds with 3,000+ episodes are about 20 MB), with a 20 s timeout and at most ~2 requests per second per host. Transcripts are capped at 2 MB each.
- Only `<podcast:transcript>` files published in the feed are downloaded; the Actor does not transcribe audio. When several formats are offered it picks, in order, plain text, WebVTT, SRT, JSON, HTML.
- Apple Podcasts URLs and `search:` terms are resolved through Apple's public iTunes Lookup/Search API (no key needed, rate-limited to about 20 requests per minute, so resolving many Apple inputs is slower than passing feed URLs). Shows that Apple lists without a public `feedUrl` (Apple-exclusive or subscription shows) produce an `APPLE_NO_FEED_URL` error item. Podcast Index GUIDs cannot be resolved without an API key and produce `GUID_RESOLUTION_UNAVAILABLE`.
- `maxEpisodesPerFeed` cuts newest-first. With `onlyNewSinceLastRun`, the baseline run records the whole feed as seen (it returns the newest episodes up to the cap; older ones are history, not news); on later runs, new episodes beyond the cap are left for the next run rather than silently dropped. The seen-GUID set per feed is capped at 5,000 episodes.
- Only public data is accessed: feeds, transcript files and Apple's public catalogue. No login, no proxies, no personal data beyond what the publisher puts in the feed (`ownerEmail` is included only when the feed publishes it).

### Related Actors

Other pay-per-event Actors from the same account, all limited-permission and agent-friendly:

- [PDF & DOCX to Markdown Converter for AI Agents](https://apify.com/owlabee/pdf-to-markdown) — convert the PDFs and DOCX files linked from show notes into Markdown and RAG chunks.
- [Bluesky Posts, Profiles & Followers Scraper](https://apify.com/owlabee/bluesky-monitor) — posts, profiles and followers for the shows and hosts you track.
- [Website Change Monitor — Sitemap & Page Content Diff](https://apify.com/owlabee/website-change-monitor) — sitemap and content change feed for podcast and network websites.

### FAQ

**Does this need proxies or an API key?** No. Feeds, transcript files and the iTunes Lookup/Search API are public endpoints read over plain HTTP from Apify's standard infrastructure. Nothing to configure.

**How is cost controlled?** Cost = episodes × $0.0005 + shows × $0.001 + transcripts × $0.005. Limit episodes with `maxEpisodesPerFeed`, leave `fetchTranscripts` off unless you need the text, use `onlyNewSinceLastRun` for recurring runs so you pay only for new episodes, and set the run's maximum total charge; the Actor stops cleanly when it is reached.

**Can I pass an Apple Podcasts link or just the show name?** Yes. `https://podcasts.apple.com/…/id123456789` (or the bare ID) is resolved with the iTunes Lookup API; `search:show name` uses the iTunes Search API and takes the top match with a public feed, recording the other matches in `searchAlternatives` on the show record so you can pick a different one next time.

**What formats of transcripts are supported?** Plain text, WebVTT (`text/vtt`), SRT (`application/srt`, `application/x-subrip`), Podcasting 2.0 JSON (`application/json`) and HTML. Timestamps, cue numbers, styling tags and repeated rolling-caption lines are removed; speaker names from `<v>` tags, `Name:` prefixes or JSON `speaker` fields are kept as `Name: …` labels.

**How does monitoring work?** With `onlyNewSinceLastRun` the Actor stores the episode GUIDs of each feed in the key-value store named by `stateStoreName`. The first run is a baseline that returns the newest episodes up to `maxEpisodesPerFeed`; later runs return only episodes with GUIDs not seen before and POST them to `webhookUrl` if set. Use a different `stateStoreName` to keep independent monitors.

**Can I run it on a schedule or call it from an AI agent?** Yes — use Apify Schedules, the API, or the Apify MCP server. The Actor is pay-per-event and uses limited permissions (its own dataset and the state store you name), so agents can discover and call it. Example prompts: "Get the last 20 episodes of The Daily with durations and audio URLs", "Download the transcripts of this week's Podnews episodes and summarize them", "Tell me when any of these 10 podcast feeds publishes a new episode".

**What happens on errors?** A feed that cannot be resolved, fetched or parsed produces an `error` item with a clear code instead of failing the whole run; other feeds in the same run are unaffected. Transcript problems are recorded per episode in `transcriptError`.

### Legal

Use the data in compliance with each publisher's terms and applicable law. The Actor reads only publicly published podcast feeds, transcript files linked from them and Apple's public catalogue API, keeps request rates polite and does not download audio. It is not a consumer reporting agency and its output must not be used for employment, credit, insurance, or tenant screening decisions.

# Actor input Schema

## `feeds` (type: `array`):

One entry per line. Accepts podcast RSS/Atom feed URLs, Apple Podcasts show URLs (https://podcasts.apple.com/…/id123456789, resolved through the public iTunes Lookup API), bare Apple IDs, and search terms written as `search:the daily` (resolved through the iTunes Search API; the top result is used and the other matches are recorded on the show record). A show's web page works too when it carries an RSS autodiscovery link. The prefilled feeds are the cheap, always-available examples used by Apify's daily automated test.

## `maxEpisodesPerFeed` (type: `integer`):

Newest-first cap on episode records per feed. The show record still reports the full `episodeCount`. With `onlyNewSinceLastRun`, new episodes beyond the cap are left for the next run.

## `includeShowRecord` (type: `boolean`):

Write one `recordType: "show"` item per feed with the show-level metadata (title, description, author, categories, image, podcast GUID, funding, episode count). Turn off to get episode records only.

## `fetchTranscripts` (type: `boolean`):

Download each episode's `<podcast:transcript>` file (first available in order text, VTT, SRT, JSON, HTML; max 2 MB) and attach `transcriptText` (plain text, cue timestamps stripped) and `transcriptWords`. Charged per transcript on top of the episode event; episodes without a transcript tag are not charged for it.

## `onlyNewSinceLastRun` (type: `boolean`):

Remember the episode GUIDs of each feed in the state store and return only episodes not seen before. The first run for a feed is a baseline and returns the newest episodes up to the cap. Ideal for scheduled monitoring and webhooks.

## `stateStoreName` (type: `string`):

Named key-value store that keeps the seen-episode GUIDs per feed when `onlyNewSinceLastRun` is on. Use a different name for independent monitors (e.g. per client or schedule).

## `maxConcurrency` (type: `integer`):

Parallel transcript downloads per feed. Requests to the same host are additionally spaced at least 500 ms apart (about 2 requests per second per host).

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

If set, the Actor POSTs a JSON summary of the run (per-feed counts and, with `onlyNewSinceLastRun`, the list of new episodes) to this URL — 10 s timeout, 2 retries. Failures are logged, never fatal.

## Actor input object example

```json
{
  "feeds": [
    "https://feeds.npr.org/510318/podcast.xml",
    "https://podnews.net/rss"
  ],
  "maxEpisodesPerFeed": 10,
  "includeShowRecord": true,
  "fetchTranscripts": false,
  "onlyNewSinceLastRun": false,
  "stateStoreName": "podcast-rss-scraper-state",
  "maxConcurrency": 5
}
```

# Actor output Schema

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

One item per show, episode or transcript in the default dataset (recordType, showTitle, title, pubDate, durationSeconds, audioUrl, link, transcriptWords; error items carry error and errorMessage). Fields are described in dataset_schema.json.

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

SUMMARY record in the run's default key-value store: feeds processed, shows, episodes and transcripts produced, charges and webhook delivery status, written when the run finishes.

# 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 = {
    "feeds": [
        "https://feeds.npr.org/510318/podcast.xml",
        "https://podnews.net/rss"
    ],
    "maxEpisodesPerFeed": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("owlabee/podcast-rss-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 = {
    "feeds": [
        "https://feeds.npr.org/510318/podcast.xml",
        "https://podnews.net/rss",
    ],
    "maxEpisodesPerFeed": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("owlabee/podcast-rss-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 '{
  "feeds": [
    "https://feeds.npr.org/510318/podcast.xml",
    "https://podnews.net/rss"
  ],
  "maxEpisodesPerFeed": 10
}' |
apify call owlabee/podcast-rss-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,owlabee/podcast-rss-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/9XhfM1cdqY0kmrZ47/builds/xeQ0MkGxaceAkzIMW/openapi.json
