If you've ever needed to know which drugs are in trial for a given condition, who's sponsoring a study, or whether a trial is still recruiting, you don't need to scrape the ClinicalTrials.gov website. The US National Library of Medicine runs an official v2 API over its registry of 500,000+ studies — free, public, and keyless. No sign-up, no token, no rate-limit tier to unlock.
The base
https://clinicaltrials.gov/api/v2/studies
Everything is a GET against that one endpoint with different query parameters — no auth header, no API key.
curl "https://clinicaltrials.gov/api/v2/studies?query.cond=diabetes&pageSize=5"
Query parameters
| Param | Meaning | Example |
|---|---|---|
query.cond |
Condition / disease (Essie expression syntax). | query.cond=lung+cancer |
query.intr |
Intervention / drug / treatment. | query.intr=semaglutide |
query.spons |
Lead sponsor or collaborator name. | query.spons=Pfizer |
query.term |
Free-text search across fields (same expression syntax). | query.term=obesity+AND+phase2 |
filter.overallStatus |
Comma-separated status filter. | filter.overallStatus=RECRUITING |
pageSize |
Studies per page — default 10, max 1000. Always set it explicitly. | pageSize=100 |
pageToken |
Cursor for the next page, taken from nextPageToken in the previous response — not a numeric offset. |
pageToken=abc123 |
countTotal |
Set true on the first request to get a totalCount in the response. |
countTotal=true |
fields |
Comma-separated field list, to shrink the payload when you don't need everything. | fields=NCTId,BriefTitle,OverallStatus |
Examples
# Recruiting trials for a specific drug, with a total count
curl "https://clinicaltrials.gov/api/v2/studies?query.intr=semaglutide&filter.overallStatus=RECRUITING&pageSize=20&countTotal=true"
# Trials sponsored by a specific company
curl "https://clinicaltrials.gov/api/v2/studies?query.spons=Pfizer&pageSize=50"
# Free-text search, narrower payload via `fields`
curl "https://clinicaltrials.gov/api/v2/studies?query.term=obesity+AND+phase2&fields=NCTId,BriefTitle,OverallStatus,LeadSponsorName"
A response looks like this (field names abbreviated for readability — the real payload nests everything under protocolSection):
{
"studies": [
{
"protocolSection": {
"identificationModule": { "nctId": "NCT07091500", "briefTitle": "GLP-1 Receptor Agonist for Obesity" },
"statusModule": { "overallStatus": "RECRUITING" },
"sponsorCollaboratorsModule": { "leadSponsor": { "name": "Example University" } }
}
}
],
"nextPageToken": "eyJ2IjoxfQ",
"totalCount": 1234
}
(The 1234 above is a placeholder — run the countTotal=true call yourself to get the live figure for your query; it changes as new trials are registered.)
Page through everything
Pagination is cursor-based, not offset-based — grab nextPageToken from each response and pass it back as pageToken until the field is absent:
curl "https://clinicaltrials.gov/api/v2/studies?query.cond=diabetes&pageSize=1000&pageToken=eyJ2IjoxfQ"
What's in a study record
Each study nests into modules — the ones people actually query for:
-
Identity —
nctId,briefTitle,officialTitle -
Status & design —
overallStatus,phases,studyType,enrollmentInfo.count -
Dates —
startDateStruct,completionDateStruct,lastUpdatePostDateStruct -
Sponsors —
leadSponsor.name,collaborators[].name -
Clinical —
conditions[],interventions[](typed, e.g.DRUG: Semaglutide),briefSummary -
Eligibility —
sex,minimumAge,maximumAge,healthyVolunteers -
Geography —
contactsLocationsModule.locations[](country, facility)
The only two rules
- Be reasonable with volume — there's no published hard rate limit for anonymous use, but batch and cache rather than hammering the endpoint.
- Use
pageToken, not a numeric offset, to page — the API doesn't support jumping to page N.
That's the whole thing. No account, no key, no scraping the SPA frontend (which just serves bootstrap JS to a screen-scraper anyway).
If you'd rather skip the module-nesting, the pagination loop and flattening the response yourself, the ClinicalTrials.gov Scraper on Apify wraps exactly these endpoints — condition/intervention/sponsor/status filters in, one flat row per study out.
📌 Full endpoint cheatsheet (copy-paste reference): github.com/noble-ronin/clinicaltrials-api


Top comments (0)