Once OpenMed has pulled a drug name out of a note, you often need authoritative
product facts: the boxed warning, approved indications, **dosage form /
route, package NDC codes, and whether the product is under recall**. The
FDA's OpenFDA API exposes the Structured Product Labeling (SPL), the NDC
directory, and enforcement (recall) reports — all public and free.
This skill is enrichment: it attaches regulatory facts to an extracted drug. It
is not clinical decision support — a label lookup informs a human, it does
not prescribe.
display, alerting, or expectedness checks.
label's openfda block.
| Endpoint | Use | Key fields |
|---|---|---|
https://api.fda.gov/drug/label.json | SPL prescribing info | boxed_warning, indications_and_usage, warnings, dosage_and_administration, openfda.brand_name, openfda.generic_name, openfda.rxcui, openfda.product_ndc |
https://api.fda.gov/drug/ndc.json | NDC directory | product_ndc, generic_name, brand_name, dosage_form, route, active_ingredients |
https://api.fda.gov/drug/enforcement.json | Recalls | product_description, reason_for_recall, classification (Class I/II/III), recalling_firm, status, recall_initiation_date |
No key needed to try it (240 req/min, 1,000/day per IP). A free api_key= raises
the daily cap to 120,000.
import requests
def openfda(endpoint: str, search: str, limit: int = 1) -> list[dict]:
url = f"https://api.fda.gov/drug/{endpoint}.json"
r = requests.get(url, params={"search": search, "limit": limit}, timeout=30)
if r.status_code == 404: # OpenFDA returns 404 for zero matches
return []
r.raise_for_status()
return r.json().get("results", [])
# 1) Label: boxed warning + indications for a generic drug.
label = openfda("label", 'openfda.generic_name:"warfarin"')
if label:
rec = label[0]
print("Boxed warning:", rec.get("boxed_warning", ["(none)"])[0][:200])
print("Indication:", rec.get("indications_and_usage", ["(none)"])[0][:200])
print("RxCUI:", rec.get("openfda", {}).get("rxcui"))
# 2) NDC: package codes, form, route.
ndc = openfda("ndc", 'generic_name:"warfarin"', limit=5)
for rec in ndc:
print(rec["product_ndc"], rec.get("dosage_form"), rec.get("route"))
# 3) Enforcement: open recalls for a product.
recalls = openfda("enforcement",
'product_description:"warfarin"+AND+status:"Ongoing"', limit=5)
for rec in recalls:
print(rec["classification"], "-", rec["reason_for_recall"][:120])
openmed.analyze_text to get the span,then prefer the RxNorm ingredient (see normalizing-rxnorm) as your query
term — openfda.generic_name and the NDC generic_name index on the
ingredient, so a normalized name hits far more records than raw note text.
/drug/label with openfda.generic_name:"<ingredient>" (oropenfda.rxcui:"<rxcui>" for an exact product). Read boxed_warning,indications_and_usage, warnings_and_cautions.
/drug/ndc for package-level codes, dosage form, and route./drug/enforcement filtered to status:"Ongoing" to surface openrecalls; gate alerts on classification (Class I = most serious).
OpenMed's analyze_text returns a dict; result["entities"] items carrytext, label, confidence, start, end.
extracting-clinical-entities: Pharmaceutical/Chemical entities arethe query seeds. From normalizing-rxnorm: pass the RxCUI toopenfda.rxcui:"..." for an exact label match.
reporting-adverse-events: the boxed warning / indications support anexpectedness judgment (is this reaction labeled?). Todetecting-pv-signals: confirm whether a disproportionality signal is already
on-label before escalating.
leaves the process to hit OpenFDA. Never send a raw note containing PHI to
the API — de-identify with openmed.deidentify first if you must derive the
query from patient text.
results list —handle it as "no match" (the helper above does).
boxed_warning, indications_and_usage, andmost SPL sections are arrays of strings (rec["boxed_warning"][0]). Many
products have no boxed warning — the key is simply absent.
openfda.brand_name and openfda.generic_name differ;query the generic (ingredient) for coverage, the brand for a specific product.
brand-new labeling change may lag. For the definitive current label, cross-check
DailyMed.
product_ndc is the 2-segment labeler-product code;package NDCs add a third segment). Normalize before joining to claims data.
status is one of Ongoing, Completed, Terminated — filter toOngoing for active risk; classification Class I > II > III by severity.
hammer the API per-note in a batch pipeline.