clinicaltrials-database-search について
clinicaltrials-database-searchは、ClinicalTrials.gov API v2で世界の臨床試験データを検索・取得するスキルです。研究者や臨床チーム、レビュー作業向けに、条件検索、試験詳細の取得、CSV出力を提供します。
主な機能
- 01疾患、介入、地域による検索
- 02スポンサー、状態、フェーズによる絞り込み
- 03NCT IDによる試験詳細の取得
- 04大量の検索結果のページング
- 05試験データのCSV出力
活用シーン
- 01疾患に関する募集中試験の検索
- 02特定薬剤の臨床研究の特定
- 03地域内で紹介可能な試験の調査
- 04システマティックレビュー用データの集約
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ jaechang-hits/ sciagent-skills --skill clinicaltrials-database-searchインストール後、Claude Code で/clinicaltrials-database-searchを入力して読み込まれたか確認します。
詳細
ClinicalTrials.gov データベース — 臨床試験の検索
概要
ClinicalTrials.gov API v2(公開、認証不要)に問い合わせ、世界中の臨床試験データを検索・取得します。疾患、介入、場所、スポンサー、ステータスでの検索、NCT ID による詳細な試験情報の取得、大規模な検索結果のページネーション、CSV へのエクスポートに対応しています。
使用する場面
- 特定の疾患や病気を対象に、参加者を募集している臨床試験を検索する
- 特定の薬剤、医療機器、介入を試験する臨床試験を探す
- 患者紹介のため、特定の地域で実施されている臨床試験を探す
- スポンサーや機関が実施する臨床試験のポートフォリオを把握する
- 特定の試験の詳細な適格基準、評価項目、連絡先を取得する
- 治療領域全体の臨床試験の傾向(フェーズ、登録数、期間)を分析する
- システマティックレビューやメタアナリシス用に試験データをエクスポートする
- 試験のステータス変更や結果の掲載状況を監視する
- 化合物の生物活性データには代わりに chembl-database-bioactivity を、公開済み文献には pubmed-database を使用してください
前提条件
uv pip install requests pandas
API の詳細:
- ベース URL:
https://clinicaltrials.gov/api/v2 - 認証:不要(公開 API)
- レート制限:IP あたり約 50 リクエスト/分
- レスポンス形式:JSON(デフォルト)、CSV
- 最大ページサイズ:1 リクエストあたり 1000 件の試験
- 日付形式:ISO 8601。テキストフィールドでは CommonMark Markdown を使用
クイックスタート
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}")
主な概念
レスポンスデータの構造
ClinicalTrials.gov は、深くネストされた JSON を返します。主なデータへのアクセスパスは次のとおりです。
| データ | パス |
|---|---|
| NCT ID | study['protocolSection']['identificationModule']['nctId'] |
| タイトル | study['protocolSection']['identificationModule']['briefTitle'] |
| ステータス | study['protocolSection']['statusModule']['overallStatus'] |
| フェーズ | study['protocolSection']['designModule']['phases'] |
| 登録数 | study['protocolSection']['designModule']['enrollmentInfo']['count'] |
| 適格基準 | study['protocolSection']['eligibilityModule'] |
| 実施施設 | study['protocolSection']['contactsLocationsModule']['locations'] |
| 介入 | study['protocolSection']['armsInterventionsModule']['interventions'] |
| 結果 | study.get('resultsSection')(結果が掲載されていない場合は None) |
試験ステータスの値
| ステータス | 説明 |
|---|---|
RECRUITING | 現在、参加者を募集しています |
NOT_YET_RECRUITING | 承認済みですが、まだ募集を開始していません |
ENROLLING_BY_INVITATION | 招待者のみを登録しています |
ACTIVE_NOT_RECRUITING | 試験は進行中ですが、登録は終了しています |
SUSPENDED | 一時的に中断しています |
TERMINATED | 予定より早く中止されました |
COMPLETED | 試験が終了しました |
WITHDRAWN | 登録開始前に取り下げられました |
試験フェーズの値
| フェーズ | 説明 |
|---|---|
EARLY_PHASE1 | 初期フェーズ 1(旧フェーズ 0) |
PHASE1 | フェーズ 1 — 安全性と用量 |
PHASE2 | フェーズ 2 — 有効性と副作用 |
PHASE3 | フェーズ 3 — 大規模な有効性評価 |
PHASE4 | フェーズ 4 — 市販後調査 |
NA | 該当なし(医薬品以外の試験) |
クエリパラメーターのリファレンス
| パラメーター | 型 | 説明 | 例 |
|---|---|---|---|
query.cond | string | 疾患・病気 | lung cancer |
query.intr | string | 介入・薬剤 | Pembrolizumab |
query.locn | string | 地理的な場所 | New York |
query.spons | string | スポンサー名 | National Cancer Institute |
query.term | string | 一般的な全文検索 | immunotherapy |
filter.overallStatus | string | ステータスフィルター(カンマ区切り) | RECRUITING,COMPLETED |
filter.phase | string | フェーズフィルター | PHASE2,PHASE3 |
filter.ids | string | NCT ID フィルター | NCT04852770 |
sort | string | 並べ替え順 | LastUpdatePostDate:desc |
pageSize | int | 1ページあたりの結果数(最大 1000) | 100 |
pageToken | string | ページネーショントークン | (前回のレスポンスから取得) |
format | string | レスポンス形式 | json または csv |
並べ替えオプション:LastUpdatePostDate、EnrollmentCount、StartDate、StudyFirstPostDate。それぞれに :asc または :desc を指定できます。
コア API
1. 疾患で検索
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. 介入・薬剤で検索
# 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. 場所で検索
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. スポンサーで検索
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. 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. 大規模な結果セットのページネーション
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. CSV へのエクスポート
response = requests.get(f"{CT_API}/studies", params={
"query.cond": "heart disease",
"filter.overallStatus": "RECRUITING",
"format": "csv",
"pageSize": 1000
}, timeout=60)
with open("heart_disease_trials.csv", "w") as f:
f.write(response.text)
print("Exported to heart_disease_trials.csv")
よくあるワークフロー
ワークフロー 1:複数条件による試験の検索
import requests, time
CT_API = "https://clinicaltrials.gov/api/v2"
def ct_search(params):
response = requests.get(f"{CT_API}/studies", params=params, timeout=30)
response.raise_for_status()
return response.json()
# Step 1: Search with multiple filters
results = ct_search({
"query.cond": "lung cancer",
"query.intr": "immunotherapy",
"query.locn": "California",
"filter.overallStatus": "RECRUITING,NOT_YET_RECRUITING",
"pageSize": 100,
"sort": "LastUpdatePostDate:desc"
})
print(f"Total matches: {results['totalCount']}")
# Step 2: Filter by phase
phase23 = [
s for s in results['studies']
if any(p in ['PHASE2', 'PHASE3']
for p in s['protocolSection'].get('designModule', {}).get('phases', []))
]
print(f"Phase 2/3 trials: {len(phase23)}")
# Step 3: Extract summaries
for study in phase23[:5]:
proto = study['protocolSection']
nct = proto['identificationModule']['nctId']
title = proto['identificationModule']['briefTitle']
enrollment = proto.get('designModule', {}).get('enrollmentInfo', {}).get('count', 'N/A')
print(f" {nct}: {title} (n={enrollment})")
ワークフロー 2:完了した試験の結果分析
# Step 1: Find completed trials with posted results
results = ct_search({
"query.cond": "alzheimer disease",
"filter.overallStatus": "COMPLETED",
"pageSize": 100,
"sort": "LastUpdatePostDate:desc"
})
with_results = [s for s in results['studies'] if s.get('hasResults', False)]
print(f"Completed with results: {len(with_results)} / {len(results['studies'])}")
# Step 2: Get detailed results for top trial
if with_results:
nct = with_results[0]['protocolSection']['identificationModule']['nctId']
detail = requests.get(f"{CT_API}/studies/{nct}", timeout=30).json()
if 'resultsSection' in detail:
outcomes = detail['resultsSection'].get('outcomeMeasuresModule', {})
measures = outcomes.get('outcomeMeasures', [])
for m in measures[:3]:
print(f" Outcome: {m.get('title')}")
print(f" Type: {m.get('type')}")
ワークフロー 3:スポンサーのポートフォリオ比較
sponsors = ["Pfizer", "Novartis", "Roche"]
for sponsor in sponsors:
results = ct_search({
"query.spons": sponsor,
"filter.overallStatus": "RECRUITING",
"pageSize": 1
})
print(f"{sponsor}: {results['totalCount']} recruiting trials")
time.sleep(1.5)
よく使うレシピ
レシピ:レート制限に対応した一括検索
def ct_search_with_retry(params, max_retries=3):
for attempt in range(max_retries):
try:
response = requests.get(f"{CT_API}/studies", params=params, timeout=30)
response.raise_for_status()
return response.json()
except requests.exceptions.HTTPError as e:
if e.response.status_code == 429:
wait = 60
print(f"Rate limited. Waiting {wait}s...")
time.sleep(wait)
else:
raise
except requests.exceptions.RequestException:
if attempt == max_retries - 1:
raise
time.sleep(2 ** attempt)
raise Exception("Max retries exceeded")
レシピ:試験の概要を抽出
def extract_summary(study):
proto = study.get('protocolSection', {})
ident = proto.get('identificationModule', {})
status = proto.get('statusModule', {})
design = proto.get('designModule', {})
return {
'nct_id': ident.get('nctId'),
'title': ident.get('officialTitle') or ident.get('briefTitle'),
'status': status.get('overallStatus'),
'phases': design.get('phases', []),
'enrollment': design.get('enrollmentInfo', {}).get('count'),
'last_update': status.get('lastUpdatePostDateStruct', {}).get('date')
}
# Usage
for study in results['studies'][:3]:
s = extract_summary(study)
print(f"{s['nct_id']}: {s['status']} | Phase: {s['phases']} | n={s['enrollment']}")
レシピ:安全なフィールドの参照
def safe_get(study, *keys, default='N/A'):
"""Navigate nested study JSON safely."""
current = study
for key in keys:
if isinstance(current, dict):
current = current.get(key)
else:
return default
if current is None:
return default
return current
# Usage — handles missing fields gracefully
nct = safe_get(study, 'protocolSection', 'identificationModule', 'nctId')
phases = safe_get(study, 'protocolSection', 'designModule', 'phases', default=[])
enrollment = safe_get(study, 'protocolSection', 'designModule', 'enrollmentInfo', 'count')
主なパラメーター
| パラメーター | エンドポイント | デフォルト | 説明 |
|---|---|---|---|
query.cond | 検索 | — | 疾患・病気の検索語 |
query.intr | 検索 | — | 介入・薬剤の検索語 |
query.locn | 検索 | — | 地理的な場所のフィルター |
query.spons | 検索 | — | スポンサー・組織のフィルター |
query.term | 検索 | — | 一般的な全文検索 |
filter.overallStatus | 検索 | all | カンマ区切りのステータス値 |
filter.phase | 検索 | all | カンマ区切りのフェーズ値 |
pageSize | 検索 | 10 | 1ページあたりの結果数(最大 1000) |
sort | 検索 | relevance | {field}:{asc|desc} |
format | 両方 | json | json または csv |
timeout | (クライアント) | 30s | requests の呼び出しで設定 |
トラブルシューティング
| 問題 | 原因 | 解決策 |
|---|---|---|
| 429 Too Many Requests | レート制限を超過しました(約 50 回/分) | 60 秒待機します。pageSize=1000 を指定し、指数バックオフを実装します |
| 空の studies 配列 | フィルターに一致する試験がありません | 検索範囲を広げます(ステータス・フェーズのフィルターを削除します)。スペルを確認します |
| 400 Bad Request | パラメーター値が無効です | ステータス・フェーズの値が列挙型と完全に一致していることを確認します(例:recruiting ではなく RECRUITING) |
resultsSection がない | 試験に投稿済みの結果がありません | 結果にアクセスする前に study['hasResults'] を確認します |
| ネストされたフィールドで KeyError | すべての試験にすべてのモジュールがあるとは限りません | デフォルト値を指定して .get() を使うか、safe_get ヘルパーを使います(レシピを参照) |
| ページネーションが早期に終了する | nextPageToken がありません | すべての結果を取得済みです。totalCount と収集した件数を確認します |
| CSV 形式が JSON と異なる | フィールド構造が異なります | CSV ではネストされた構造がフラット化されます。プログラムからアクセスする場合は JSON を使います |
| 大規模なエクスポートでタイムアウトする | 多数の結果を含む CSV | タイムアウトを延長します。または pageSize=1000 を指定してページネーションします |
ベストプラクティス
- 一括取得では最大ページサイズ(1000)を使用して、レート制限に対するリクエスト数を最小限に抑えます
resultsSectionにアクセスする前に、必ずhasResultsを確認します。ほとんどの試験には公開済みの結果がありません.get()チェーンで安全に移動します。すべての試験ですべてのモジュール(特にcontactsLocationsModule、armsInterventionsModule)にデータが含まれているとは限りません- 複数のステータス値はカンマ区切りで指定します(例:
RECRUITING,NOT_YET_RECRUITING)。ステータスごとに個別のリクエストを送信しないでください - デフォルトで
sort=LastUpdatePostDate:descを使用します。これにより、最終更新日が新しい試験から順に返されます - 日付の解釈:
lastUpdatePostDateStruct.dateは ISO 8601 文字列です。typeフィールドはACTUALとESTIMATEDのどちらかを示します
関連スキル
pubmed-database— 試験登録データを補完する、公開済み文献の検索chembl-database-bioactivity— 調査中の医薬品の化合物生物活性データbioservices-multi-database— 統合 Python インターフェースによる別のデータベースアクセス方法
参考資料
- ClinicalTrials.gov API ドキュメント:https://clinicaltrials.gov/data-api/api
- API 移行ガイド(v1→v2):https://clinicaltrials.gov/data-api/about-api/api-migration
- ClinicalTrials.gov ホームページ:https://clinicaltrials.gov/
- OpenAPI 仕様:https://clinicaltrials.gov/data-api/about-api/api-spec
同梱リソース
自己完結型のエントリーです。元の合計:866 行(SKILL.md 507 行 + api_reference.md 359 行)。スクリプト:216 行(query_clinicaltrials.py)。
元ファイルの扱い:
SKILL.md(507 行)→ コア API モジュール 1~7(condition、intervention、location、sponsor、details、pagination、CSV export)。「Core Capabilities」セクション 1~10 を統合:Search by Condition → Module 1、Search by Intervention → Module 2、Geographic Search → Module 3、Search by Sponsor → Module 4、Retrieve Detailed Study → Module 5、Pagination → Module 6、Data Export → Module 7、Combined Query → Workflow 1、Extract Summary → Recipe。「Resources」セクションの仮置き部分 → 削除し、内容をインラインに統合。ユースケース別の扱い:Patient Matching → When to Use の箇条書き + Workflow 1、Research Analysis → When to Use + Workflow 2、Drug Tracking → When to Use + Module 2、Geographic Search → Module 3、Sponsor Tracking → Module 4 + Workflow 3、Data Export → Module 7、Trial Monitoring → When to Use の箇条書き、Eligibility Screening → Module 5references/api_reference.md(359 行)→ 全内容をインラインに統合:エンドポイントパラメーター → Key Concepts の「Query Parameters Reference」テーブル、ステータス/フェーズの値 → Key Concepts のテーブル、レスポンス構造 → Key Concepts の「Response Data Structure」テーブル、HTTP エラーコード → Troubleshooting のテーブル、レート制限のガイダンス → Prerequisites + Best Practices、ユースケース → メインの SKILL.md の例と重複するため、Core API に統合。データ標準(ISO 8601、CommonMark)→ Prerequisites の注記。エラー処理パターン → Recipes の「Rate-Limited Bulk Search」scripts/query_clinicaltrials.py(216 行)→ ヘルパー関数のパターン:search_studies()→ Quick Start のct_search()ヘルパー、get_study_details()→ Module 5 にインラインで記載、search_with_all_results()→ Module 6 のページネーションパターン、extract_study_summary()→ Recipe の「Extract Study Summary」。薄いラッパーのショートカットを適用 — 各関数はrequests.get()を呼び出す薄いラッパーでした
保持率:元の866行(スクリプトを除く)のうち約465行(約54%)。
コメント
Markdown 対応