# Podcast Owner & Contact Email Finder - Directory & RSS Scraper (`neverempty/podcast-directory-contacts`) Actor

Find podcasts by keyword and get the owner / contact email each show publishes, plus website, episode count, last episode date and frequency. Emails come only from the show's own RSS feed or website, each with its source. Shows without a contact cost less.

- **URL**: https://apify.com/neverempty/podcast-directory-contacts.md
- **Developed by:** [NeverEmpty](https://apify.com/neverempty) (community)
- **Categories:** Lead generation, Marketing
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $24.00 / 1,000 podcast with a contact emails

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

## Podcast Owner & Contact Email Finder - podcast directory and RSS scraper

Search the Apple Podcasts directory by keyword and get a list of shows with the **owner / contact email each show publishes** in its own RSS feed or on its own website, where that email was found, the show's website, **episode count**, **last episode date**, publishing frequency and the newest episodes. Useful for sponsorship sales, PR and guest pitching, and agency prospecting.

- **The contact email from the show's own RSS feed** - the owner address (`itunes:owner`) or editor address (`managingEditor`) the show puts in its feed.
- **When the feed has no email, the show's website is checked** (home page and contact page) for an address written on the page, so a show without a feed email can still come back with one.
- **Every email says where it came from** - `rss: itunes:owner` or `website: https://.../contact`.
- **Shows you can't contact cost much less** than shows with an email, and you can switch them off entirely.
- **Active-show filter** - keep only podcasts that published in the last 90 days (or any number of days you set).
- No login, no API key, no cookies.

These addresses can be personal data. How you use them, including following CAN-SPAM, GDPR and other email and privacy rules, is your responsibility: reach out about the show and honor opt-outs.

### What you get on real data

Measured on 2026-10-21 across 15 popular keywords (marketing, business, true crime, health, comedy, technology, sports, news, history, education, fitness, finance, parenting, music, travel), 20 shows each:

| | Shows |
|---|---|
| Shows found (duplicates removed) | 294 |
| RSS feed read | 283 |
| Row with a contact email | 238 (81% of shows found, 84% of feeds read) |
| ...taken from the feed (itunes:owner or managingEditor) | 223 |
| ...taken from the show's own website only | 15 |
| Feed links the show's website | 274 |

Some feeds carry an address on a hosting platform's domain (a forwarding address, or the platform's own feed address) instead of the show's own; 18 shows had only such an address. Those are listed separately in `relayEmails` and are **not counted as a contact**, so a "with contact" row carries an address the show published itself.

### One row per podcast

```json
{
  "status": "ok",
  "podcastId": 1234567890,
  "title": "Example Marketing Show",
  "author": "Example Media",
  "hasContact": true,
  "contactEmail": "hello@example-show.com",
  "contactEmailSource": "rss: itunes:owner",
  "ownerEmail": "hello@example-show.com",
  "emails": [{ "email": "hello@example-show.com", "source": "rss: itunes:owner" }],
  "relayEmails": [],
  "ownerName": "Example Media",
  "hasWebsite": true,
  "websiteUrl": "https://example-show.com/",
  "websitePagesChecked": 0,
  "episodeCount": 412,
  "appleEpisodeCount": 412,
  "lastEpisodeDate": "2026-10-18T10:00:00.000Z",
  "appleLatestEpisodeDate": "2026-10-18T10:00:00.000Z",
  "daysSinceLastEpisode": 3,
  "isActive": true,
  "medianDaysBetweenEpisodes": 7,
  "episodeFrequency": "weekly",
  "firstEpisodeDate": "2018-03-02T09:00:00.000Z",
  "feedLastBuildDate": "2026-10-18T10:05:00.000Z",
  "language": "en-us",
  "categories": ["Business", "Marketing"],
  "primaryGenre": "Marketing",
  "genres": ["Marketing", "Business"],
  "explicit": false,
  "showType": "episodic",
  "description": "Weekly interviews with ...",
  "copyright": "2026 Example Media",
  "feedUrl": "https://feeds.example.com/show.xml",
  "feedComplete": true,
  "applePodcastsUrl": "https://podcasts.apple.com/us/podcast/example-marketing-show/id1234567890",
  "artworkUrl": "https://example-show.com/cover.jpg",
  "storeCountry": "USA",
  "searchTerms": ["marketing"],
  "feedMovedTo": null,
  "recentEpisodes": [
    { "title": "Episode 412", "publishedAt": "2026-10-18T10:00:00.000Z", "durationSeconds": 2710, "episodeUrl": "https://example-show.com/412", "audioUrl": "https://cdn.example.com/412.mp3" }
  ],
  "checkedAt": "2026-10-21T08:00:00.000Z"
}
```

Sort by `isActive` and `daysSinceLastEpisode` to reach shows that are still publishing, and filter `hasContact = true` for your outreach list.

### Input

| Field | What it does |
|---|---|
| `searchTerms` | Keywords, one per line. Shows found by several terms are returned once, with every matching term in `searchTerms`. |
| `maxResults` | Shows per search term, 1-200 (default 50). |
| `country` | Directory country, two letters (default `us`). |
| `feedUrls` | Podcast RSS feeds you already have (http:// or https://) - enrich your own list the same way. |
| `onlyWithContact` | Return only shows that publish a contact email. Others are not saved and not charged. |
| `activeOnly` / `activeWithinDays` | Return only shows with a recent episode (90 days, or the days you set). |
| `crawlWebsite`, `maxPagesPerSite`, `respectRobots` | Website lookup when the feed has no email (on by default, up to 3 pages, robots rules respected). |
| `maxEpisodesPerShow` | Newest episodes listed per row (default 5, 0 for none). |
| `timeoutSecs` | Seconds to wait for one feed or web page (default 20, up to 60). Each show also has an overall limit of about 60 seconds. A feed that times out returns a free row with the reason. |

Example:

```json
{
  "searchTerms": ["marketing", "true crime"],
  "maxResults": 20,
  "country": "us",
  "onlyWithContact": false
}
```

### Pricing

Pay per result, two kinds of row:

- **Podcast with a contact email** (`podcast-with-contact`) - the main result.
- **Podcast without a contact email** (`podcast-without-contact`) - directory data, episode stats and website, at a much lower price. Turn on `onlyWithContact` and these are never charged.

Rows that explain why a show could not be read or is not returned (no feed, the feed refused the request, the feed asks directories not to list the show, a search returned nothing) are **not charged**. Set a maximum cost per run and the Actor stops cleanly at that amount, saying so in the run status.

### Good to know

- Contacts are what each show publishes in its own feed or writes on its own website. Pages of hosting and listening platforms are not crawled, because the addresses there belong to the platform, not the show.
- Addresses a website hides on purpose (Cloudflare email protection, "name \[at] site \[dot] com") are not decoded.
- Shows whose feed is marked `<itunes:block>yes</itunes:block>` (the owner asked directories not to list it) are not returned; you get a free row with the reason.
- The directory returns up to about 200 shows per search term and country. For more shows, add more specific terms (for example "saas marketing", "b2b marketing", "marketing for coaches") or run other countries.
- A few feeds are larger than 15 MB. For those, the contact, website and show details are still returned, `feedComplete` is `false`, and the feed-based episode count and dates are left empty; `appleEpisodeCount` and `appleLatestEpisodeDate` still give you the directory's figures, and `daysSinceLastEpisode` / `isActive` are worked out from the directory's latest episode date.

### 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

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

Keywords to search the Apple Podcasts directory with, one per line (for example: marketing, true crime, real estate investing). Each term returns up to the number of shows set below. Shows found by more than one term are returned once.

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

How many shows to take from the directory for each search term (1-200).

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

Two-letter country code of the Apple Podcasts directory to search (us, gb, de, jp, au, ...). Results and ranking differ by country.

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

Podcast RSS feed addresses you already have. Each one is read the same way as a search result, so you can enrich your own list with the contact emails the shows publish and episode data. Only http:// and https:// addresses are read. Can be used with or without search terms.

## `onlyWithContact` (type: `boolean`):

Return only shows that publish a contact email in their feed or on their website. Shows without one are not saved and not charged.

## `activeOnly` (type: `boolean`):

Return only shows that published an episode in the last 90 days. Skipped shows are not saved and not charged.

## `activeWithinDays` (type: `integer`):

Return only shows whose latest episode is at most this many days old. 0 means no limit. When set, it is used instead of the 90 days of 'Active podcasts only'.

## `crawlWebsite` (type: `boolean`):

When the RSS feed lists no email but links the show's own website, open that site and its contact page to look for an address. Hosting platform pages are not crawled, and robots rules are respected.

## `maxPagesPerSite` (type: `integer`):

How many pages of the show's website to open before giving up. Stops as soon as an address is found.

## `respectRobots` (type: `boolean`):

Skip website pages that the site's robots.txt asks crawlers not to open.

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

How many of the newest episodes to list in each row (title, date, duration, link, audio URL). 0 lists none; the episode count and dates are still returned.

## `timeoutSecs` (type: `integer`):

How long to wait for one feed or web page before giving up on it (up to 60). Each show also has an overall limit of about 60 seconds for its feed and website together. A feed that times out returns a free row with the reason.

## Actor input object example

```json
{
  "searchTerms": [
    "marketing",
    "true crime"
  ],
  "maxResults": 10,
  "country": "us",
  "onlyWithContact": false,
  "activeOnly": false,
  "activeWithinDays": 0,
  "crawlWebsite": true,
  "maxPagesPerSite": 3,
  "respectRobots": true,
  "maxEpisodesPerShow": 5,
  "timeoutSecs": 20
}
```

# Actor output Schema

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

One row per podcast: the owner / contact email the show publishes and where it was found, website, episode count, last episode date, frequency and recent episodes. A show that could not be read returns the reason instead.

# 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 = {
    "searchTerms": [
        "marketing",
        "true crime"
    ],
    "maxResults": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("neverempty/podcast-directory-contacts").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 = {
    "searchTerms": [
        "marketing",
        "true crime",
    ],
    "maxResults": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("neverempty/podcast-directory-contacts").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 '{
  "searchTerms": [
    "marketing",
    "true crime"
  ],
  "maxResults": 10
}' |
apify call neverempty/podcast-directory-contacts --silent --output-dataset

```

## MCP server setup

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

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/Mb8SnXufFBEjZoX9J/builds/NmQeJI4sqZaIUFVaR/openapi.json
