Source: https://github.com/aipoch/medical-research-skills
When to Use
- Pharmacovigilance / safety signal screening when you need adverse event counts, common reactions, or serious-event rates for a drug.
- Medical device regulatory research when you need 510(k)/PMA context, device classification, UDI lookups, or device adverse events/recalls.
- Recall and enforcement monitoring when you need to track Class I/II/III recalls across drugs, devices, or foods.
- Substance identity resolution when you need UNII/CAS/name-based lookups and basic substance relationship/structure retrieval.
- Veterinary safety analysis when you need animal adverse events filtered by species/breed and product.
Key Features
- Unified Python interface (
FDAQuery) for multiple openFDA domains (drug, device, food, animalandveterinary, other).
- Convenience helpers for common tasks:
- Drug events, labels, recalls, shortages
- Device events, classification, 510(k), PMA, UDI
- Food events and recalls
- Animal/veterinary adverse events
- Substance (UNII/name) lookups
- Supports openFDA query patterns:
- Fielded search strings, date ranges, wildcards
- Aggregations via
count_by_field(...) (with .exact support)
- Pagination via
skip/limit and bulk retrieval via query_all(...)
- Operational safeguards:
- Optional API key support for higher daily limits
- Built-in caching (TTL) and rate limiting (as implemented in
scripts/fda_query.py)
- Basic error handling patterns
Additional endpoint notes and query syntax are typically documented in:
references/api_basics.md, references/drugs.md, references/devices.md, references/foods.md, references/animal_veterinary.md, references/other.md.
Dependencies
- Python 3.9+
- openFDA API access (public)
- Optional: openFDA API key (recommended for higher daily quota)
Package-level dependencies (e.g., requests) are defined by the repository implementation in scripts/fda_query.py. If you maintain this skill, pin them in requirements.txt (for example, requests==2.31.0) to ensure reproducibility.
Example Usage
The following example is designed to be runnable in a repository that contains scripts/fda_query.py and the FDAQuery class.
1) Set an API key (optional, recommended)
export FDA_API_KEY="your_key_here"
2) Run a complete script
import os
from datetime import datetime, timedelta
from scripts.fda_query import FDAQuery
def drug_safety_profile(fda: FDAQuery, drug_name: str):
# Total adverse events (meta.total)
events = fda.query_drug_events(drug_name, limit=1)
total = events.get("meta", {}).get("results", {}).get("total", 0)
# Top reactions (aggregation)
reactions = fda.count_by_field(
"drug",
"event",
search=f"patient.drug.medicinalproduct:*{drug_name}*",
field="patient.reaction.reactionmeddrapt",
exact=True,
)
top_reactions = reactions.get("results", [])[:10]
# Serious events
serious = fda.query(
"drug",
"event",
search=f"patient.drug.medicinalproduct:*{drug_name}*+AND+serious:1",
limit=1,
)
serious_total = serious.get("meta", {}).get("results", {}).get("total", 0)
# Recent recalls
recalls = fda.query_drug_recalls(drug_name=drug_name)
recall_results = recalls.get("results", [])
return {
"drug": drug_name,
"total_events": total,
"serious_events": serious_total,
"serious_rate_pct": (serious_total / total * 100.0) if total else 0.0,
"top_reactions": top_reactions,
"recalls_sample": recall_results[:5],
}
def monthly_event_trend(fda: FDAQuery, drug_name: str, months: int = 6):
trends = []
for i in range(months):
end = datetime.now() - timedelta(days=30 * i)
start = end - timedelta(days=30)
date_range = f"[{start.strftime('%Y%m%d')}+TO+{end.strftime('%Y%m%d')}]"
search = (
f"patient.drug.medicinalproduct:*{drug_name}*"
f"+AND+receivedate:{date_range}"
)
result = fda.query("drug", "event", search=search, limit=1)
count = result.get("meta", {}).get("results", {}).get("total", 0)
trends.append({"month": start.strftime("%Y-%m"), "events": count})
return list(reversed(trends))
def main():
fda = FDAQuery(api_key=os.getenv("FDA_API_KEY"))
# Drug: safety profile + trend
profile = drug_safety_profile(fda, "aspirin")
trend = monthly_event_trend(fda, "aspirin", months=6)
# Device: quick cross-database lookup
device_lookup = {
"adverse_events": fda.query_device_events("pacemaker", limit=10),
"classification": fda.query_device_classification("DQY"),
"510k": fda.query_device_510k(applicant="Medtronic"),
"udi": fda.query("device", "udi", search="brand_name:*pacemaker*", limit=5),
}
# Food: recall monitoring
food_recalls = fda.query_food_recalls(reason="undeclared peanut", limit=10)
# Substance: UNII lookup
substance = fda.query_substance_by_unii("R16CO5Y76E")
print({"drug_profile": profile, "drug_trend": trend})
print({"device_lookup_keys": list(device_lookup.keys())})
print({"food_recalls_count": len(food_recalls.get("results", []))})
print({"substance_keys": list(substance.keys())})
if __name__ == "__main__":
main()
3) Run the repository examples (if provided)
python scripts/fda_examples.py
Implementation Details
API domains and endpoints
This skill is a thin client over openFDA endpoints, typically accessed as:
- Drugs:
drug/event, drug/label, drug/ndc, drug/enforcement, drug/drugsfda, drug/drugshortages
- Devices:
device/event, device/510k, device/classification, device/enforcement, device/recall, device/pma, device/registrationlisting, device/udi, device/covid19serology
- Foods:
food/event, food/enforcement
- Animal/Veterinary:
animalandveterinary/event
- Other/Substances:
other/substance, other/nsde
Exact helper method names (e.g., query_drug_events, query_device_510k) are implemented in scripts/fda_query.py.
Query construction
- Searches are passed as openFDA query strings (Lucene-like), e.g.:
- Field match:
patient.drug.medicinalproduct:aspirin
- Wildcards:
*aspirin* (use sparingly)
- Boolean:
A+AND+B
- Date range:
receivedate:[20240101+TO+20241231]
- Pagination uses:
limit (page size)
skip (offset)
- Aggregations use
count_by_field(domain, endpoint, search, field, exact=True):
- When
exact=True, the implementation typically appends .exact to the aggregation field to avoid tokenization issues.
Rate limits and authentication
- openFDA supports unauthenticated access with lower daily quotas; an API key increases the daily request limit.
- The client is expected to:
- Attach the API key when provided
- Apply rate limiting and retries (per
FDAQuery implementation)
Result handling and robustness
- Responses generally follow:
{
"meta": { "results": { "skip": 0, "limit": 100, "total": 12345 } },
"results": []
}
- Always guard for:
- Missing
results
- Empty result sets
error objects returned by the API
Caching
- If enabled in
FDAQuery, caching reduces repeated calls for identical queries.
- Typical parameters (implementation-dependent):
use_cache=True
cache_ttl=<seconds>
1---2name: fda-database3description: Query the openFDA API to retrieve FDA regulatory datasets (drugs, devices, adverse events, recalls, submissions, UNII) when you need programmatic safety/regulatory evidence for analysis or research.4license: MIT5---6> **Source**: [https://github.com/aipoch/medical-research-skills](https://github.com/aipoch/medical-research-skills)78## When to Use9101. **Pharmacovigilance / safety signal screening** when you need adverse event counts, common reactions, or serious-event rates for a drug.112. **Medical device regulatory research** when you need 510(k)/PMA context, device classification, UDI lookups, or device adverse events/recalls.123. **Recall and enforcement monitoring** when you need to track Class I/II/III recalls across drugs, devices, or foods.134. **Substance identity resolution** when you need UNII/CAS/name-based lookups and basic substance relationship/structure retrieval.145. **Veterinary safety analysis** when you need animal adverse events filtered by species/breed and product.1516## Key Features1718- Unified Python interface (`FDAQuery`) for multiple openFDA domains (drug, device, food, animalandveterinary, other).19- Convenience helpers for common tasks:20 - Drug events, labels, recalls, shortages21 - Device events, classification, 510(k), PMA, UDI22 - Food events and recalls23 - Animal/veterinary adverse events24 - Substance (UNII/name) lookups25- Supports openFDA query patterns:26 - Fielded search strings, date ranges, wildcards27 - Aggregations via `count_by_field(...)` (with `.exact` support)28 - Pagination via `skip/limit` and bulk retrieval via `query_all(...)`29- Operational safeguards:30 - Optional API key support for higher daily limits31 - Built-in caching (TTL) and rate limiting (as implemented in `scripts/fda_query.py`)32 - Basic error handling patterns3334> Additional endpoint notes and query syntax are typically documented in:35> `references/api_basics.md`, `references/drugs.md`, `references/devices.md`, `references/foods.md`, `references/animal_veterinary.md`, `references/other.md`.3637## Dependencies3839- Python **3.9+**40- openFDA API access (public)41- Optional: openFDA API key (recommended for higher daily quota)4243> Package-level dependencies (e.g., `requests`) are defined by the repository implementation in `scripts/fda_query.py`. If you maintain this skill, pin them in `requirements.txt` (for example, `requests==2.31.0`) to ensure reproducibility.4445## Example Usage4647The following example is designed to be runnable in a repository that contains `scripts/fda_query.py` and the `FDAQuery` class.4849### 1) Set an API key (optional, recommended)5051```bash52export FDA_API_KEY="your_key_here"53```5455### 2) Run a complete script5657```python58import os59from datetime import datetime, timedelta6061from scripts.fda_query import FDAQuery626364def drug_safety_profile(fda: FDAQuery, drug_name: str):65 # Total adverse events (meta.total)66 events = fda.query_drug_events(drug_name, limit=1)67 total = events.get("meta", {}).get("results", {}).get("total", 0)6869 # Top reactions (aggregation)70 reactions = fda.count_by_field(71 "drug",72 "event",73 search=f"patient.drug.medicinalproduct:*{drug_name}*",74 field="patient.reaction.reactionmeddrapt",75 exact=True,76 )77 top_reactions = reactions.get("results", [])[:10]7879 # Serious events80 serious = fda.query(81 "drug",82 "event",83 search=f"patient.drug.medicinalproduct:*{drug_name}*+AND+serious:1",84 limit=1,85 )86 serious_total = serious.get("meta", {}).get("results", {}).get("total", 0)8788 # Recent recalls89 recalls = fda.query_drug_recalls(drug_name=drug_name)90 recall_results = recalls.get("results", [])9192 return {93 "drug": drug_name,94 "total_events": total,95 "serious_events": serious_total,96 "serious_rate_pct": (serious_total / total * 100.0) if total else 0.0,97 "top_reactions": top_reactions,98 "recalls_sample": recall_results[:5],99 }100101102def monthly_event_trend(fda: FDAQuery, drug_name: str, months: int = 6):103 trends = []104 for i in range(months):105 end = datetime.now() - timedelta(days=30 * i)106 start = end - timedelta(days=30)107 date_range = f"[{start.strftime('%Y%m%d')}+TO+{end.strftime('%Y%m%d')}]"108109 search = (110 f"patient.drug.medicinalproduct:*{drug_name}*"111 f"+AND+receivedate:{date_range}"112 )113 result = fda.query("drug", "event", search=search, limit=1)114 count = result.get("meta", {}).get("results", {}).get("total", 0)115116 trends.append({"month": start.strftime("%Y-%m"), "events": count})117118 return list(reversed(trends))119120121def main():122 fda = FDAQuery(api_key=os.getenv("FDA_API_KEY"))123124 # Drug: safety profile + trend125 profile = drug_safety_profile(fda, "aspirin")126 trend = monthly_event_trend(fda, "aspirin", months=6)127128 # Device: quick cross-database lookup129 device_lookup = {130 "adverse_events": fda.query_device_events("pacemaker", limit=10),131 "classification": fda.query_device_classification("DQY"),132 "510k": fda.query_device_510k(applicant="Medtronic"),133 "udi": fda.query("device", "udi", search="brand_name:*pacemaker*", limit=5),134 }135136 # Food: recall monitoring137 food_recalls = fda.query_food_recalls(reason="undeclared peanut", limit=10)138139 # Substance: UNII lookup140 substance = fda.query_substance_by_unii("R16CO5Y76E")141142 print({"drug_profile": profile, "drug_trend": trend})143 print({"device_lookup_keys": list(device_lookup.keys())})144 print({"food_recalls_count": len(food_recalls.get("results", []))})145 print({"substance_keys": list(substance.keys())})146147148if __name__ == "__main__":149 main()150```151152### 3) Run the repository examples (if provided)153154```bash155python scripts/fda_examples.py156```157158## Implementation Details159160### API domains and endpoints161162This skill is a thin client over openFDA endpoints, typically accessed as:163164- **Drugs**: `drug/event`, `drug/label`, `drug/ndc`, `drug/enforcement`, `drug/drugsfda`, `drug/drugshortages`165- **Devices**: `device/event`, `device/510k`, `device/classification`, `device/enforcement`, `device/recall`, `device/pma`, `device/registrationlisting`, `device/udi`, `device/covid19serology`166- **Foods**: `food/event`, `food/enforcement`167- **Animal/Veterinary**: `animalandveterinary/event`168- **Other/Substances**: `other/substance`, `other/nsde`169170Exact helper method names (e.g., `query_drug_events`, `query_device_510k`) are implemented in `scripts/fda_query.py`.171172### Query construction173174- Searches are passed as openFDA query strings (Lucene-like), e.g.:175 - Field match: `patient.drug.medicinalproduct:aspirin`176 - Wildcards: `*aspirin*` (use sparingly)177 - Boolean: `A+AND+B`178 - Date range: `receivedate:[20240101+TO+20241231]`179- Pagination uses:180 - `limit` (page size)181 - `skip` (offset)182- Aggregations use `count_by_field(domain, endpoint, search, field, exact=True)`:183 - When `exact=True`, the implementation typically appends `.exact` to the aggregation field to avoid tokenization issues.184185### Rate limits and authentication186187- openFDA supports unauthenticated access with lower daily quotas; an API key increases the daily request limit.188- The client is expected to:189 - Attach the API key when provided190 - Apply rate limiting and retries (per `FDAQuery` implementation)191192### Result handling and robustness193194- Responses generally follow:195196```json197{198 "meta": { "results": { "skip": 0, "limit": 100, "total": 12345 } },199 "results": []200}201```202203- Always guard for:204 - Missing `results`205 - Empty result sets206 - `error` objects returned by the API207208### Caching209210- If enabled in `FDAQuery`, caching reduces repeated calls for identical queries.211- Typical parameters (implementation-dependent):212 - `use_cache=True`213 - `cache_ttl=<seconds>`