Clinical trials as JSON or CSV: the ClinicalTrials.gov API v2, a Python example, the terms of use and a free sample
How to pull clinical trial records from ClinicalTrials.gov yourself, which parameters and limits matter, what the terms of use require when you republish the data, and when a normalized feed is worth paying for.
The short version
- ClinicalTrials.gov, run by the US National Library of Medicine, has a free REST API v2 with no key:
GET https://clinicaltrials.gov/api/v2/studies. It holds 610,285 studies (5 November 2026) and refreshes daily. - The endpoint is friendly, but you need to know three things: ask only for the
fieldsyou need, page withnextPageToken(pageSizeis capped at 1000), and filter with the Essie expression syntax infilter.advancedfor phases and date ranges. - The data comes with terms that are unusual for an open API: attribution, showing the processing date, describing your modifications, and never using extracted e-mail addresses for marketing. They are quoted in section 5.
1. The ClinicalTrials.gov API v2
Documentation: clinicaltrials.gov/data-api/api (an OpenAPI description sits behind it). GET /api/v2/version returned apiVersion 2.0.5 and dataTimestamp 2026-11-04T10:00:04 on 5 November 2026, which is the "date the data were processed" you are asked to show.
curl -s "https://clinicaltrials.gov/api/v2/studies?query.cond=diabetes&filter.overallStatus=RECRUITING&pageSize=2&countTotal=true&fields=NCTId|BriefTitle|OverallStatus"
Without any filter, countTotal=true returned totalCount: 610285; query.cond=diabetes matched 24,598 studies.
Query parameters that worked
| Goal | Parameter |
|---|---|
| Condition, intervention, sponsor, free text, title | query.cond, query.intr, query.spons, query.term, query.titles |
| Location | query.locn=Milan AND Italy (1,736 recruiting studies matched in our test), or filter.geo=distance(45.46,9.19,50km) |
| Status | filter.overallStatus=RECRUITING,NOT_YET_RECRUITING (comma list) |
| Specific studies | filter.ids=NCT04000165,NCT05867927 (unknown IDs are silently dropped) |
| Phase, dates, study type | filter.advanced=AREA[Phase](PHASE2 OR PHASE3) AND AREA[StartDate]RANGE[2025-01-01,MAX]; MIN and MAX open a range |
| Sort | sort=LastUpdatePostDate:desc, StudyFirstPostDate:desc, StartDate:desc or @relevance |
| Payload size | fields=NCTId|BriefTitle|... (module or piece names) |
| Paging | pageSize (maximum 1000), pageToken from the response's nextPageToken, countTotal=true |
Behaviour and limits we saw
- Errors are
HTTP 400with a plain-text body, not JSON:Unknown sort field,Incorrect pageToken format. Do not retry them. pageSizeis capped silently at 1000. Asking for 1001 or 5000 returned exactly 1000 studies with no error.- Payloads get big. 1,000 studies with the modules a catalogue needs weighed about 70 KB per 1,000 when only the NCT ID was requested; with identification, status, design, sponsor, conditions and locations modules it is roughly 8 MB (about 3 seconds). A full record with the
resultsSectioncan be much larger. Requestfieldsexplicitly. - Paging by token is the way through a long result; the token changes on every page and is tied to the query.
- Rate limit: we sent 70 back-to-back requests and saw no
429. We could not find an official figure on the API pages we read (/data-api/api,/data-api/about-api), so we pace at one request per second and honourRetry-After; do the same. - Dates are published as they are. Many are
YYYY-MM(month only), for example"primaryCompletionDate": "2028-09"; they sort as text but are not always full dates. - Nested JSON. Everything sits under
protocolSection.<module>(for examplestatusModule.startDateStruct.date); dates come as{date, type}structs; phases are a list (PHASE2andPHASE3for a Phase 2/3 study) and are absent for observational studies. - Big trials have hundreds of sites: the studies in our sample range from 0 to 378 locations.
2. DIY: recruiting studies to CSV in Python
This script (tested on 5 November 2026: 99 recruiting Phase 3 breast cancer studies started since 2025, all written) pages through results, retries on transient errors, flattens the nested JSON and prints the data-processed stamp for attribution. It needs pip install requests.
import csv
import time
import requests
BASE = "https://clinicaltrials.gov/api/v2"
# Ask only for the modules you need: a full record can weigh tens of KB (results sections are huge).
FIELDS = "NCTId,BriefTitle,OverallStatus,Phase,StudyType,Condition,LeadSponsorName,LeadSponsorClass," \
"EnrollmentCount,StartDate,PrimaryCompletionDate,LastUpdatePostDate,LocationCountry"
PARAMS = {
"query.cond": "breast cancer",
"filter.overallStatus": "RECRUITING", # comma list: RECRUITING,NOT_YET_RECRUITING
"filter.advanced": "AREA[Phase]PHASE3 AND AREA[StartDate]RANGE[2025-01-01,MAX]", # Essie expression
"sort": "LastUpdatePostDate:desc",
"fields": FIELDS,
"pageSize": 200, # 1000 is the maximum; a larger value is silently capped
"countTotal": "true",
}
def get(params):
for attempt in range(5):
r = requests.get(f"{BASE}/studies", params=params, timeout=60)
if r.status_code in (429, 500, 502, 503, 504):
time.sleep(int(r.headers.get("Retry-After", 2 ** attempt)))
continue
r.raise_for_status() # 400 = plain-text message (bad sort, fields, pageToken)
return r.json()
raise RuntimeError("ClinicalTrials.gov unavailable")
stamp = requests.get(f"{BASE}/version", timeout=30).json()["dataTimestamp"] # "date the data were processed"
rows, token = [], None
while len(rows) < 400: # demo cap
p = dict(PARAMS, **({"pageToken": token} if token else {}))
data = get(p)
if token is None:
print(data["totalCount"], "studies match; data processed", stamp)
for s in data["studies"]:
ps = s["protocolSection"]
rows.append({
"nct_id": ps["identificationModule"]["nctId"],
"title": ps["identificationModule"].get("briefTitle"),
"status": ps["statusModule"].get("overallStatus"),
"phases": ";".join(ps.get("designModule", {}).get("phases", [])),
"conditions": ";".join(ps.get("conditionsModule", {}).get("conditions", [])),
"sponsor": ps.get("sponsorCollaboratorsModule", {}).get("leadSponsor", {}).get("name"),
"enrollment": ps.get("designModule", {}).get("enrollmentInfo", {}).get("count"),
"start": ps["statusModule"].get("startDateStruct", {}).get("date"), # "2025-03" or "2025-03-14"
"last_update": ps["statusModule"].get("lastUpdatePostDateStruct", {}).get("date"),
"url": f"https://clinicaltrials.gov/study/{ps['identificationModule']['nctId']}",
})
token = data.get("nextPageToken")
if not token:
break
time.sleep(1)
with open("trials.csv", "w", newline="", encoding="utf-8") as f:
w = csv.DictWriter(f, fieldnames=rows[0].keys())
w.writeheader()
w.writerows(rows)
print(len(rows), "studies written. Source: ClinicalTrials.gov, data processed", stamp)
3. When the Actor saves time
For a one-off export the script is enough, and free. The Apify Actor ClinicalTrials.gov Search (by Nexus Scrapers) wraps the same official API. It pays off when you would otherwise maintain that code:
- A flat study record with stable keys: NCT ID and link, titles, status, phases as a list, study type, summary, conditions, interventions (type and name), lead sponsor and class, collaborators, enrollment, start and completion dates, posting dates, total site count, distinct site countries and a capped site list, age and sex, healthy volunteers, and a has-results flag. Missing values are
nullor empty lists. - Filters as plain inputs instead of Essie strings: condition, intervention, sponsor, search term, statuses, phases, study types, country and city, a radius around a point, specific NCT IDs, only studies with results, and a sort choice. The default is recruiting and not-yet-recruiting studies, newest update first.
- Retries, paging and pacing are handled (1 request per second, backoff, Retry-After).
- Contacts are off by default. Study staff names, e-mails and phones are only added if you switch
includeContactson. Eligibility criteria and outcomes are opt-in too. - Scheduling and integrations through Apify: weekly runs into Slack, e-mail, Google Sheets or a webhook; export as JSON, CSV or Excel; API and MCP access.
Pricing is pay per result; the Actor page shows the current price.
What it does not do: it does not return results data (outcome statistics, adverse events), arm details or the detailed description; there is no change tracking yet ("only new since last run"); runs are capped at 10,000 studies (split larger exports by date range); and it is not medical advice. The terms in section 5 apply to your use of the output as well. For a single list, use the script.
4. Real sample: 19 normalized records
These 19 records were produced on 5 November 2026 by running the Actor's own code locally on live API responses (no Apify platform involved). Data processed by ClinicalTrials.gov: 2026-11-04 10:00:04 (from /api/v2/version). Selection: the 13 most recently updated recruiting or not-yet-recruiting studies, 3 Phase 3 breast cancer studies recruiting in Italy and 3 completed diabetes studies with posted results, minus any study whose lead sponsor or collaborator is a named individual (one was skipped). They are the first matches in that order, not hand-picked for quality.
Contacts: none. The sample has no central contacts, no overall officials, no investigators and no e-mail address or phone number (we checked: zero @ characters in the file, and a scrub pass over all text fields found nothing to remove).
Modifications we made, as the terms ask us to say: the nested JSON is flattened into one record per study; locations is cut to the first five sites while locationsCount and locationCountries are computed from all sites; contacts, results data, arm groups and the detailed description are left out; dates are as published; the rest of the text is unchanged. Attribution: source ClinicalTrials.gov, US National Library of Medicine.
Download: sample.json (full records, UTF-8) and sample.csv (flattened, lists joined with | , opens in Excel). Free to use under the terms in section 5; the records go stale, so check ClinicalTrials.gov before relying on a status.
| Study | Status | Phase | Lead sponsor | Title | Enrol. | Updated |
|---|---|---|---|---|---|---|
| NCT07807280 | recruiting | NA | University of California, L… | C-Carbs: A CGM-guided Approach to Teaching Carb Awa… | 40 | 2026-11-04 |
| NCT05867927 | recruiting | - | Intuitive Surgical | GENERATE - Next GENERATion of GynEcological Surgery… | 1250 | 2026-11-04 |
| NCT07719582 | recruiting | NA | Dream Video LLC | Effectivity of P1821, a Novel Tongue Strengthening… | 100 | 2026-11-04 |
| NCT07915284 | not yet recruiting | - | Sohag University | Platelet Transfusion Thresholds in Non-Bleeding Pat… | 30 | 2026-11-04 |
| NCT07703878 | recruiting | P1 | Gilead Sciences | A Study to Evaluate the Safety and Antitumor Activi… | 302 | 2026-11-04 |
| NCT07226843 | recruiting | P1 | Eli Lilly and Company | A Study of LY4584180 in Adult Participants With Pre… | 460 | 2026-11-04 |
| NCT07916558 | not yet recruiting | - | Samsun University | Impact of Parasternal and Rectointercostal Blocks o… | 40 | 2026-11-04 |
| NCT06362733 | recruiting | NA | Stanford University | Modified Pivotal Response Treatment for Insistence… | 44 | 2026-11-04 |
| NCT07770633 | not yet recruiting | NA | The Cleveland Clinic | Negative Pressure Wound Therapy Devices in Total Kn… | 300 | 2026-11-04 |
| NCT07917364 | recruiting | - | University of Applied Scien… | Asprosin and miRNA Expression in Young Women With M… | 110 | 2026-11-04 |
| NCT07915115 | not yet recruiting | NA | Texas Christian University | Computer-Assisted Vocabulary Intervention for Spani… | 135 | 2026-11-04 |
| NCT07139132 | recruiting | NA | University of Wisconsin, Ma… | The AHA Oneida Study | 95 | 2026-11-04 |
| NCT07144150 | recruiting | NA | Cook Research Incorporated | EVERO Drug-coated Balloon (DCB) Randomized Trial | 410 | 2026-11-04 |
| NCT07174336 | recruiting | P3 | Eli Lilly and Company | A Study of Tersolisib (LY4064809/STX-478) With Othe… | 800 | 2026-11-04 |
| NCT07060807 | recruiting | P3 | Merck Sharp & Dohme LLC | A Clinical Study of Patritumab Deruxtecan to Treat… | 1000 | 2026-11-03 |
| NCT07173751 | recruiting | P3 | BioNTech SE | ROSETTA Breast-01: The Effects and Safety of Pumita… | 558 | 2026-10-30 |
| NCT00679042 | completed | P3 | CellTrans Inc. | Islet Transplantation in Type 1 Diabetic Patients U… | 21 | 2026-11-04 |
| NCT04828785 | completed | NA | University of North Carolin… | Food As MedicinE for Diabetes | 215 | 2026-11-03 |
| NCT05923827 | completed | NA | Insulet Corporation | Omnipod® 5 With Libre 2 vs. MDI for Type 1 Diabetes… | 189 | 2026-11-03 |
One record, trimmed (two of the sites, no summary):
{
"schemaVersion": "1.0",
"nctId": "NCT07226843",
"url": "https://clinicaltrials.gov/study/NCT07226843",
"briefTitle": "A Study of LY4584180 in Adult Participants With Previously Treated Blood Cancers",
"acronym": null,
"overallStatus": "RECRUITING",
"whyStopped": null,
"phases": [
"PHASE1"
],
"studyType": "INTERVENTIONAL",
"conditions": [
"Lymphoma, Non-Hodgkin's",
"Lymphoma, Diffuse Large B-Cell",
"Follicular Lymphoma"
],
"interventions": [
{
"type": "DRUG",
"name": "LY4584180"
},
{
"type": "DRUG",
"name": "Rituximab"
}
],
"leadSponsor": "Eli Lilly and Company",
"leadSponsorClass": "INDUSTRY",
"collaborators": [],
"enrollment": 460,
"enrollmentType": "ESTIMATED",
"startDate": "2026-04-17",
"startDateType": "ACTUAL",
"primaryCompletionDate": "2028-09",
"completionDate": "2030-02",
"firstPostedDate": "2025-11-12",
"lastUpdatePostedDate": "2026-11-04",
"statusVerifiedDate": "2026-11",
"locationsCount": 46,
"locationCountries": [
"United States",
"France",
"Germany",
"Italy",
"Japan",
"Poland",
"Spain",
"United Kingdom"
],
"locations": [
{
"facility": "Mayo Clinic - Scottsdale",
"city": "Scottsdale",
"state": "Arizona",
"country": "United States",
"status": "RECRUITING"
},
{
"facility": "City of Hope",
"city": "Duarte",
"state": "California",
"country": "United States",
"status": "RECRUITING"
}
],
"minimumAge": "18 Years",
"maximumAge": null,
"ageGroups": [
"ADULT",
"OLDER_ADULT"
],
"sex": "ALL",
"healthyVolunteers": false,
"hasResults": false,
"scrapedAt": "2026-11-05T07:48:30+00:00"
}
5. Data licence and terms
The data is free for everyone, inside and outside the US, but it is not a standard open licence. The ClinicalTrials.gov Terms and Conditions (last updated 31 January 2023, read on 5 November 2026) say they apply to all data obtained from ClinicalTrials.gov, in any format and by any method, and that downloading the data indicates acceptance. In any publication or distribution of the data, you should:
- Attribute the source of the data as ClinicalTrials.gov;
- Update the data so that they are current at all times;
- Clearly display the date the data were processed by ClinicalTrials.gov (for the API, the
dataTimestampfrom/api/v2/version); - State any modifications made to the content, with a complete description of them.
You must not assert proprietary rights to any part of the database, or represent it to anyone as other than a United States Government database. You must not use e-mail addresses extracted from the database for marketing or other promotional purposes (study contacts' addresses are exactly that). The data carry an international copyright outside the United States and its territories, and some data may be subject to the copyright of third parties, for which you should consult those entities. The US Government gives no warranties and accepts no liability for any use of the data. The terms stay in effect as long as you keep any of the data.
This is a summary, not legal advice. Study records are written by sponsors; nothing here is medical advice, and the registry is not a substitute for talking to a clinician. Personal data: contacts and investigators are named staff, which is why the Actor leaves them out by default.