Listen Notes API & Scraper
The Listen Notes API returns podcast and episode data from the largest podcast search engine as clean JSON.
🤖 Using an AI assistant? Copy this link into ChatGPT / Claude / Cursor — it reads every endpoint and parameter instantly and tells you if this API fits your use case.
The primary search endpoint returns matching podcasts with id, title, publisher, image and claim/explicit flags, and you can drill into a podcast, a single episode, the best_podcasts per genre and the full genre taxonomy. It is built for podcast apps, media-monitoring and research tools that need structured podcast discovery data without scraping or a rate-limited official quota. One ReefAPI key, one shared credit pool, the standard envelope.
The three id shapes, and the fields that are not the type you expect
Listen Notes hands out three different identifiers and they are not interchangeable, which is the usual reason a lookup 404s. Two more fields read as numbers and are not. Everything below was measured on live calls: The Daily (xp7nhsmSkX2, 2,961 episodes), The Startup Solution (PD732XsA50q), the full genres taxonomy and the US and Swedish best_podcasts lists.
| Field | Measured shape | Where it comes from |
|---|---|---|
| podcast id | 11 characters, mixed case, with - and _ in the alphabet: xp7nhsmSkX2, PD732XsA50q, 1dILNU8oAB- | every search, best_podcasts and genre row; this is what the podcast action takes |
| podcast uuid | 32 lowercase hex, a separate field on the same object | not the id, and not accepted as one |
| episode id | 32 lowercase hex: 27e6e7b37155455bb76ba3a98e66c073 | latest_episodes[].episode_id inside a podcast response; feed it to the episode action |
| published (episode) | ISO-8601 with an offset: "2026-08-26T02:45:00-07:00" | not a unix millisecond timestamp. The offset is Listen Notes' own, so normalize to UTC before sorting across shows. |
| audio_length / audio_length_sec | "00:50:06" and 3006 | the episode action. The same pair is named duration / duration_sec inside a podcast's latest_episodes, so the field names change between actions. |
| genre_ids | integers from the 159-entry taxonomy, ids running 68 to 266 and not contiguous | 93 Business, 127 Technology, 133 Comedy, 134 Music, 135 True Crime, 68 TV & Film, 144 Personal Finance. Call genres for the full list. |
| listen_score | 0-100 integer (measured 46, 52, 54, 60), or null | best_podcasts and genre rows. Null across the whole Swedish list, populated across the US list. |
| global_rank | a STRING band, not a number: "TOP 0.1%", "TOP 0.5%", "TOP 1%" | same rows as listen_score, and null wherever listen_score is null |
| rating / rating_count | float out of 5 plus a count (The Daily: 4.3 over 121,517 ratings) | podcast action only; search rows carry neither |
| is_claimed / explicit | booleans on search rows (measured: claimed on 1 of 10, explicit on 1 of 10) | explicit comes back null rather than false on the podcast detail object |
| rss / itunes_url | null on both podcasts measured, with rss_login_required true alongside | podcast action. Treat the feed URL as usually absent rather than usually present. |
Page sizes are small and uneven: search returned 10 podcasts, best_podcasts returned 9 rows for se and 8 for us with has_more true, and genre 93 returned 7. Page until the list comes back empty rather than assuming a fixed page size.
Real request and response JSON
Captured from the indexed primary action, search, on .
{
"method": "POST",
"url": "https://api.reefapi.com/listennotes/v1/search",
"headers": {
"x-api-key": "$REEF_KEY",
"content-type": "application/json"
},
"body": {
"query": "startup"
}
}{
"ok": true,
"meta": {
"api": "listennotes",
"endpoint": "search",
"mode": "live",
"latency_ms": 772.9,
"record_count": 10,
"bytes": 6966,
"cache_hit": false,
"stop_reason": "complete",
"query": "startup",
"charged_credits": 1,
"version": "1.0.0"
},
"data": {
"podcasts": [
{
"id": "xCa-DyApFwT",
"uuid": "24a0721faf7d488990e952c1f76514bc",
"title": "Startup Insider",
"publisher": "Startup Insider",
"image": "https://cdn-images-3.listennotes.com/podcasts/startup-insider-startup-insider-WjH90A2XZ-x-xCa-DyApFwT.1400x1400.jpg",
"thumbnail": "https://cdn-images-3.listennotes.com/podcasts/startup-insider-startup-insider-k1LVGIFC4Wd-xCa-DyApFwT.300x300.jpg",
"url": "https://www.listennotes.com/podcasts/startup-insider-startup-insider-xCa-DyApFwT/",
"is_claimed": false,
"explicit": false
},
{
"id": "5KtRMmeD_L8",
"uuid": "ed450f75b8704987a255533f7db15b95",
"title": "Startup Hustle",
"publisher": "Startup Hustle",
"image": "https://cdn-images-3.listennotes.com/podcasts/startup-hustle-startup-hustle-2GLaIdKm7NZ-5KtRMmeD_L8.1400x1400.jpg",
"thumbnail": "https://cdn-images-3.listennotes.com/podcasts/startup-hustle-startup-hustle-4ZKd39cbDw8-5KtRMmeD_L8.300x300.jpg",
"url": "https://www.listennotes.com/podcasts/startup-hustle-startup-hustle-5KtRMmeD_L8/",
"is_claimed": false,
"explicit": true
},
{
"id": "LS4ysR8UBpr",
"uuid": "7f5866390a3a45f9ae78ffe7c65d7d96",
"title": "The Sweaty Startup",
"publisher": "Nick Huber",
"image": "https://cdn-images-3.listennotes.com/podcasts/the-sweaty-startup-nick-huber-7PerRKQ3mbA-LS4ysR8UBpr.1400x1400.jpg",
"thumbnail": "https://cdn-images-3.listennotes.com/podcasts/the-sweaty-startup-nick-huber-x4JYfjUC8oR-LS4ysR8UBpr.300x300.jpg",
"url": "https://www.listennotes.com/podcasts/the-sweaty-startup-nick-huber-LS4ysR8UBpr/",
"is_claimed": false,
"explicit": false
}
]
}
}What the Listen Notes API does
| Action | Description | Concrete use case | Key params |
|---|---|---|---|
| search | Search Listen Notes for podcasts by keyword. Returns up to 10 matching podcasts with title, publisher, cover image and the podcast id (feed into `podcast` for full details). | Content platforms call search to search Listen Notes for podcasts by keyword. | query |
| podcast | Full detail for one podcast by id (from search) or a Listen Notes podcast URL: title, publisher, description, cover art, language, country, genres, hosts, rating, total episode count, social links and its latest episodes. | Research tools call podcast to get full detail for one podcast by id (from search) or a Listen Notes podcast URL. | id, url |
| episode | Full detail for one episode by id or a Listen Notes episode URL: title, description, audio URL, duration, publish date, cover art and the parent podcast. | Community analysts call episode to get full detail for one episode by id or a Listen Notes episode URL. | id, url |
| best_podcasts | The curated 'best podcasts' list for a country, paginated. Each entry has the podcast title, publisher, cover image, id, Listen Score and global rank. | Media monitors call best_podcasts to get the curated 'best podcasts' list for a country, paginated. | region, page |
| genre | Browse the best podcasts in a genre, paginated. Use the `genres` action to get the genre id → name list. Each entry has title, publisher, image, id, Listen Score and global rank. | Content platforms call genre to get browse the best podcasts in a genre, paginated. | genre_id, page |
| genres | The complete Listen Notes genre taxonomy — every genre id mapped to its name. These ids are the input vocabulary for the `genre` action. | Research tools call genres to get the complete Listen Notes genre taxonomy. | none |
Call search from your stack
curl -X POST https://api.reefapi.com/listennotes/v1/search \
-H "x-api-key: $REEF_KEY" \
-H "content-type: application/json" \
-d '{"query":"startup"}'import requests
r = requests.post(
"https://api.reefapi.com/listennotes/v1/search",
headers={"x-api-key": REEF_KEY},
json={
"query": "startup"
},
)
print(r.json()["data"])const res = await fetch("https://api.reefapi.com/listennotes/v1/search", {
method: "POST",
headers: {
"x-api-key": process.env.REEF_KEY,
"content-type": "application/json",
},
body: JSON.stringify({
"query": "startup"
}),
});
const { ok, data, meta, error } = await res.json();Ask your MCP-connected assistant: call reefapi.listennotes.search with {"query":"startup"}.Who uses this API and why
- Podcast apps call search to power in-app discovery across the full Listen Notes index.
- Media-monitoring tools search episodes to find every show mentioning a brand or topic.
- Researchers use best_podcasts and genre to map the top shows in any category.
Questions developers ask before integrating
Is published a unix timestamp in milliseconds?
No. A live episode lookup returned published as the string "2026-08-26T02:45:00-07:00", ISO-8601 with a UTC offset. A second episode on an unrelated show returned "2025-06-26T00:02:00-07:00", same shape. Parse it as a datetime with offset; do not divide it by 1000 or hand it to a unix-epoch constructor.
Why is global_rank a string like "TOP 0.5%" instead of a rank number?
Because that is what Listen Notes publishes. It is a percentile band, not an ordinal position, so there is no rank 1, 2, 3 to give you. Sort by listen_score, a 0-100 integer, when you need a numeric ordering, and expect ties inside a band: three of five measured US rows shared "TOP 0.5%" at listen scores of 52, 52 and 54.
listen_score and global_rank are null on my results. Is the field broken?
No. Listen Notes only assigns a Listen Score to part of its index, and non-US lists are thin. Measured: every row of best_podcasts for region us carried both fields, and every row for region se carried null for both. Null means unscored, not zero, so drop those rows from a ranking rather than treating them as the worst entries.
I have a podcast uuid. Can I pass it to the podcast action?
No. Three ids coexist and each action takes exactly one of them. The podcast action takes the 11-character id (xp7nhsmSkX2); the 32-hex uuid on the same object is a different identifier and is not a lookup key. The episode action takes a 32-hex episode id, which looks like a uuid but belongs to an episode, not a show. When in doubt, pass the full listennotes.com URL instead, which both actions accept.
What happens if I pass a genre id that does not exist?
You get a clean NOT_FOUND rather than an empty list. genre_id 999999 returned error code NOT_FOUND naming the 404 URL it tried. That distinction matters, because an empty 200 would look like "this genre has no podcasts", which is a different claim. Call genres first: it returned 159 entries with ids from 68 to 266, and the ids are not contiguous, so you cannot iterate a range.
Why do episode descriptions start with a timecode?
Listen Notes prefixes the episode description with the runtime, so the text arrives as "00:50:06 - This summer, the Democratic Socialists...". It is upstream formatting rather than a parsing artifact, and the same prefix appeared on episodes from two unrelated shows. Strip the leading "HH:MM:SS - " before you index the text or feed it to a model; the same number is already there as audio_length_sec.
Why is rss null when the podcast obviously has a feed?
Because Listen Notes puts the raw RSS URL behind its own login for most shows, and the response says so: rss was null on both podcasts measured, with rss_login_required true in the same object, and itunes_url was null alongside it. What you do get is extra_urls, which carried working Spotify, Amazon and Google links plus Twitter and Facebook handles for The Daily.
How many episodes does the podcast action return?
Ten, regardless of how many the show has. The Daily reported total_episodes 2961 and returned 10 rows in latest_episodes; The Startup Solution reported 41 and also returned 10. Those rows carry episode_id, title, audio, duration and duration_sec, which is enough to hand each one to the episode action for the full record including description and publish date.
What is the Listen Notes API?
Listen Notes API is a ReefAPI endpoint group for listen notes It returns live JSON through POST requests under /listennotes/v1.
Is the Listen Notes API free to try?
Yes. ReefAPI starts with 1,000 free credits, no card required. Listen Notes calls use the same shared credit balance as every other ReefAPI engine.
Do I need a Listen Notes login or account?
No login to Listen Notes is needed for the API response. You call ReefAPI with your x-api-key header, and the playground can run live examples before you create a production key.
How fresh is the Listen Notes data?
The page example is captured from a live search call, and production requests fetch live data through ReefAPI rather than a static sample.
How many credits does the Listen Notes API use?
Listen Notes actions currently cost 1 credit per successful call. Failed or blocked calls are free. All APIs draw from one credit pool.
Can I call Listen Notes from an AI assistant or MCP client?
Yes. Connect ReefAPI once through MCP and your assistant can call listennotes actions with the same key, credit pool and JSON envelope used by normal REST requests.