developer api
Everything the site does, from a script.
Search podcasts, list episodes, fetch transcripts as text, JSON, SRT or VTT with segment and word-level timestamps, queue new episodes for transcription, and ask AI Search to find a topic, name or keyword in any episode. One key, one credit balance, shared with the website.
Authentication
Accounts need a verified email, there's no way to get a spendable key without proving
control of a real inbox. Start at POST /api/v1/register/start, click the link
it emails you (or hit POST /api/v1/register/confirm with the token from that
link if you'd rather stay in a terminal), then pass the returned key as a bearer token.
curl -X POST https://podmenti.com/api/v1/register/start \
-H "Content-Type: application/json" \
-d '{"email": "you@example.com"}'
# check your email, then either click the link or:
curl -X POST https://podmenti.com/api/v1/register/confirm \
-H "Content-Type: application/json" \
-d '{"token": "<from the emailed link>"}'
# every call after that:
curl https://podmenti.com/api/v1/me \
-H "Authorization: Bearer pm_YOUR_KEY" Finding a show and its ids
Two ids run everything. A feed_id is the show's number in the open Podcast
Index, the directory of every public podcast. A transcript_id is the
episode's key: sixteen hex characters, sha1(episode guid) cut to sixteen,
identical for everyone, never changing. You never have to guess either: every discovery
call returns the ids and the URLs to call next, and none of them cost credits on any plan.
- Start from a name:
GET /api/v1/search?q=hubermanreturns shows withfeed_id,apple_idand anepisodes_url. - Start from a link: the same
?q=accepts an Apple Podcasts URL or an RSS feed URL. Or useGET /api/v1/shows/lookup?apple_id=…/?feed_url=…. Spotify links can't be resolved, Spotify hides the feed. - Start from what's popular:
GET /api/v1/charts?genre=comedy(Apple's daily charts, withapple_idand alookup_url) orGET /api/v1/trending?category=Technology(what's publishing right now across the index). - Then list episodes:
GET /api/v1/shows/{feed_id}/episodesgives every episode with itstranscript_id,transcript_status(ready, processing, none), audio URL, description, and the exact URLs to read or request it. - Check before you spend:
GET /api/v1/episodes/{transcript_id}tells you whether we hold it, its length, and whether your key already unlocked it (then it costs 0).
All of this is served from our own copy of the index, refreshed daily for shows and hourly for episode lists, so browsing is fast, never throttled by the index, and a bad minute there never becomes an error for you.
Endpoints
Two meters, and the last column says which one each call touches. Every call made
with a key counts as one request against your plan's daily allowance (Free 1,000, Plus 5,000, Pro 20,000, Business 50,000 a
day; X-RateLimit-Limit and X-RateLimit-Remaining on every
response, 429 when spent, reset at midnight UTC). Credits are charged on top
only where a price is shown, and only for work that costs us compute.
| endpoint | what it does | meter |
|---|---|---|
POST /api/v1/register/start | {"email"}: emails a one-time verification link. Same address
twice re-sends your existing key instead of creating a duplicate account. Creating
an account accepts the terms and privacy policy.
| no key |
POST /api/v1/register/confirm | {"token"}: the token from the emailed link. Free, includes
10 credits on first confirmation.
| no key |
GET /api/v1/me | Balance, plan and everything you've unlocked. | 1 request |
GET /api/v1/usage | The fuller picture behind your account: this cycle's allowance and what's left of it, requests used today, the credit ledger and recent unlocks. | 1 request |
GET /api/v1/search?q= | Find shows by name, or resolve a pasted Apple Podcasts or RSS link. Returns feed_id, apple_id, categories, language and the URLs to call next. Free. | 1 request |
GET /api/v1/shows/lookup | ?url=, ?apple_id= or ?feed_url= to a feed_id. Free. | 1 request |
GET /api/v1/shows/{feed_id} | Show details: description, artwork, language, categories, feed and website URLs, Apple id, newest episode date. Free. | 1 request |
GET /api/v1/charts?genre= | Apple's US top chart, overall (100) or per genre (50), updated daily. Free. | 1 request |
GET /api/v1/trending?category= | Shows publishing right now across the whole index, optionally within a category; also lists the categories. Free. | 1 request |
GET /api/v1/shows/{feed_id}/episodes?max=100 |
Episodes with transcript_id and status: ready,
processing or none. Up to 1000, which covers a show's full
history except a handful of the most prolific, where PodcastIndex itself stops
tracking further back (response includes truncated: true when that's
the case).
| 1 request |
GET /api/v1/episodes/{transcript_id} | One episode by transcript id: title, show, date, length, word count, whether a transcript exists, and whether your key already unlocked it. Free. | 1 request |
GET /api/v1/transcripts/{transcript_id} |
The transcript, including the original audio_url, for an unlocked
episode: ?format=text, json,
json×tamps=1&words=1, srt,
vtt and timestamped txt. 402 until unlocked.
| 1 request |
POST /api/v1/unlock | {"transcript": "<id>"}: 1 credit, once per
episode of any length, then every format is yours forever.
| 1 credit+ 1 request |
POST /api/v1/requests |
Transcribe episodes we don't have yet: one credit per hour of audio each (nearest
hour, never less than one; pass secs from the episodes endpoint), up to
25 at a time, unlock included. Returns a job with an ETA and poll URL;
pass webhook_url to be called when it lands. Add "fast": true
for the fast lane: the same episodes on a rented GPU, ready in minutes whatever their
length, at 2 credits per hour, promised within promise_seconds (at least 5 minutes,
longer for big batches) or the extra credits are refunded automatically. "dry_run": true quotes either lane without spending. Episodes already in the library are unlocked on the spot for
1 each (nothing if this key had them already) and never re-queued, so the
response splits into already_transcribed (instant) and
queued, alongside credits_charged and
credits_remaining. Emails you when the queued ones land.
| 1 credit / h2 / h fast lane, 1 per library episode, + 1 request |
GET /api/v1/jobs/{id} | Poll a transcription job: status, ETA, suggested poll interval, per-episode readiness with URLs. GET /api/v1/jobs lists recent ones. Free. | 1 request |
GET /api/v1/webhooks/secret | Your webhook signing secret (created on first call). POST rotates it. Free. | 1 request |
POST /api/v1/webhooks/inbox | A hosted https test receiver for 24 hours; GET /api/v1/webhooks/inbox/{id} shows what was delivered. Free. | 1 request |
POST /api/v1/ai-search/query | Search one episode for a topic, name or keyword and get verbatim quotes with timestamps. 1 credit on every plan, Free included. If we don't hold that episode yet you get a 409 quoting the real total (its hours to transcribe, plus 1 for the query; then query it as often as you like). | 1 credit+ 1 request |
POST /api/v1/key/rotate | Issues a new key and retires the current one immediately. Credits, plan and unlocks move across. | 1 request |
POST /api/v1/billing/portal | Returns a Stripe billing portal URL for the calling account: switch plan, cancel, update card. | 1 request |
Word-level timestamps
Our transcriber stores the start time of every single word, not just every subtitle line.
After unlocking, ?format=json×tamps=1&words=1 returns them:
{
"segments": [{
"start": 61.4, "end": 66.9,
"text": "And I'm Tim Houlihan. So we talk with researchers",
"words": [
{"w": "And", "s": 61.4}, {"w": "I'm", "s": 61.55},
{"w": "Tim", "s": 61.72}, {"w": "Houlihan.", "s": 61.94}, …
]
}, …]
} That powers karaoke-style highlighting, precise audio deep links, clip cutting and search that jumps to the exact second. SRT and VTT exports drop straight into video editors and players.
Shows using dynamic ad insertion re-splice their audio over time, so the file at
audio_url today can differ from the one a transcript was cut against.
Podmenti detects that change and re-aligns every timestamp automatically, so what the API
returns always matches the audio the host is serving now. How that
works.
AI Search
Instead of downloading a transcript and searching it yourself, ask a question and get back
exactly where it's answered. This is the same kind of extraction that runs Bookmenti's
book-mention pipeline at scale, pointed at whatever you ask: see
how it works for a real example. Give
POST /api/v1/ai-search/query a transcript_id and a
topic ("mentions of Acme Corp", a person's name, a product, a claim you want
fact-checked), and it returns verbatim quotes with timestamps:
the request
curl -s -X POST https://podmenti.com/api/v1/ai-search/query -H "$AUTH" \
-H "Content-Type: application/json" \
-d '{"transcript_id": "a7c18c255b33d645",
"topic": "books recommended or referenced by title, with the author if mentioned"}' the response, complete, exactly as the API returned it for the sample episode
{
"transcript_id": "a7c18c255b33d645",
"topic": "books recommended or referenced by title, with the author if mentioned",
"found": true,
"summary": "The podcast transcript references several books by title and author, including works by Elizabeth Gilbert, Anne Lamott, Jerry Seinfeld, Mary Carr, Brandon Sanderson, and Seth Godin.",
"mentions": [
{
"quote": "Elizabeth Gilbert is the author of Eat Pray Love Big magic. And city of girls.",
"context": "Tim Ferriss introduces Elizabeth Gilbert by mentioning several of her books.",
"timestamp_seconds": 114.6,
"listen_url": "https://podmenti.com/t/a7c18c255b33d645#t=114"
},
{
"quote": "Anne Lamott is the author of twenty books. Including Bird by bird. Operating instructions. And help. Thanks. Wow.",
"context": "Tim Ferriss introduces Anne Lamott, listing several of her authored books.",
"timestamp_seconds": 387.1,
"listen_url": "https://podmenti.com/t/a7c18c255b33d645#t=387"
},
{
"quote": "I'm gonna ask a question that no doubt you've been asked before, but I have to ask What do you think it is about bird by bird? that affected so many people so deeply.",
"context": "Tim Ferriss asks Anne Lamott about the impact of her book Bird by Bird.",
"timestamp_seconds": 402.1,
"listen_url": "https://podmenti.com/t/a7c18c255b33d645#t=402"
},
{
"quote": "He he's uh Neil Allen. He wrote Shapes of Truth.",
"context": "Anne Lamott mentions her husband Neil Allen's book Shapes of Truth while discussing inner critic work.",
"timestamp_seconds": 654.4,
"listen_url": "https://podmenti.com/t/a7c18c255b33d645#t=654"
},
{
"quote": "Joy Carol Oates has written more than seventy books, including Blonde And we were the Mulvanies. and won the National Book Award for her novel Them.",
"context": "Tim Ferriss introduces Joyce Carol Oates and references several of her books.",
"timestamp_seconds": 786.7,
"listen_url": "https://podmenti.com/t/a7c18c255b33d645#t=786"
},
{
"quote": "And wrote Is this anything?",
"context": "Tim Ferriss mentions Jerry Seinfeld's book Is this anything? during his introduction.",
"timestamp_seconds": null,
"listen_url": null
},
{
"quote": "I picked up this book uh by Bill Phillips called Body for Life.",
"context": "Jerry Seinfeld talks about using Bill Phillips's book Body for Life to help him get in shape.",
"timestamp_seconds": 1254.8,
"listen_url": "https://podmenti.com/t/a7c18c255b33d645#t=1254"
},
{
"quote": "Mary Carr wrote the best selling memoirs. The Liers Club? Cherry and Lit And her book on writing. The Art of Memoir.",
"context": "Tim Ferriss introduces Mary Carr and lists her memoirs and her book on writing.",
"timestamp_seconds": 1589.3,
"listen_url": "https://podmenti.com/t/a7c18c255b33d645#t=1589"
},
{
"quote": "Brandon Sanderson created the Mistborn and Stormlight archive series.",
"context": "Tim Ferriss introduces Brandon Sanderson, referencing his major series.",
"timestamp_seconds": 2110.6,
"listen_url": "https://podmenti.com/t/a7c18c255b33d645#t=2110"
},
{
"quote": "I read uh On Writing by Stephen King and How to Write Sci Fi and Fantasy by Orson Scottkard.",
"context": "Brandon Sanderson discusses reading books by Stephen King and Orson Scott Card while studying writing before breaking in.",
"timestamp_seconds": 2304.3,
"listen_url": "https://podmenti.com/t/a7c18c255b33d645#t=2304"
},
{
"quote": "Seth Godin has written twenty-one bestsellers published in forty languages, and Including Purple Cow? Lynchpin. And the practice.",
"context": "Tim Ferriss introduces Seth Godin by listing several of his best-selling books.",
"timestamp_seconds": 2707.2,
"listen_url": "https://podmenti.com/t/a7c18c255b33d645#t=2707"
},
{
"quote": "First twenty years ago. I wrote a book called Permission Marketing.",
"context": "Seth Godin discusses writing his early book Permission Marketing.",
"timestamp_seconds": null,
"listen_url": null
},
{
"quote": "And thanks to Malcolm. He sent me he was unknown. He sent me this new book called The Tipping Point.",
"context": "Seth Godin recounts receiving Malcolm Gladwell's book The Tipping Point.",
"timestamp_seconds": null,
"listen_url": null
},
{
"quote": "And 10 years later, the same 2010 you're saying. I wrote a book called Lynchpin.",
"context": "Seth Godin talks about writing his book Lynchpin.",
"timestamp_seconds": null,
"listen_url": null
}
],
"credits": 8
}
The episode has to exist first: if transcript_status isn't ready,
queue it with POST /api/v1/requests (one credit per hour of audio), and we'll
email you when it lands. Each query is 1 credit on any plan including Free, and
only charges on success: a failed query is refunded automatically. Want this running
continuously across a show, with a webhook when something new turns up? Write to
contact@podmenti.com, that's next.
Jobs: polling, webhooks and the ETA
Transcription takes minutes, so POST /api/v1/requests does not make you
wait. Episodes we already hold come back unlocked immediately; the rest become a
job. The job carries an honest estimate: we know how much requested audio is
already queued ahead of you, and we measure our transcriber's real throughput, so
estimated_ready_at is arithmetic, not a guess. Then either poll it or let us
call you.
start a job (webhook optional)
curl -s -X POST https://podmenti.com/api/v1/requests -H "$AUTH" \
-H "Content-Type: application/json" \
-d '{"episodes": [{"guid": "…", "url": "https://…/episode.mp3", "title": "…", "secs": 3600}],
"webhook_url": "https://example.com/hooks/podmenti",
"fast": false}' # true: GPU fast lane, minutes instead of within the hour, 2x credits response
{
"request_id": "9f1c2b7e-3d44-4c1a-9a6e-2f0b5c7d8e91",
"job": {
"id": "9f1c2b7e-3d44-4c1a-9a6e-2f0b5c7d8e91",
"status": "queued",
"estimated_ready_at": "2026-09-02T07:41:00.000Z",
"estimated_seconds": 1380,
"queue_ahead_episodes": 2,
"poll_url": "https://podmenti.com/api/v1/jobs/9f1c2b7e-3d44-4c1a-9a6e-2f0b5c7d8e91",
"poll_after_seconds": 300,
"webhook_url": "https://example.com/hooks/podmenti",
"episodes": [
{
"transcript_id": "3b9e1f0c7a2d4e58",
"title": "Episode title",
"status": "queued",
"charged": 1
}
]
},
"already_transcribed": [],
"queued": 1,
"credits_charged": 1,
"credits_remaining": 9,
"delivery": "queued now, estimated ready 2026-09-02T07:41:00.000Z; poll the job, or we call your webhook and email you when it lands",
"notify": "you@example.com"
} poll it
# come back after poll_after_seconds
curl -s https://podmenti.com/api/v1/jobs/9f1c2b7e-3d44-4c1a-9a6e-2f0b5c7d8e91 -H "$AUTH" GET /api/v1/jobs/{id} returns the same job object with a live
status (queued, processing, done), a fresh poll_after_seconds
(a quarter of the remaining estimate, between 30 seconds and 5 minutes), and per-episode
status with the transcript and page URLs once each one is ready. GET
/api/v1/jobs lists your recent jobs.
or receive this at your webhook when every episode has settled
{
"event": "job.done",
"job": {
"id": "9f1c2b7e-3d44-4c1a-9a6e-2f0b5c7d8e91",
"status": "done",
"poll_url": "https://podmenti.com/api/v1/jobs/9f1c2b7e-3d44-4c1a-9a6e-2f0b5c7d8e91",
"episodes": [
{
"transcript_id": "3b9e1f0c7a2d4e58",
"title": "Episode title",
"status": "ready",
"url": "https://podmenti.com/t/3b9e1f0c7a2d4e58",
"transcript_url": "https://podmenti.com/api/v1/transcripts/3b9e1f0c7a2d4e58"
}
],
"done": 1,
"total": 1
},
"sent_at": "2026-09-02T07:38:12+00:00"
}
The webhook is a POST with X-Podmenti-Event: job.done,
X-Podmenti-Job and X-Podmenti-Signature headers, delivered from
our transcription server the moment the transcripts are readable. https only. A
non-2xx answer or a timeout is retried with backoff: after 1, 5, 15, 60 and 240 minutes,
then we give up and the job stays pollable. Deliveries are signed like Stripe's:
t=<unix>,v1=<hex HMAC-SHA256(secret, t + "." + raw body)>, with the
secret from GET /api/v1/webhooks/secret (created on first call,
POST to rotate). Verify over the raw body, compare in constant time, and
reject a t older than five minutes.
Nothing to receive it with yet? POST /api/v1/webhooks/inbox gives you a hosted
https URL for 24 hours; pass it as webhook_url, then read
GET /api/v1/webhooks/inbox/{id} to see exactly what we sent, headers
and all. The same inbox is what our own smoke test uses.
A full example
# find the show (by name, or paste an Apple / RSS link)
curl -s "https://podmenti.com/api/v1/search?q=huberman" -H "$AUTH"
# -> shows[0].feed_id = 1365758, plus episodes_url
# list episodes, grab a transcript_id with transcript_status "ready"
curl -s "https://podmenti.com/api/v1/shows/1365758/episodes" -H "$AUTH"
# what would it cost me? (0 if already unlocked)
curl -s "https://podmenti.com/api/v1/episodes/$ID" -H "$AUTH"
# unlock once (1 credit), then read and export in every format
curl -s -X POST https://podmenti.com/api/v1/unlock -H "$AUTH" \
-H "Content-Type: application/json" -d '{"transcript": "'$ID'"}'
curl -s "https://podmenti.com/api/v1/transcripts/$ID?format=text" -H "$AUTH"
curl -sO "https://podmenti.com/api/v1/transcripts/$ID?format=srt" -H "$AUTH" Pricing and limits
- One credit is one hour of audio. An episode we already hold is 1 credit to unlock, any length, every format included. One we don't is its length in hours, nearest hour, never less than one. Nothing else moves a price.
- Unmetered on every plan: search, episode lists and transcript status.
- AI Search: 1 credit per query on every plan including Free.
- Plans: 120/mo for $9, 500/mo for $29, 2,000/mo for $99, or 10 free, at /plans. Credits reset every billing cycle, no rollover.
- Fast lane: 2 credits per hour, ready in minutes whatever the length. The measured median is on /plans and in
faston /api/queue; the promise is on the job, and missing it refunds the premium. - Requests: every call with a key counts one against the plan's daily allowance, Free 1,000, Plus 5,000, Pro 20,000, Business 50,000 a day.
X-RateLimit-LimitandX-RateLimit-Remainingride on every response;GET /api/v1/usagereportsrequests_todayandrequest_limit. Resets at midnight UTC. - Errors are JSON:
{"error": {"code", "message"}}. 402 means credits are needed, 429 means today's requests are spent.
OpenAPI and llms.txt
The whole API is described in /openapi.json (OpenAPI 3.1), which any client generator or AI agent can read directly, and summarised for language models in /llms.txt. Both quote the same prices as this page, because they are generated from the same source.
Building something bigger, need higher limits, or want an MCP connector for your AI assistant? Write to contact@podmenti.com.