Skip to content
Security
Skill

/searching-clinicaltrials

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

BOOST
From plugin
openmed
5.7k74 skills
Install
$ npx -y skills add maziyarpanahi/openmed --skill searching-clinicaltrials --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/searching-clinicaltrials

Context preview

The summary Claude sees to decide when to auto-load this skill.

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

SKILL.md

searching-clinicaltrials.SKILL.md
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.

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:

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.

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:

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 an

Read more
Ships withopenmed

Local-first healthcare AI: clinical NER and HIPAA PII de-identification on hardware you control. 2,200+ medical models, 35 model-backed PII languages, and Python, MLX, Android and browser runtimes. Apache-2.0 SDK.

Get the whole plugin

Other skills on openmed.