ClinicalTrials.gov Database — Clinical Trial Search
Overview
Query the ClinicalTrials.gov API v2 (public, no authentication) to search and retrieve clinical trial data worldwide. Supports searching by condition, intervention, location, sponsor, and status; retrieving detailed study information by NCT ID; paginating large result sets; and exporting to CSV.
When to Use
- Searching for recruiting clinical trials for a specific condition or disease
- Finding trials testing a specific drug, device, or intervention
- Locating trials in a specific geographic region for patient referral
- Tracking a sponsor's or institution's clinical trial portfolio
- Retrieving detailed eligibility criteria, outcomes, and contacts for a specific trial
- Analyzing clinical trial trends (phases, enrollment, timelines) across a therapeutic area
- Exporting trial data for systematic reviews or meta-analyses
- Monitoring trial status changes and results postings
- For chemical compound bioactivity data use chembl-database-bioactivity instead; for published literature use pubmed-database
Prerequisites
uv pip install requests pandas
API details:
- Base URL:
https://clinicaltrials.gov/api/v2 - Authentication: None required (public API)
- Rate limit: ~50 requests/minute per IP
- Response formats: JSON (default), CSV
- Max page size: 1000 studies per request
- Date format: ISO 8601; text fields use CommonMark Markdown
Quick Start
import requests
import time
CT_API = "https://clinicaltrials.gov/api/v2"
def ct_search(params):
"""Reusable helper for ClinicalTrials.gov searches."""
response = requests.get(f"{CT_API}/studies", params=params, timeout=30)
response.raise_for_status()
return response.json()
# Search for recruiting breast cancer trials
results = ct_search({
"query.cond": "breast cancer",
"filter.overallStatus": "RECRUITING",
"pageSize": 10,
"sort": "LastUpdatePostDate:desc"
})
print(f"Found {results['totalCount']} trials")
for study in results['studies'][:3]:
nct = study['protocolSection']['identificationModule']['nctId']
title = study['protocolSection']['identificationModule']['briefTitle']
print(f" {nct}: {title}")
Key Concepts
Response Data Structure
ClinicalTrials.gov returns deeply nested JSON. Key navigation paths:
| Data | Path | |------|------| | NCT ID | study['protocolSection']['identificationModule']['nctId'] | | Title | study['protocolSection']['identificationModule']['briefTitle'] | | Status | study['protocolSection']['statusModule']['overallStatus'] | | Phase | study['protocolSection']['designModule']['phases'] | | Enrollment | study['protocolSection']['designModule']['enrollmentInfo']['count'] | | Eligibility | study['protocolSection']['eligibilityModule'] | | Locations | study['protocolSection']['contactsLocationsModule']['locations'] | | Interventions | study['protocolSection']['armsInterventionsModule']['interventions'] | | Results | study.get('resultsSection') (None if no results posted) |
Study Status Values
| Status | Description | |--------|-------------| | RECRUITING | Currently recruiting participants | | NOT_YET_RECRUITING | Approved but not yet open | | ENROLLING_BY_INVITATION | Invitation-only enrollment | | ACTIVE_NOT_RECRUITING | Active, enrollment closed | | SUSPENDED | Temporarily halted | | TERMINATED | Stopped prematurely | | COMPLETED | Study concluded | | WITHDRAWN | Withdrawn before enrollment |
Study Phase Values
| Phase | Description | |-------|-------------| | EARLY_PHASE1 | Early Phase 1 (formerly Phase 0) | | PHASE1 | Phase 1 — safety and dosing | | PHASE2 | Phase 2 — efficacy and side effects | | PHASE3 | Phase 3 — large-scale efficacy | | PHASE4 | Phase 4 — post-market surveillance | | NA | Not applicable (non-drug studies) |
Query Parameters Reference
| Parameter | Type | Description | Example | |-----------|------|-------------|---------| | query.cond | string | Condition/disease | lung cancer | | query.intr | string | Intervention/drug | Pembrolizumab | | query.locn | string | Geographic location | New York | | query.spons | string | Sponsor name | National Cancer Institute | | query.term | string | General full-text search | immunotherapy | | filter.overallStatus | string | Status filter (comma-separated) | RECRUITING,COMPLETED | | filter.phase | string | Phase filter | PHASE2,PHASE3 | | filter.ids | string | NCT ID filter | NCT04852770 | | sort | string | Sort order | LastUpdatePostDate:desc | | pageSize | int | Results per page (max 1000) | 100 | | pageToken | string | Pagination token | (from previous response) | | format | string | Response format | json or csv |
Sort options: LastUpdatePostDate, EnrollmentCount, StartDate, StudyFirstPostDate — each with :asc or :desc.
Core API
1. Search by Condition
results = ct_search({
"query.cond": "type 2 diabetes",
"filter.overallStatus": "RECRUITING",
"pageSize": 20,
"sort": "LastUpdatePostDate:desc"
})
print(f"Found {results['totalCount']} recruiting diabetes trials")
for study in results['studies'][:5]:
proto = study['protocolSection']
nct = proto['identificationModule']['nctId']
title = proto['identificationModule']['briefTitle']
print(f" {nct}: {title}")
2. Search by Intervention/Drug
# Find Phase 3 trials testing Pembrolizumab
results = ct_search({
"query.intr": "Pembrolizumab",
"filter.overallStatus": "RECRUITING,ACTIVE_NOT_RECRUITING",
"filter.phase": "PHASE3",
"pageSize": 50
})
print(f"Phase 3 Pembrolizumab trials: {results['totalCount']}")
3. Search by Location
results = ct_search({
"query.cond": "cancer",
"query.locn": "New York",
"filter.overallStatus": "RECRUITING",
"pageSize": 20
})
# Extract location details
for study in results['studies'][:3]:
locs = study['protocolSection'].get('contactsLocationsModule', {}).get('locations', [])
for loc in locs:
if 'New York' in loc.get('city', ''):
print(f" {loc.get('facility')}: {loc['city']}, {loc.get('state', '')}")
4. Search by Sponsor
results = ct_search({
"query.spons": "National Cancer Institute",
"pageSize": 20
})
for study in results['studies'][:5]:
sponsor_mod = study['protocolSection']['sponsorCollaboratorsModule']
lead = sponsor_mod['leadSponsor']['name']
collabs = [c['name'] for c in sponsor_mod.get('collaborators', [])]
print(f" Lead: {lead}, Collaborators: {collabs}")
5. Retrieve Study Details by NCT ID
nct_id = "NCT04852770"
response = requests.get(f"{CT_API}/studies/{nct_id}", timeout=30)
response.raise_for_status()
study = response.json()
# Extract key information
proto = study['protocolSection']
print(f"Title: {proto['identificationModule']['briefTitle']}")
print(f"Status: {proto['statusModule']['overallStatus']}")
# Eligibility criteria
elig = proto.get('eligibilityModule', {})
print(f"Ages: {elig.get('minimumAge')} - {elig.get('maximumAge')}")
print(f"Sex: {elig.get('sex')}")
print(f"Criteria:\n{elig.get('eligibilityCriteria', 'N/A')[:300]}")
6. Pagination for Large Result Sets
all_studies = []
page_token = None
max_pages = 10
for page in range(max_pages):
params = {
"query.cond": "cancer",
"filter.overallStatus": "RECRUITING",
"pageSize": 1000,
}
if page_token:
params["pageToken"] = page_token
results = ct_search(params)
all_studies.extend(results['studies'])
page_token = results.get('nextPageToken')
if not page_token:
break
time.sleep(1.5) # respect rate limits
print(f"Retrieved {len(all_studies)} studies across {page + 1} pages")
7. Export to CSV
response = requests.get(f"{CT_API}/studies", params={
"query.cond": "heart disease",