The PDUFA API
curl https://www.pdufa.bio/api/v1/pdufa?limit=3
Tiers
| Tier | Price | Quota | Burst | Fields |
|---|---|---|---|---|
| Anonymous no key | $0 | 1,000/day | 10/min | Core |
| Free x-api-key | $0 | 10,000/mo | 30/min | Core |
| Pro | $10/mo · $100/yr | 100,000/mo | 120/min | Core + Depth |
| Quant | $149/mo | 2,000,000/mo | 600/min | Core + Depth + History |
How dates work, and one breaking change on 2026-09-09
date is an announced day, or it is null.
date_precision was not day: 263 rows served the 15th and 62 more served the 30th or 31st, days no sponsor ever announced. Roughly seven rows in ten. Our own source comment said not to read those as days, which is no protection for a parser or for a language model summarising the feed. So the field now means one thing.
date_precision: "day",dateis that day, sourced to an FDA notice, an SEC filing or the sponsor’s own release.date_precision: "month","quarter"or"year",dateisnull, and the granularity we actually hold is indate_month(for example"2026-06"). Most of these are ClinicalTrials.gov primary-completion estimates, and they move.days_to_decisionisnullwheneverdateis. Counting down to a day we invented would be the same fabrication one field over.
date, sort on date_month first, then date. Rows with a real day still sort correctly inside their month, and filtering for date_precision === "day" gives you the dated calendar with nothing estimated in it.What’s free, and what isn’t
/pdufa/* page. Charging for data anyone could scrape off the page was leaky and dishonest, so we stopped. All of it is now free.source names the document a row rests on ("Corcept 8-K 2026-07-29 (EX-99.1)", "trial-estimate (not company-confirmed)") and source_url links it: an SEC filing, a ClinicalTrials.gov record, an FDA notice or the sponsor’s release. Both are null when we could not settle the row in a primary document, and a date_precision: "day" row with a null source_url is one we are still working to source. date_history is an array of every date the row has held and why it moved: [{"date":"2026-08-22"}, {"date":"2026-11-22","changed":"2026-08-24","why":"The FDA extended the goal date..."}]. Row ids embed the date a row was created on and are stable: when a goal date is extended or a registry estimate slides, the id keeps its original date and date_history records the move, so your stored rows stay one row. Ids are re-keyed only when the original date was our error, never for a legitimate change./api/v1/runup• Bulk export of the whole dataset (CSV / JSONL)
• Subscribable .ics calendar feeds
• Date-slip webhooks
• 100,000 requests/month instead of 1,000/day
In other words: you pay for scale and for the series, not for secrets. The catalyst facts are free forever: that’s the whole point of the calendar.
Getting a key
curl it.
Send it as a header:
x-api-key: pk_live_… (or ?key= for quick tests).
Rate limits: published, so they feel fair
| Tier | Quota | Burst | Cost per call |
|---|---|---|---|
| Anonymous | 1,000 / day | 10 / min | 1 |
| Free (keyed) | 10,000 / mo | 30 / min | 1 |
| Pro | 100,000 / mo | 120 / min | 1 · runup 5 · export 50 |
| Quant | 2,000,000 / mo | 600 / min | same |
402: with top-up credits that never expire. Burst abuse returns 429; a commercial limit is never disguised as an abuse error.Endpoints
| Path | Tier | Notes |
|---|---|---|
| /api/v1/events | Any | All catalyst types |
| /api/v1/pdufa | Any | FDA decision dates |
| /api/v1/readouts | Any | Trial readouts |
| /api/v1/conferences | Any | Conference presentations |
| /api/v1/adcomm | Any | Advisory committee meetings |
| /api/v1/usage | Any | Your quota, credits, tier |
| /api/v1/health | Any | Liveness |
| /api/v1/runup | Pro | Per-event daily T-120→T+5 series (5 credits) |
| /api/v1/export | Pro | Bulk CSV / JSONL (50 credits) |
| /api/v1/calendar.ics | Pro | Subscribable calendar feed |
Parameters
ticker (comma-separated) · type · ta · status · from · to · q (free-text) · limit (max 1000) · offset · include · format (json|csv) · id
Copy-paste recipes
=IMPORTDATA("https://www.pdufa.bio/api/v1/pdufa?format=csv&limit=100")import pandas as pd
df = pd.read_csv("https://www.pdufa.bio/api/v1/pdufa?format=csv&limit=1000")
print(df[["ticker","date","name","market_cap_tier"]].head())const r = await fetch("https://www.pdufa.bio/api/v1/events?type=PDUFA&from=2026-07-01");
const { data } = await r.json();
data.forEach(e => console.log(e.date, e.ticker, e.name));https://www.pdufa.bio/api/v1/calendar.ics?key=YOUR_KEY&ticker=CELC,MNKD
Rate-limit headers
Every response tells you exactly where you stand, no surprises.
X-Api-Tier: anonymous X-RateLimit-Limit: 1000 X-RateLimit-Remaining: 998 X-RateLimit-Reset: 1785542400 X-Credits-Remaining: 0 X-Credits-Cost: 1 X-Quota-State: ok # ok | warning | grace | exhausted | unmetered X-Request-Id: req_01J9Z...
We never hard-slam you. At 80% you get warning. Over 100% you get a grace overage so nothing breaks mid-job. Only past that do you get a 402, with credit and upgrade links, and still-cached data so your client degrades instead of dying. Burst abuse returns 429; a commercial limit is never disguised as an abuse error.
Errors
| Code | Meaning |
|---|---|
| 400 invalid_param | Unknown parameter: the message lists the valid ones |
| 401 invalid_key | Key missing or unrecognised |
| 403 tier_forbidden | Endpoint/field above your tier: includes _upgrade |
| 402 quota_exhausted | Commercial limit: credit + upgrade options in the body |
| 429 rate_limited | Burst abuse: honour Retry-After |
| 503 degraded | Serving stale cache: check meta.served_from |
X-Request-Id. Quote it and we can trace it.Attribution
Free and anonymous use requires a visible link back. Copy-paste:
Catalyst data from <a href="https://www.pdufa.bio">pdufa.bio</a>
Questions
Is the pdufa.bio API free?
Yes. The read API at /api/v1/ is free, and /llms.txt documents it for AI agents.
What does the API return?
The tracked catalyst dataset: PDUFA dates, readout windows, conference presentations and advisory committee meetings, each record with its canonical page URL.
How current is the API?
Rebuilt daily with the site; the as_of field carries the build date.
Not affiliated with or endorsed by the FDA. pdufa.bio is an independent publication with no affiliation with, endorsement by, sponsorship by, or connection to the U.S. Food and Drug Administration or any other government agency. “FDA”, “PDUFA” and all company, drug and ticker names are used descriptively and remain the property of their respective owners.
Informational and educational purposes only. Not investment advice. Nothing on this page is investment, legal, tax or medical advice, or an offer or solicitation to buy or sell any security. pdufa.bio is not a registered investment adviser or broker-dealer and does not recommend trades or publish individual-drug approval probabilities. Verify every date and outcome against primary FDA, SEC or company filings. Data is provided as is, without warranty of any kind, and past behaviour does not predict future outcomes.
© 2026 pdufa.bio. All rights reserved.