DEV Community

Noble Ronin
Noble Ronin

Posted on

Clinical Trials Data Has a Free JSON API — Every Study on ClinicalTrials.gov, No Key Required

Clinical Trials Data Has a Free JSON API

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
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

(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"
Enter fullscreen mode Exit fullscreen mode

What's in a study record

Each study nests into modules — the ones people actually query for:

  • IdentitynctId, briefTitle, officialTitle
  • Status & designoverallStatus, phases, studyType, enrollmentInfo.count
  • DatesstartDateStruct, completionDateStruct, lastUpdatePostDateStruct
  • SponsorsleadSponsor.name, collaborators[].name
  • Clinicalconditions[], interventions[] (typed, e.g. DRUG: Semaglutide), briefSummary
  • Eligibilitysex, minimumAge, maximumAge, healthyVolunteers
  • GeographycontactsLocationsModule.locations[] (country, facility)

Query the ClinicalTrials.gov API without an account

The only two rules

  1. Be reasonable with volume — there's no published hard rate limit for anonymous use, but batch and cache rather than hammering the endpoint.
  2. 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)