mcpField
F
fda-database

fda-database

github.com/jaechang-hits数据与数据库38470 次安装1 周前更新
代码仓库

关于 fda-database

fda-database 是用于查询 openFDA 公共监管数据的技能,面向药物安全监测、标签检索和产品调查。它通过 REST API 提供 FAERS 不良事件、药品标签、NDC 产品及召回数据的搜索与聚合。

主要功能

  1. 01搜索 FAERS 药物不良事件报告
  2. 02按药物汇总常见不良事件
  3. 03检索药品标签及警告信息
  4. 04按 NDC 或成分查询药品产品
  5. 05查找药品召回和执法行动

适用场景

  1. 01比较同类药物的不良事件报告
  2. 02提取药品适应症、禁忌和警告
  3. 03核查药品召回及执法记录
  4. 04构建药物安全信号监测流程

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jaechang-hits/sciagent-skills --skill fda-database

安装后在 Claude Code 中输入/fda-database确认已加载。

详细介绍

openFDA 药品与不良事件数据库

概述

openFDA 通过简单的 REST API 提供 FDA 监管数据的公开访问。主要数据集包括 FDA 不良事件报告系统(FAERS),其中有 2,000 多万份不良事件报告;药品产品标签(NDC、SPL);药品批准信息(Drugs@FDA);医疗器械报告;以及召回执法行动。该 API 支持使用 Elasticsearch 风格语法进行全文搜索和结构化查询。

适用场景

  • 检索某种药品的不良事件报告,以评估安全信号和副作用特征
  • 查询 FAERS,进行不成比例分析(比较不同药品的不良事件特征)
  • 按药品名称或 NDC 查找官方药品标签(适应症、禁忌症、警告、剂量)
  • 按药品名称或公司搜索药品召回和执法行动
  • 查找所有含有指定活性成分的上市产品
  • 构建药物警戒工作流,通过公开监管数据监测药品安全信号
  • 如需临床试验疗效数据,请使用 clinicaltrials-database-search;如需药品结构/靶点信息,请使用 drugbank-database-access

前提条件

  • Python 包:requests、pandas
  • 数据要求:药品名称、活性成分、MedDRA 术语、NDC 代码
  • 环境:需要互联网连接;基本使用无需身份验证
  • 速率限制:不使用 API 密钥时每天 1000 次请求;使用 https://open.fda.gov/apis/authentication/ 提供的免费 API 密钥时每天 120,000 次请求
pip install requests pandas

快速开始

import requests

BASE = "https://api.fda.gov/drug"
# Optional: add api_key parameter for higher rate limits

# Find adverse events for aspirin
r = requests.get(
    f"{BASE}/event.json",
    params={
        "search": 'patient.drug.medicinalproduct:"aspirin"',
        "count": "patient.reaction.reactionmeddrapt.exact",
        "limit": 10
    }
)
r.raise_for_status()
data = r.json()
print("Top adverse reactions for aspirin:")
for item in data["results"][:5]:
    print(f"  {item['term']:40s} count={item['count']}")

核心 API

查询 1:不良事件报告搜索(FAERS)

在 FDA 不良事件报告系统中搜索药品与事件的关联。

import requests, pandas as pd

BASE = "https://api.fda.gov/drug"

def faers_search(drug_name, limit=100):
    """Search FAERS for adverse event reports mentioning a drug."""
    r = requests.get(f"{BASE}/event.json",
                     params={"search": f'patient.drug.medicinalproduct:"{drug_name}"',
                             "limit": limit})
    r.raise_for_status()
    return r.json()

data = faers_search("warfarin", limit=5)
total = data["meta"]["results"]["total"]
print(f"Total FAERS reports for warfarin: {total:,}")

# Show first report summary
report = data["results"][0]
print(f"\nReport {report['safetyreportid']}:")
print(f"  Date     : {report.get('receivedate', 'n/a')}")
print(f"  Serious  : {report.get('serious', 'n/a')}")
drugs = [d.get("medicinalproduct", "n/a") for d in report.get("patient", {}).get("drug", [])]
print(f"  Drugs    : {drugs[:5]}")
reactions = [r.get("reactionmeddrapt", "n/a") for r in report.get("patient", {}).get("reaction", [])]
print(f"  Reactions: {reactions[:5]}")
查询 2:统计某种药品的高频不良事件

使用 count 参数汇总不良事件术语。

import requests, pandas as pd

BASE = "https://api.fda.gov/drug"

def top_adverse_events(drug_name, limit=20):
    """Get the most frequently reported adverse events for a drug."""
    r = requests.get(f"{BASE}/event.json",
                     params={
                         "search": f'patient.drug.medicinalproduct:"{drug_name}"',
                         "count": "patient.reaction.reactionmeddrapt.exact",
                         "limit": limit
                     })
    r.raise_for_status()
    results = r.json()["results"]
    return pd.DataFrame(results).rename(columns={"term": "reaction", "count": "reports"})

df_atorvastatin = top_adverse_events("atorvastatin", limit=15)
print("Top adverse events for atorvastatin:")
print(df_atorvastatin.head(10).to_string(index=False))
df_atorvastatin.to_csv("atorvastatin_adverse_events.csv", index=False)
# Compare two drugs: adverse event profile overlap
df_drug1 = top_adverse_events("simvastatin", limit=20)
df_drug2 = top_adverse_events("atorvastatin", limit=20)

common = set(df_drug1["reaction"]) & set(df_drug2["reaction"])
print(f"\nCommon adverse events (simvastatin ∩ atorvastatin): {len(common)}")
print("Shared reactions:", list(common)[:10])
查询 3:药品标签搜索

检索官方药品标签(适应症、警告、剂量、禁忌症)。

import requests

BASE = "https://api.fda.gov/drug"

def get_label(drug_name):
    """Retrieve FDA drug label by brand or generic name."""
    r = requests.get(f"{BASE}/label.json",
                     params={"search": f'openfda.brand_name:"{drug_name}"',
                             "limit": 1})
    if r.status_code == 404:
        r = requests.get(f"{BASE}/label.json",
                         params={"search": f'openfda.generic_name:"{drug_name}"',
                                 "limit": 1})
    r.raise_for_status()
    results = r.json()["results"]
    return results[0] if results else None

label = get_label("Lipitor")
if label:
    print(f"Brand name  : {label.get('openfda', {}).get('brand_name', ['n/a'])[0]}")
    print(f"Generic name: {label.get('openfda', {}).get('generic_name', ['n/a'])[0]}")
    print(f"Manufacturer: {label.get('openfda', {}).get('manufacturer_name', ['n/a'])[0]}")
    indications = label.get("indications_and_usage", ["n/a"])[0]
    print(f"\nIndications (first 300 chars):\n{indications[:300]}...")
查询 4:按 NDC 查询药品产品

按国家药品代码检索上市产品信息。

import requests, pandas as pd

BASE = "https://api.fda.gov/drug"

def ndc_search(ndc_or_name, limit=10):
    """Search NDC directory for drug product information."""
    # Search by product name or NDC
    r = requests.get(f"{BASE}/ndc.json",
                     params={"search": f'generic_name:"{ndc_or_name}"',
                             "limit": limit})
    r.raise_for_status()
    return r.json()

data = ndc_search("metformin", limit=10)
total = data["meta"]["results"]["total"]
print(f"Metformin products: {total}")

rows = []
for prod in data["results"]:
    rows.append({
        "product_ndc": prod.get("product_ndc"),
        "brand_name": prod.get("brand_name"),
        "generic_name": prod.get("generic_name"),
        "dosage_form": prod.get("dosage_form"),
        "route": ", ".join(prod.get("route", [])),
        "labeler": prod.get("labeler_name"),
    })
df = pd.DataFrame(rows)
print(df.to_string(index=False))
查询 5:药品召回搜索

按药品名称或公司搜索 FDA 执法行动和药品召回。

import requests, pandas as pd

BASE = "https://api.fda.gov/drug"

def drug_recalls(drug_name, limit=20):
    """Find FDA drug recalls for a given drug name."""
    r = requests.get(f"{BASE}/enforcement.json",
                     params={
                         "search": f'product_description:"{drug_name}"',
                         "limit": limit
                     })
    if r.status_code == 404:
        return pd.DataFrame()
    r.raise_for_status()
    results = r.json()["results"]
    return pd.DataFrame([{
        "recalling_firm": rec.get("recalling_firm"),
        "product": rec.get("product_description", "")[:80],
        "reason": rec.get("reason_for_recall", "")[:100],
        "classification": rec.get("classification"),
        "recall_date": rec.get("recall_initiation_date"),
        "status": rec.get("status"),
    } for rec in results])

recalls = drug_recalls("metformin", limit=5)
print(f"Metformin recalls: {len(recalls)}")
if not recalls.empty:
    print(recalls[["recalling_firm", "classification", "recall_date", "status"]].to_string(index=False))
查询 6:跨产品搜索活性成分

查找所有含有特定活性成分的药品产品。

import requests, pandas as pd

BASE = "https://api.fda.gov/drug"

def products_by_ingredient(ingredient, limit=50):
    """Find all FDA-listed products with a given active ingredient."""
    r = requests.get(f"{BASE}/ndc.json",
                     params={
                         "search": f'active_ingredients.name:"{ingredient}"',
                         "limit": limit
                     })
    r.raise_for_status()
    data = r.json()
    print(f"Total products with {ingredient}: {data['meta']['results']['total']}")

    rows = []
    for prod in data["results"]:
        for ai in prod.get("active_ingredients", []):
            if ingredient.lower() in ai.get("name", "").lower():
                rows.append({
                    "brand": prod.get("brand_name"),
                    "generic": prod.get("generic_name"),
                    "strength": ai.get("strength"),
                    "dosage_form": prod.get("dosage_form"),
                    "route": ", ".join(prod.get("route", [])),
                })
    return pd.DataFrame(rows)

df = products_by_ingredient("metformin hydrochloride")
print(df.drop_duplicates(subset=["generic", "strength", "dosage_form"]).head(10).to_string(index=False))

核心概念

openFDA API 端点
端点数据集主要用途
/drug/event.jsonFAERS(不良事件)药物警戒、安全信号
/drug/label.json结构化产品标签适应症、警告、剂量
/drug/ndc.jsonNDC 目录上市产品、规格
/drug/enforcement.json召回与执法药品召回、市场撤回
/device/event.jsonMAUDE(器械事件)医疗器械不良事件
查询语法

openFDA 使用 Elasticsearch 风格的查询。使用 field:"exact phrase" 进行精确匹配,使用 field:term 进行模糊匹配,使用 +field1:"A" +field2:"B" 表示 AND 逻辑。使用 count 参数进行汇总(相当于 GROUP BY)。使用 limit(1–1000)进行分页,并通过 skip 设置偏移量。

常见工作流

工作流 1:药品安全信号分析

目标:比较同一治疗类别中多种药品的不良事件频率,以识别差异化的安全特征。

import requests, pandas as pd, time

BASE = "https://api.fda.gov/drug"

drugs = ["atorvastatin", "simvastatin", "rosuvastatin"]

def count_reactions(drug, limit=20):
    r = requests.get(f"{BASE}/event.json",
                     params={"search": f'patient.drug.medicinalproduct:"{drug}"',
                             "count": "patient.reaction.reactionmeddrapt.exact",
                             "limit": limit})
    if r.status_code != 200:
        return pd.Series(dtype=float, name=drug)
    df = pd.DataFrame(r.json()["results"])
    df.columns = ["reaction", drug]
    return df.set_index("reaction")[drug]

series_list = []
for drug in drugs:
    s = count_reactions(drug, limit=20)
    series_list.append(s)
    time.sleep(0.5)

comparison = pd.concat(series_list, axis=1).fillna(0)
comparison = comparison.sort_values(drugs[0], ascending=False)
print("Adverse event count comparison (statins):")
print(comparison.head(10).to_string())
comparison.to_csv("statin_safety_comparison.csv")
工作流 2:药品标签信息提取器

目标:提取多种药品的适应症、禁忌症和警告,并保存为 CSV。

import requests, pandas as pd, time, re

BASE = "https://api.fda.gov/drug"

def get_label_sections(drug_name):
    r = requests.get(f"{BASE}/label.json",
                     params={"search": f'openfda.generic_name:"{drug_name}"', "limit": 1})
    if r.status_code != 200 or not r.json()["results"]:
        return None
    label = r.json()["results"][0]

    def clean(field):
        text = " ".join(label.get(field, [""]))
        return re.sub(r"\s+", " ", text).strip()[:500]

    return {
        "drug": drug_name,
        "indications": clean("indications_and_usage"),
        "contraindications": clean("contraindications"),
        "warnings": clean("warnings_and_cautions") or clean("warnings"),
    }

drugs = ["metformin", "atorvastatin", "lisinopril", "omeprazole"]
rows = []
for drug in drugs:
    info = get_label_sections(drug)
    if info:
        rows.append(info)
    time.sleep(0.4)

df = pd.DataFrame(rows)
df.to_csv("drug_labels.csv", index=False)
print(df[["drug", "indications"]].to_string(index=False))

关键参数

参数模块默认值范围 / 选项作用
search所有端点—Elasticsearch 语法筛选查询
count所有端点—字段名称 + .exact按字段值聚合/计数
limit所有端点11–1000每次请求的结果数
skip所有端点0整数分页偏移量
api_key所有端点—API 密钥字符串将速率限制提高到每天 120K 次
.exact 后缀count 字段—附加在字段名称后精确字符串匹配,而非分词匹配

最佳实践

  1. 获取免费 API 密钥:在 https://open.fda.gov/apis/authentication/ 注册即可立即获取密钥,并将每日请求限额从 1,000 次提高到 120,000 次;生产环境中必不可少。

  2. 搜索药品名称时使用 .exact:patient.drug.medicinalproduct.exact(而不是 .medicinalproduct)可进行精确短语匹配,避免部分匹配导致计数虚高。

  3. 规范化药品名称:FAERS 报告者使用多种名称拼写(例如,“Lipitor”、“atorvastatin calcium”、“ATORVASTATIN”)。可搜索多个名称变体,或使用通用成分字段以确保覆盖一致。

  4. 谨慎解读 FAERS 计数:报告数量不等于发生率。FAERS 为自愿报告,且存在报告偏倚;计数较高可能反映市场规模或媒体关注度,而非风险更高。

  5. 对大型结果集分页:limit 最大值为 1000;使用 skip 对大型结果集分页(meta.results 中的 total)。

常见示例

示例:统计某种药物的 FAERS 报告总数

适用场景:快速查看某种药物的不良事件报告总量。

import requests

drug = "ibuprofen"
r = requests.get("https://api.fda.gov/drug/event.json",
                 params={"search": f'patient.drug.medicinalproduct.exact:"{drug}"',
                         "limit": 1})
total = r.json()["meta"]["results"]["total"]
print(f"Total FAERS reports for {drug}: {total:,}")
示例:仅查找严重不良事件

适用场景:筛选 FAERS 中被归类为严重事件(死亡、住院、残疾)的报告。

import requests, pandas as pd

r = requests.get("https://api.fda.gov/drug/event.json",
                 params={
                     "search": 'patient.drug.medicinalproduct:"warfarin" AND serious:1',
                     "count": "patient.reaction.reactionmeddrapt.exact",
                     "limit": 10
                 })
df = pd.DataFrame(r.json()["results"])
df.columns = ["reaction", "serious_reports"]
print(df.to_string(index=False))
示例:查询药品的上市批准状态

适用场景:确认药品是否获得 FDA NDA/ANDA 批准,并查找批准年份。

import requests

r = requests.get("https://api.fda.gov/drug/label.json",
                 params={"search": 'openfda.generic_name:"metformin"', "limit": 1})
label = r.json()["results"][0]
openfda = label.get("openfda", {})
print(f"Application numbers: {openfda.get('application_number', ['n/a'])}")
print(f"Product type: {openfda.get('product_type', ['n/a'])}")
print(f"NDA sponsor: {openfda.get('manufacturer_name', ['n/a'])}")

故障排除

问题原因解决方案
HTTP 404,并返回 {"error": {"code": "NOT_FOUND"}}查询无匹配结果检查药品名称拼写;尝试其他名称格式
HTTP 429 Too Many Requests超出速率限制注册获取 API 密钥;在请求之间添加 time.sleep(1)
计数结果与预期不符药品名称分词使用 .exact 后缀:medicinalproduct.exact,而非 medicinalproduct
标签搜索返回错误药品名称存在歧义添加 +openfda.product_type:"HUMAN PRESCRIPTION DRUG" 进行筛选
FAERS 报告中缺少字段自愿报告信息不完整使用 .get("field", "n/a") 检查字段是否存在
出现 skip + limit > 26000 错误分页限制openFDA 将分页上限设为 26,000 条记录;如需获取更多记录的聚合结果,请使用 count 端点

相关技能

  • clinicaltrials-database-search — 查询通过 openFDA 识别的药品临床试验数据
  • drugbank-database-access — 药物结构、靶点和相互作用,用于辅助理解 FDA 数据
  • chembl-database-bioactivity — FAERS 数据库中药品的临床前生物活性数据
  • string-database-ppi — 通过不良事件分析发现的药物靶点的蛋白质相互作用

参考资料

评论

支持 Markdown