ClinicalTrials.gov API: Look Up a Study by NCT Number
Fetch one trial by NCT number with the ClinicalTrials.gov v2 API: the /studies/{nctId} endpoint, NCT format, fields, 404 vs 400, aliases and batch lookups.
Published · Updated · By Omar Eldeeb
To look up a clinical trial by NCT number, call https://clinicaltrials.gov/api/v2/studies/{nctId}, for example /api/v2/studies/NCT04437511. It returns the full record as JSON, with no API key and no signup. Add fields= to return only what you need. An unknown NCT number returns HTTP 404, and a malformed one returns HTTP 400.
This page covers the single-study endpoint of the ClinicalTrials.gov v2 API: the NCT number format, the response, the errors, aliases, and how to fetch many trials at once. Everything below was run against the live API on 2026-10-14 (API version 2.0.5).
The NCT number format
An NCT number is the ClinicalTrials.gov Identifier. The API's own field definition (from /api/v2/studies/metadata) says: "The format is "NCT" followed by an 8-digit number." For example, NCT04437511 is Eli Lilly's TRAILBLAZER-ALZ 2 donanemab trial.
What the endpoint accepts, as tested:
| Input | Result |
|---|---|
NCT04437511 |
HTTP 200, the study |
nct04437511 (lowercase) |
HTTP 200, the same study |
NCT4437511 (leading zero dropped) |
HTTP 200, nctId comes back as NCT04437511 |
NCT99999999 (well-formed, doesn't exist) |
HTTP 404, text body NCT number NCT99999999 not found |
04437511 (no NCT prefix) |
HTTP 400, text body Parameter `nctId` has incorrect format |
NCT00009113 (an old alias) |
HTTP 301 redirect to NCT00010647 |
Error bodies are text/plain, not JSON, so check the status code before you call .json().
The endpoint
GET https://clinicaltrials.gov/api/v2/studies/{nctId}| Parameter | Values | Notes |
|---|---|---|
format |
json (default), csv, json.zip, fhir.json, ris |
fhir.json ignores fields. ris is a citation record. |
fields |
Field, piece or module names, comma- or pipe-separated | JSON uses names like NCTId, BriefTitle, ConditionsModule. CSV uses column names like NCT Number. |
markupFormat |
markdown (default), legacy |
Applies to JSON only. |
These are the parameters listed for /studies/{nctId} in the official OpenAPI spec.
What the JSON contains
The full record for NCT04437511 has five top-level keys: protocolSection, resultsSection, documentSection, derivedSection and hasResults. The spec also defines an annotationSection, which appears only on some studies.
| Section | What's in it |
|---|---|
protocolSection |
Registration data in modules: identificationModule, statusModule, sponsorCollaboratorsModule, descriptionModule, conditionsModule, designModule, armsInterventionsModule, outcomesModule, eligibilityModule, contactsLocationsModule, and more. |
resultsSection |
Posted results: participant flow, baseline, outcome measures, adverse events. Present only when hasResults is true. |
documentSection |
Uploaded protocol, statistical analysis plan and consent PDFs. |
derivedSection |
Fields ClinicalTrials.gov adds, such as MeSH terms for conditions and interventions. |
hasResults |
true when results are posted. |
A full record can be large (the API's /api/v2/stats/size reported an average of 17,304 bytes per study and a 99th percentile of 149,567 bytes), so use fields when you only need a few values.
curl: one study, a few fields
curl -s "https://clinicaltrials.gov/api/v2/studies/NCT04437511?fields=NCTId,BriefTitle,OverallStatus,Phase,LeadSponsorName,HasResults"Result:
{"protocolSection":{"identificationModule":{"nctId":"NCT04437511","briefTitle":"A Study of Donanemab (LY3002813) in Participants With Early Alzheimer's Disease (TRAILBLAZER-ALZ 2)"},"statusModule":{"overallStatus":"ACTIVE_NOT_RECRUITING"},"sponsorCollaboratorsModule":{"leadSponsor":{"name":"Eli Lilly and Company"}},"designModule":{"phases":["PHASE3"]}},"hasResults":true}Even with fields, the response keeps the nested path of each field. Phase comes back as designModule.phases, a list.
The same lookup as a one-row CSV:
curl -s "https://clinicaltrials.gov/api/v2/studies/NCT04437511?format=csv&fields=NCT%20Number,Study%20Title,Study%20Status,Phases,Sponsor"NCT Number,Study Title,Study Status,Sponsor,Phases
NCT04437511,A Study of Donanemab (LY3002813) in Participants With Early Alzheimer's Disease (TRAILBLAZER-ALZ 2),ACTIVE_NOT_RECRUITING,Eli Lilly and Company,PHASE3The columns come back in the API's order, not the order you asked for.
Python: a lookup that handles every case
import re
import requests
BASE = "https://clinicaltrials.gov/api/v2/studies"
def normalize_nct(raw):
"""'nct4437511', ' NCT04437511 ' -> 'NCT04437511' (NCT + 8 digits)."""
s = raw.strip().upper().replace(" ", "")
m = re.fullmatch(r"NCT(\d{1,8})", s)
if not m:
raise ValueError(f"not an NCT number: {raw!r}")
return "NCT" + m.group(1).zfill(8)
def get_study(nct_id, fields=None):
nct_id = normalize_nct(nct_id)
params = {"fields": ",".join(fields)} if fields else {}
r = requests.get(f"{BASE}/{nct_id}", params=params, timeout=30)
if r.status_code == 404:
return None # body is plain text: "NCT number ... not found"
r.raise_for_status()
# An old alias NCT ID answers 301 to the current record. The redirect drops
# the query string, so re-request the canonical ID to keep `fields`.
if r.history and fields:
canonical = r.url.rstrip("/").rsplit("/", 1)[-1]
r = requests.get(f"{BASE}/{canonical}", params=params, timeout=30)
r.raise_for_status()
return r.json()
def get_many(nct_ids, fields):
"""Up to 1,000 trials in one call with filter.ids."""
ids = [normalize_nct(x) for x in nct_ids]
r = requests.get(BASE, params={
"filter.ids": ",".join(ids),
"fields": ",".join(fields),
"pageSize": 1000,
}, timeout=60)
r.raise_for_status()
return r.json()["studies"]
if __name__ == "__main__":
FIELDS = ["NCTId", "BriefTitle", "OverallStatus", "Phase", "LeadSponsorName", "HasResults"]
s = get_study("nct4437511", FIELDS)
p = s["protocolSection"]
print(p["identificationModule"]["nctId"], "|", p["identificationModule"]["briefTitle"])
print(p["statusModule"]["overallStatus"], p["designModule"]["phases"],
p["sponsorCollaboratorsModule"]["leadSponsor"]["name"], "hasResults:", s["hasResults"])
print(get_study("NCT99999999")) # -> None
alias = get_study("NCT00009113", ["NCTId", "NCTIdAlias", "BriefTitle"])
print(alias["protocolSection"]["identificationModule"])
for st in get_many(["NCT04437511", "NCT04368728", "NCT00010647"], ["NCTId", "OverallStatus"]):
p = st["protocolSection"]
print(p["identificationModule"]["nctId"], p["statusModule"]["overallStatus"])Output on 2026-10-14:
NCT04437511 | A Study of Donanemab (LY3002813) in Participants With Early Alzheimer's Disease (TRAILBLAZER-ALZ 2)
ACTIVE_NOT_RECRUITING ['PHASE3'] Eli Lilly and Company hasResults: True
None
{'nctId': 'NCT00010647', 'nctIdAliases': ['NCT00009113'], 'briefTitle': 'Cost-Effectiveness of and Long-Term Outcomes Following Acupuncture Treatment for Osteoarthritis of the Knee'}
NCT00010647 COMPLETED
NCT04437511 ACTIVE_NOT_RECRUITING
NCT04368728 COMPLETEDDetails that trip people up
- Aliases redirect and drop your query string. Some studies were registered twice and merged. The old ID is kept in
nctIdAliases, and requesting it returns a 301 to the current ID. In the test above, the redirect target had no?fields=on it, so following it blindly returns the full record. On 2026-10-14 there were 3,164 studies with at least one alias. - Batch lookups don't keep your order.
filter.idsreturned the three studies in the API's order, not the order I sent. Index the results bynctId. - NCT IDs in a search query switch filters off. The
/studiesdocs say: "If any query parameter contains only NCT IDs (comma- and/or space-separated), filters are ignored." I sentquery.term=NCT04437511 NCT04368728withfilter.overallStatus=RECRUITINGand got both studies back, although neither is recruiting. Usefilter.idswhen you want the filters applied. - Document PDFs live on a CDN.
documentSection.largeDocumentModule.largeDocslists file names such asProt_000.pdf. For NCT04437511 the file was athttps://cdn.clinicaltrials.gov/large-docs/11/NCT04437511/Prot_000.pdf, where11is the last two digits of the NCT number. That path pattern is what I observed; it isn't documented in the API spec. - There is no history endpoint. The v2 API returns only the current version. Past versions are on the website's History of Changes tab (
https://clinicaltrials.gov/study/NCT04437511?tab=history). The internal endpoint the site appears to use,/api/int/studies/{nctId}/history, returned HTTP 403 to a script.
Related guides for this API
- ClinicalTrials.gov API v2 query parameters: the full reference, for searching rather than fetching one ID.
- Get completed trial results with the ClinicalTrials.gov API, which reads the
resultsSectionthis page skipped. - Migrating from the classic ClinicalTrials.gov API to v2, if your code still calls
/api/query/full_studies. - Export ClinicalTrials.gov search results to CSV in Python.
Do it without code
The free ClinicalTrials.gov search tool calls this same API live from your browser. Search by condition or drug and filter by phase and status. It shows each study's NCT ID, title, status, phase and lead sponsor. No login, no key.
At scale
To look up hundreds of NCT numbers and join the trials with FDA approvals, device clearances, adverse events and recalls, use the Clinical Trials & FDA Pipeline actor on Apify. Its trial_details mode takes a list of NCT IDs (nctIds) and returns one flat row per trial: status, phase, enrollment, primary and secondary endpoints, eligibility criteria, sponsors and locations. It exports JSON, CSV or Excel. It's free to start, then pay-as-you-go.
Disclosure: I build the datatooly tool and the Apify actor. Everything above works with the public API alone.
Sources: ClinicalTrials.gov API (interactive OpenAPI docs), Study data structure, and live responses from clinicaltrials.gov/api/v2 on 2026-10-14.
Quick answers
How do I look up a clinical trial by NCT number?
Call https://clinicaltrials.gov/api/v2/studies/NCT04437511 (swap in your NCT number). It returns the full study record as JSON, with no API key. Add fields=NCTId,BriefTitle,OverallStatus to get only the fields you need. On the website, the same record is at https://clinicaltrials.gov/study/NCT04437511.
What is the clinicaltrials.gov API endpoint for a single study (api/v2/studies/{nctId})?
GET https://clinicaltrials.gov/api/v2/studies/{nctId}. It accepts format (json, csv, json.zip, fhir.json, ris), fields and markupFormat. An unknown NCT number returns HTTP 404 with NCT number ... not found; a malformed one returns HTTP 400 with the text: Parameter nctId has incorrect format. Checked live on 2026-10-14.
What is a ClinicalTrials.gov identifier (NCT number)?
It is the unique ID ClinicalTrials.gov gives a study when it is first registered. The API's own field definition says the format is "NCT" followed by an 8-digit number, for example NCT04437511. It is also called the ClinicalTrials.gov Identifier or NCT ID.
Is there a ClinicalTrials.gov API endpoint for historical versions of a study?
Not in the public v2 API. Its OpenAPI spec (version 2.0.5) lists no history endpoint, and /api/v2/studies/{nctId}/history returned HTTP 404 on 2026-10-14. Earlier versions are on the website's History of Changes tab: https://clinicaltrials.gov/study/NCT04437511?tab=history.