Searches ClinicalTrials.gov for studies by condition, intervention, and recruitment status using the modern v2 REST API with cursor (pageToken) pagination. Use when the user wants to find trials for a diagnosis or drug, screen patients against open studies, build a trial-matching feature, or pull a trial corpus for analysis. Trigger keywords: clinical trial, ClinicalTrials.gov, NCT number, trial search, recruiting studies, eligibility, query.cond, query.intr, pageToken, v2 API. Pairs adjacent...
Scanned 9/12/2026
Install to Claude Code
npx -y skills add maziyarpanahi/openmed --skill searching-clinicaltrials --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Searching Clinicaltrials?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/maziyarpanahi-searching-clinicaltrials)More formats (shields.io, HTML) on the badges page.
---
name: searching-clinicaltrials
description: "Searches ClinicalTrials.gov for studies by condition, intervention, and recruitment status using the modern v2 REST API with cursor (pageToken) pagination. Use when the user wants to find trials for a diagnosis or drug, screen patients against open studies, build a trial-matching feature, or pull a trial corpus for analysis. Trigger keywords: clinical trial, ClinicalTrials.gov, NCT number, trial search, recruiting studies, eligibility, query.cond, query.intr, pageToken, v2 API. Pairs adjacent to OpenMed: take Disease/Pharmaceutical entities from openmed.analyze_text and turn them into query.cond / query.intr filters; the returned eligibility text feeds parsing-trial-eligibility. ClinicalTrials.gov API v2 is fully public — no API key, no license."
license: Apache-2.0
metadata:
project: OpenMed
category: research-genomics
pairs: adjacent
version: "1.0"
---
# Searching ClinicalTrials.gov (v2 REST API)
Query **ClinicalTrials.gov** — the U.S. registry of clinical studies — for trials
matching a condition, intervention, and recruitment status. This skill uses the
**modern v2 REST API** (`/api/v2/studies`), which returns structured JSON and
paginates with an opaque cursor (`pageToken`), not page numbers.
The v2 API is **fully public**: no API key, no registration, no license barrier.
The legacy v1/classic API and the older `query_term`-style endpoints are
deprecated — do not build on them.
## When to use
- OpenMed extracted a diagnosis ("metastatic colorectal cancer") or a drug
("pembrolizumab") and you want open trials for it.
- You are building a patient-to-trial matching feature and need candidate studies
before applying eligibility logic (`parsing-trial-eligibility`).
- You need a corpus of trial records (eligibility text, outcomes) to feed back
into `openmed.analyze_text` for biomedical NER.
If you already have an NCT number, fetch the single study directly
(`/api/v2/studies/NCT01234567`) instead of searching.
## Quick start (real v2 API call)
Base URL: `https://clinicaltrials.gov/api/v2`. No auth. JSON by default.
```python
import requests
BASE = "https://clinicaltrials.gov/api/v2"
def search_trials(condition: str, intervention: str | None = None,
status: str = "RECRUITING", page_size: int = 50) -> dict:
"""One page of studies for a condition (+ optional intervention)."""
params = {
"query.cond": condition, # condition / disease search
"filter.overallStatus": status, # comma-separated enum values
"pageSize": min(page_size, 1000), # max 1000; default 10
"countTotal": "true", # include totalCount on first page
"format": "json",
}
if intervention:
params["query.intr"] = intervention # drug / intervention search
r = requests.get(f"{BASE}/studies", params=params, timeout=30)
r.raise_for_status()
return r.json()
data = search_trials("breast cancer", intervention="trastuzumab")
print(data["totalCount"]) # total matches (first page only)
for study in data["studies"]:
ps = study["protocolSection"]
nct = ps["identificationModule"]["nctId"]
title = ps["identificationModule"]["briefTitle"]
print(nct, "-", title)
```
Equivalent cURL:
```bash
curl "https://clinicaltrials.gov/api/v2/studies?query.cond=breast+cancer\
&query.intr=trastuzumab&filter.overallStatus=RECRUITING&pageSize=50&format=json"
```
## Response shape
Top level: `studies` (array), `nextPageToken` (present only if more results),
and `totalCount` (only when `countTotal=true`, on the first page). Each study is
a `protocolSection` of typed modules:
| Field path | Meaning |
| --- | --- |
| `identificationModule.nctId` | `NCT........` study id |
| `identificationModule.briefTitle` | short title |
| `statusModule.overallStatus` | `RECRUITING`, `COMPLETED`, … |
| `conditionsModule.conditions` | list of condition strings |
| `armsInterventionsModule.interventions` | drugs / procedures |
| `eligibilityModule.eligibilityCriteria` | free-text inclusion/exclusion |
| `eligibilityModule.sex` / `minimumAge` / `maximumAge` | demographic gates |
| `contactsLocationsModule.locations` | recruiting sites |
## Cursor pagination
There are **no page numbers**. Loop until `nextPageToken` is absent. The token is
opaque — pass it back verbatim. Do not re-send `countTotal` after page 1.
```python
def iter_all(condition: str, status: str = "RECRUITING"):
params = {"query.cond": condition, "filter.overallStatus": status,
"pageSize": 1000, "format": "json"}
while True:
r = requests.get(f"{BASE}/studies", params=params, timeout=30)
r.raise_for_status()
page = r.json()
yield from page.get("studies", [])
token = page.get("nextPageToken")
if not token:
break
params["pageToken"] = token # cursor for the next page
```
### Trimming payloads
Default responses are large. Restrict to the fields you need with `fields` (dotted
paths or module names) to cut bandwidth:
```python
params["fields"] = ("NCTId,BriefTitle,OverallStatus,"
"Condition,EligibilityCriteria")
```
## Workflow
1. **Build the query from OpenMed facts.** Map extracted Disease spans →
`query.cond`; Pharmaceutical spans → `query.intr`. Free-text keywords go in
`query.term`. Combine status filters as `filter.overallStatus=RECRUITING,NOT_YET_RECRUITING`.
2. **Page through** with the cursor until `nextPageToken` is gone; cap total pulls.
3. **Persist** `nctId`, status, conditions, interventions, and the raw eligibility
text. Eligibility goes to `parsing-trial-eligibility`.
4. **Optionally re-NER** the eligibility / outcomes text with
`openmed.analyze_text` to structure inclusion criteria.
## Hand-off to / from OpenMed
- **From OpenMed → trial search.** `openmed.analyze_text(note, model_name="disease_detection_superclinical")`
yields Disease and Pharmaceutical entities. Use the surface forms (or a grounded
term from `coding-icd10` / `normalizing-rxnorm`) as `query.cond` / `query.intr`.
- **Trial text → OpenMed.** Feed `eligibilityModule.eligibilityCriteria` and brief
summaries back through `openmed.analyze_text` to extract conditions, meds, and
labs mentioned in the criteria. Then hand to `parsing-trial-eligibility` for
inclusion/exclusion matching against patient facts.
- Keep patient data local. The API call carries only the **query terms**
(condition/drug names), never the patient note or any PHI.
## Edge cases & gotchas
- **Synonyms & spelling.** The condition matcher is fuzzy but not infinite —
"MI" will not match "myocardial infarction". Normalize OpenMed output first
(ICD-10 / RxNorm) and consider issuing a few synonym variants.
- **Status enums are exact.** Valid values include `RECRUITING`,
`NOT_YET_RECRUITING`, `ENROLLING_BY_INVITATION`, `ACTIVE_NOT_RECRUITING`,
`COMPLETED`, `SUSPENDED`, `TERMINATED`, `WITHDRAWN`, `UNKNOWN`. Comma-separate;
do not lowercase.
- **`totalCount` is first-page only.** Request `countTotal=true` once; it is not
repeated on subsequent pages.
- **Page size cap is 1000.** Larger values are silently clamped.
- **Rate limits.** No key required, but throttle politely (a short sleep between
pages); aggressive scraping can be blocked. For bulk/offline work, consider the
full registry data dump rather than thousands of paged calls.
- **`pageToken` expires** if the underlying index shifts; restart the query if a
token is rejected.
- **Not medical advice.** A trial appearing in results does not mean the patient
qualifies — eligibility is decided downstream and reviewed by a clinician.
## Standards & references
- ClinicalTrials.gov API v2 — https://clinicaltrials.gov/data-api/api
- Study data structure (modules / field paths) —
https://clinicaltrials.gov/data-api/about-api/study-data-structure
- Search areas & query syntax —
https://clinicaltrials.gov/data-api/about-api/search-areas
- OpenAPI / interactive reference — https://clinicaltrials.gov/api/v2/
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!