Proxies for Podcast Analytics and Media Data
How proxies for podcast analytics collect RSS feeds, Podcasting 2.0 tags and per-country chart rankings, and the download numbers you honestly cannot scrape.
Proxies for podcast analytics exist because podcast data is split down the middle: the RSS layer is genuinely open and machine-readable, while charts, rankings and availability sit inside per-country Apple and Spotify storefronts that return a different answer depending on where your request comes from.
That split decides your whole architecture. Feeds you can parse with an XML library and a polite crawler. Charts you have to collect roughly 175 times, once per storefront, because a show sitting at #4 in Ireland may not chart at all in the United States. And the number everyone actually wants, downloads, is not on the public internet in any form.
Key Takeaways
- Podcast RSS is an open, documented format. The Podcasting 2.0 namespace adds transcripts, chapters, funding links and a stable
podcast:guidthat survives feed URL changes.- Chart position is storefront-specific. You must collect per country, and Apple exposes a clean public JSON endpoint per storefront that most teams never find.
- Episode and catalogue change detection runs on conditional HTTP requests plus GUID set comparison, not on refetching millions of feeds every hour.
- Real download and listener counts live with the hosting provider and the platform. They cannot be scraped. Anything sold as a "download estimate" is an inference from public popularity signals.
What Podcast Data Actually Exists in Public
Sort the field into three tiers before writing any code, because each tier needs a different collection method and carries a different confidence level.
| Tier | Examples | Where it lives | Collection method |
|---|---|---|---|
| Open and authoritative | Titles, descriptions, episode list, publish dates, durations, categories, transcript links, funding links | The show's own RSS feed, served over plain HTTP | Fetch and parse XML |
| Public but platform-specific | Chart rank, category rank, storefront availability, artwork, ratings and review counts | Apple Podcasts and Spotify surfaces, one result set per country | Per-storefront collection, geo-aware |
| Private | Downloads, unique listeners, completion rate, drop-off curve, demographics | The hosting provider's servers and each platform's creator dashboard | Not available. Full stop. |
Tier one is the reason podcasting stayed an open ecosystem while video consolidated. A podcast is, structurally, an RSS 2.0 document with an element pointing at an audio file. The RSS 2.0 specification has not meaningfully changed since 2003, and Apple's podcast feed requirements layer an itunes: namespace on top of it rather than replacing it. Nobody needs an API key to read a feed. That is by design.
Tier two is where proxies earn their place. Tier three is where honest analytics products draw a line that their competitors often blur, and we will get to that line.
Feed Discovery: Finding the RSS Behind a Show
You almost never start with a feed URL. You start with a show name, an Apple collection ID, or a Spotify URI, and you need to resolve it to the canonical RSS.
Route 1: the iTunes Lookup API
Apple runs a free, unauthenticated JSON endpoint that returns feedUrl for any podcast collection ID. It is documented in Apple's iTunes Search API reference.
curl -s "https://itunes.apple.com/lookup?id=1200361736&entity=podcast"
The response carries collectionName, artistName, genres, trackCount and, critically, feedUrl. Two things bite people here. Apple documents a soft limit of roughly 20 calls per minute per IP, and the results are storefront-sensitive: add &country=DE and a show unavailable in Germany returns resultCount: 0. That second behaviour is useful, since it doubles as an availability probe.
Route 2: the Podcast Index API
The open Podcast Index API indexes several million feeds and supports lookup by Apple ID, feed URL, GUID or search term. Auth is an odd but simple scheme: a SHA-1 of your API key, secret and current Unix timestamp in the Authorization header.
import hashlib, time, requests
KEY, SECRET = "your-key", "your-secret"
now = str(int(time.time()))
auth = hashlib.sha1((KEY + SECRET + now).encode()).hexdigest()
r = requests.get(
"https://api.podcastindex.org/api/1.0/podcasts/byitunesid",
params={"id": 1200361736},
headers={
"User-Agent": "SparkProxyResearch/1.0",
"X-Auth-Key": KEY,
"X-Auth-Date": now,
"Authorization": auth,
},
timeout=30,
)
feed = r.json()["feed"]
print(feed["url"], feed["podcastGuid"])
Route 3: the show's own website
Well-built podcast sites still advertise the feed in the document head:
<link rel="alternate" type="application/rss+xml" title="Feed" href="https://feeds.sparkproxy.io/show.xml">
Worth checking, because it sometimes reveals the raw host feed behind a tracking-prefixed URL that Apple displays instead.
Spotify is the awkward one. An open.spotify.com/show/... URI does not resolve to an RSS feed through any public API, because Spotify deliberately does not expose the underlying feed. Matching a Spotify show to its RSS means fuzzy matching on title plus publisher plus the first episode's publish date, and you should carry a confidence score on that join rather than pretending it is exact.
Scraping at scale? Skip the blocks.
Fast, unblockable datacentre proxies with unlimited bandwidth.
The Podcasting 2.0 Namespace
This is the part most media analytics stacks skip, and it is free structured data sitting in feeds they already fetch.
The Podcast Namespace is a community-maintained extension, declared as xmlns:podcast="https://podcastindex.org/namespace/1.0". Adoption is partial but rising, and the tags that actually appear in production feeds are worth handling:
| Tag | Level | What you get |
|---|---|---|
| `podcast:guid` | Channel | A stable show identifier that survives a feed URL change |
| `podcast:transcript` | Item | URL plus MIME type for a VTT, SRT or JSON transcript |
| `podcast:chapters` | Item | JSON chapter file with segment titles and timestamps |
| `podcast:person` | Both | Named hosts and guests with roles, the closest thing to a guest graph |
| `podcast:funding` | Channel | Where the show monetises, a direct business-model signal |
| `podcast:location` | Both | Geographic subject of the show or episode |
| `podcast:locked` | Channel | Whether the owner has blocked automated feed migration |
| `podcast:medium` | Channel | `podcast`, `music`, `audiobook`, `newsletter`, distinguishes format |
podcast:guid deserves special attention. The spec defines it as a UUID version 5 generated over the feed URL with the protocol scheme and any trailing slashes stripped, using the fixed namespace UUID ead4c236-bf58-58c6-a2c6-a6b28d128cb6. That means you can compute the expected GUID for any feed yourself and use it as a join key, even for feeds that do not publish the tag:
import uuid
PODCAST_NS = uuid.UUID("ead4c236-bf58-58c6-a2c6-a6b28d128cb6")
def podcast_guid(feed_url: str) -> str:
stripped = feed_url.split("://", 1)[-1].rstrip("/")
return str(uuid.uuid5(PODCAST_NS, stripped))
print(podcast_guid("https://feeds.sparkproxy.io/show.xml"))
For a media analytics product, that one function solves the deduplication problem that otherwise haunts every podcast dataset: the same show reachable through four different tracking-prefixed URLs, counted four times.
podcast:person is the sleeper. If you track guest appearances across a media landscape, the way you would track byline networks in news and media monitoring, the tag hands you a name, a role and often a URL without any named entity recognition at all.
Why Chart Position Must Be Collected Per Country
Here is the fact that reshapes the pipeline: there is no such thing as "the podcast chart". There are roughly 175 Apple storefronts, each with its own Top Shows list and its own per-category lists, and they diverge wildly.
A show with a concentrated audience in a small market routinely charts top ten there and nowhere else. A US-heavy show may be invisible in every other storefront. Ranking is also relative to a storefront's total listening volume, so #20 in the United States and #1 in Iceland are not comparable achievements, and any dashboard that averages ranks across countries into one number is producing a meaningless figure.
Apple does not publish the ranking formula. What it has said publicly is that the Top Shows chart reflects recent follower and listener activity rather than lifetime download totals, which is why a show can drop 40 places in a week without losing a single subscriber. Treat rank as a momentum signal, not a size signal.
Apple's public per-storefront JSON
Most teams scrape podcasts.apple.com HTML for this. You usually do not have to. This is the classic case for finding the hidden JSON endpoint behind a rendered page: Apple's Marketing Tools RSS service publishes chart data as clean JSON, keyed by storefront in the path.
# Top 100 shows in the US storefront
curl -s "https://rss.marketingtools.apple.com/api/v2/us/podcasts/top/100/podcasts.json"
# The same list for Germany
curl -s "https://rss.marketingtools.apple.com/api/v2/de/podcasts/top/100/podcasts.json"
Each entry carries the collection ID, name, artist and artwork URL. Pair that ID with the Lookup call from earlier and you have chart rank joined to a feed URL. Sweeping every storefront daily is a few hundred requests, which is exactly the volume where a single origin IP starts collecting 403s and where distributed egress stops being optional.
Spotify and the rest
Spotify's Web API has Get Show and episode endpoints, requires OAuth client credentials, and takes a market parameter that changes availability but does not return chart position. Spotify's charts live on a separate public web property, podcastcharts.byspotify.com, broken out by country and refreshed weekly rather than daily. Different cadence, different methodology, not comparable to Apple's numbers.
So your chart table needs a composite key of (platform, storefront, chart_type, category, captured_at). Anyone who models it as show_id -> rank has already lost the data.
Where the IP actually matters
Be precise about this, because vendors oversell it. The Apple Marketing Tools endpoint takes the storefront in the URL, so a German IP is not required to read the German chart. What geo-distributed IPs genuinely buy you:
- Rate limit headroom. 175 storefronts multiplied by dozens of categories, run daily, is tens of thousands of requests from one address.
- Availability checks. Whether a show is listed at all in a storefront, and what artwork or description a local listener sees, is served by IP-inferred surfaces on the web properties.
- Localised web pages.
podcasts.apple.comand Spotify's web player render country-specific copy, subscription pricing, and different recommendation modules. - Region-restricted catalogue. Rights-limited and explicit-content-filtered shows disappear entirely from certain storefronts, and that disappearance is only observable from inside.
Points 2 through 4 are the same problem covered in geo-targeting with proxies, and they are why a podcast analytics stack looks structurally like an app store data pipeline. If you have built App Store and Google Play collection before, the storefront fan-out pattern will feel familiar.
Episode and Catalogue Change Detection
Refetching millions of feeds on a fixed schedule is wasteful and gets you throttled. Change detection is the difference between a pipeline that costs a fortune and one that does not.
Conditional requests first
Podcast hosts are unusually good about HTTP caching headers, because they serve feeds to millions of podcast apps. Use them. RFC 9110 conditional requests define the contract:
import requests
def poll(feed_url, etag=None, last_modified=None):
headers = {"User-Agent": "SparkProxyPodcastBot/1.0 (+https://www.sparkproxy.io/bot)"}
if etag:
headers["If-None-Match"] = etag
if last_modified:
headers["If-Modified-Since"] = last_modified
r = requests.get(feed_url, headers=headers, timeout=45)
if r.status_code == 304:
return None, etag, last_modified # unchanged, a few hundred bytes spent
return r.text, r.headers.get("ETag"), r.headers.get("Last-Modified")
In a large sweep the majority of daily polls come back 304. That single header pair cuts bandwidth dramatically and drops your visible request weight enough that hosts stop noticing you.
Push instead of poll
Two mechanisms let you skip polling for participating hosts. WebSub is advertised in the feed as and gives you a callback on publish. Podping is podcast-specific: hosts write a lightweight notification when a feed updates, and consumers watch that stream instead of crawling. Neither covers the whole ecosystem, so they reduce your polling load rather than replacing it.
Diffing the catalogue
When a feed does change, compare GUID sets rather than diffing raw XML:
def diff_catalogue(previous_guids: set, current: list) -> dict:
current_guids = {e["guid"] for e in current}
return {
"new": sorted(current_guids - previous_guids),
"removed": sorted(previous_guids - current_guids),
"retained": len(current_guids & previous_guids),
}
The removed bucket is the interesting one, and it is the metric nobody tracks. Episodes vanish from feeds constantly: sponsorship expiry, legal takedowns, quiet retractions after a bad guest, or a host truncating the feed to the most recent 300 items. A silent unpublish is often a genuine news event, and you only catch it if you retain your own history of the catalogue.
Three more mutations to watch:
- Title and description edits on existing GUIDs, usually SEO tuning or a correction.
- Enclosure URL changes on an unchanged GUID, which normally means a re-upload with a different ad load rather than new content.
appearing at channel level, the standard signal that the show has migrated hosts. Follow it once and update your canonical URL, or your record goes stale within a month.
Rate limiting discipline applies throughout. The ethical scraping and rate limiting guide covers the general shape. For feeds specifically, cap concurrency per host domain rather than globally, since a handful of large hosting providers serve a very long tail of shows.
Collecting Podcast Data With the SparkProxy Scraping API
Raw XML feeds usually need nothing more than a rotating pool and a sane User-Agent. The chart and storefront surfaces are where a managed layer pays for itself, because they are JavaScript-rendered and country-sensitive. Parameters below come from the SparkProxy Scraping API docs.
A plain feed fetch through a rotating exit, no rendering needed:
curl -X GET "https://scrape.sparkproxy.io/api/v1?url=https://feeds.sparkproxy.io/show.xml&render_js=false" \
-H "X-API-Key: sk-xxxxxxxxxxxxxxxx"
Feed polling in Python, keeping the conditional-request logic intact:
import requests
API = "https://scrape.sparkproxy.io/api/v1"
API_KEY = "sk-xxxxxxxxxxxxxxxx"
def fetch_feed(feed_url, country=None):
params = {"url": feed_url, "render_js": "false", "format": "json"}
if country:
params["country_code"] = country # ISO alpha-2: US, GB, DE
r = requests.get(API, params=params, headers={"X-API-Key": API_KEY}, timeout=90)
data = r.json()
return data["status_code"], data["body"], data["credits_used"]
Pulling the Apple chart JSON for one storefront:
def apple_chart(storefront: str, limit: int = 100):
url = (f"https://rss.marketingtools.apple.com/api/v2/{storefront}"
f"/podcasts/top/{limit}/podcasts.json")
r = requests.get(API, params={"url": url, "format": "json"},
headers={"X-API-Key": API_KEY}, timeout=60)
return r.json()["body"]
Checking how a show's storefront page renders to a listener in Japan, which needs a real browser and a local exit:
curl -X GET "https://scrape.sparkproxy.io/api/v1?url=https://podcasts.apple.com/jp/podcast/id1200361736&country_code=JP&render_js=true&wait_for=3000&format=html" \
-H "X-API-Key: sk-xxxxxxxxxxxxxxxx"
For storefronts that block datacenter ranges, route through the residential tier and pin a session so a multi-step check stays on one IP:
params = {
"url": "https://podcastcharts.byspotify.com/",
"country_code": "BR",
"render_js": "true",
"premium_proxy": "true",
"session_id": "spotify-charts-br",
"wait_for": "4000",
"format": "html",
}
r = requests.get(API, params=params, headers={"X-API-Key": API_KEY}, timeout=120)
Capturing dated visual evidence of a chart position, useful when a client disputes what ranked where:
curl -X GET "https://scrape.sparkproxy.io/api/v1?url=https://podcasts.apple.com/ie/charts&country_code=IE&render_js=true&format=screenshot" \
-H "X-API-Key: sk-xxxxxxxxxxxxxxxx" \
--output chart_ie_2026-08-18.png
Cost note: render_js and premium_proxy both cost more per request. Feed polls are the bulk of your volume and need neither, so keep them on the cheap path and reserve the expensive path for chart and storefront sweeps. The response returns credits_used per call, which makes it easy to spot when someone has accidentally left rendering enabled on the feed crawler.
The Honest Limit: You Cannot Scrape Downloads
This is the section that should decide whether you trust a podcast analytics vendor.
A podcast download is counted server-side, at the moment a client requests bytes from the audio file. That request goes to the hosting provider's origin or CDN, and the count lives in their logs. There is no public endpoint, no rendered page, and no undocumented JSON that exposes it. The IAB Tech Lab Podcast Measurement Technical Guidelines define what counts as a download at all, including the byte threshold served and the filtering of known bots, and compliance is certified per hosting platform. Nothing in that process touches a surface a crawler can reach.
The same applies to platform-side listening. Apple Podcasts Connect and Spotify for Creators report plays, followers, completion and drop-off only to the verified owner of the show, behind authentication. Scraping those dashboards would require credentials you do not have, and using someone else's would be unauthorised access, not data collection.
So what can you legitimately say from public data?
| Public signal | What it genuinely indicates | What it does not indicate |
|---|---|---|
| Chart rank in a storefront | Recent momentum in follows and listens, relative to that market | Audience size, or comparability across countries |
| Chart persistence over weeks | Sustained engagement rather than a launch spike | Any absolute number |
| Publishing cadence and gaps | Production health, resourcing, likely hiatus | Listenership trend |
| Review and rating counts by storefront | Rough engaged-audience floor and geographic spread | Downloads. Review rates vary hugely by genre |
| Ad load, derived from episode duration drift | Monetisation intensity | Revenue |
| `podcast:funding` and sponsor mentions | Business model and active advertisers | Deal value |
Every serious estimate of podcast audience size is a model fitted on these signals against a small set of shows whose real numbers are known through a partnership or a public disclosure. That is legitimate work, and it is also inference. Label it as an estimate and give it a confidence interval. If a dataset presents a download figure for a show it has no relationship with, and no interval on the number, the number was modelled, whatever the marketing says.
Saying this plainly is not a weakness in your product. It is the thing that makes the rest of your numbers believable.
Audio, Transcripts and Rights
The boundary here is clean, and staying inside it costs you nothing analytically.
Do not download the audio. The URL is public, but the audio file is a copyrighted work, and pulling it at scale also inflates the publisher's download counts and their bandwidth bill. You would be corrupting the exact metric everyone else depends on. Read the metadata, skip the media.
Transcripts carry their own rights questions. A podcast:transcript tag links a file the publisher chose to make available, typically WebVTT under the W3C WebVTT specification or SRT. Published deliberately for accessibility is not the same as licensed for redistribution. The transcript is a derivative of a copyrighted work, so republishing it wholesale, or training on a corpus of them, is a licensing decision rather than a technical one. Reading a transcript to extract topics, entities or sponsor mentions for analysis is a very different act from serving that transcript as your own content.
Generating your own transcripts is worse, not better. Running speech-to-text over the audio means you first made a copy of the audio. The output is still derived from the original work.
Respect the mechanical signals. Check robots.txt on the host serving the feed, honour podcast:locked when the owner has blocked automated migration, and send a User-Agent with a contact URL so anyone who wants to talk to you can. The same courtesy that applies to social media monitoring applies here, with the added point that podcast publishers are individually much smaller and much more likely to notice you.
None of this stops you building a strong product. Feed metadata, Podcasting 2.0 tags, per-storefront chart history and catalogue change detection give you a picture of the medium that no single platform dashboard offers. The constraint is only on audio, and on claiming private numbers you do not have.
Frequently asked questions
Frequently Asked Questions
For a few hundred feeds, no. Once you are polling thousands of feeds concentrated on a handful of large hosting providers, per-IP throttling and CDN bot rules make a rotating pool necessary, and conditional requests matter more than raw IP count. Chart collection is the part that genuinely needs distributed IPs.
Each Apple storefront and each Spotify country chart is computed only from activity in that market. A show with a concentrated regional audience can rank top ten in one storefront and be absent everywhere else, so podcast chart rankings by country are separate datasets, not variations of one list.
No. Downloads are counted server-side by the hosting provider under the IAB Tech Lab measurement guidelines, and listener data sits behind owner authentication in Apple Podcasts Connect and Spotify for Creators. Any public download figure is a model fitted on chart, review and cadence signals.
It is a community-maintained XML extension adding transcripts, chapters, funding links, named people and a stable podcast:guid. Adoption is partial, but it sits in feeds you fetch anyway, so parsing it costs nothing and podcast:guid alone fixes cross-platform deduplication.
Poll with If-None-Match and If-Modified-Since so unchanged feeds return a 304, subscribe to WebSub hubs or Podping where hosts support them, and on any change compare episode GUID sets to find additions and removals rather than diffing the XML.
Reading public RSS metadata and public chart pages is ordinary collection of published information. Downloading the copyrighted audio, redistributing transcripts, or accessing an owner's private analytics dashboard are separate acts with separate legal exposure, so keep your pipeline to metadata.
Get 20% off your first month
Premium datacentre proxies with unlimited bandwidth. Use the code at checkout.
Save up to 15% more on quarterly, half-yearly and yearly plans
Related articles

Best Proxies for AI Agents and Autonomous Browsing
Which proxies for AI agents to buy: datacenter, ISP or residential, how to size threads for browser agents, and what a runaway agent loop costs you.

Using Proxies for Distributed Load Testing
Proxies for distributed load testing: why single-IP runs mislead, how to size concurrency against plan threads, and k6, JMeter, Locust and Gatling setup.

Proxies for Web3 Data and NFT Marketplace Feeds
Proxies for Web3 data: where they fix IPFS gateway and marketplace throttling, where an API key makes them useless, and how to collect NFT floor and trait data.
