# Querying Openfda Labels

> Looks up FDA drug labels, NDC directory entries, indications, boxed warnings, and recalls/enforcement actions via the free public OpenFDA API to enrich drugs that OpenMed extracts. Use when the user wants the prescribing information for a drug, its boxed warning, approved indications, dosage forms and routes, package NDC codes, RxCUI, or whether a product has an open recall. Trigger keywords: OpenFDA, drug label, SPL, prescribing information, boxed warning, black box warning, indications, NDC, package code, recall, enforcement, Class I recall, drug enrichment. Pairs adjacent to OpenMed NER: take a drug name (or RxNorm RxCUI) from openmed.analyze_text and resolve its label, NDC, and recall status. OpenFDA is public and free — no license barrier; send only de-identified drug names, never raw clinical notes.

- Skill: `maziyarpanahi/querying-openfda-labels` (Agent Skill)
- Install (CLI): `npx skillmds@latest add maziyarpanahi/querying-openfda-labels`
- Raw SKILL.md: https://api.skillmd.com/api/skills/maziyarpanahi/querying-openfda-labels/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- License: Apache-2.0
- Author: maziyarpanahi (https://skillmd.com/u/maziyarpanahi)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/maziyarpanahi/querying-openfda-labels

---


# Querying OpenFDA drug labels, NDC, and recalls

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.

## When to use

- You extracted a drug and need its **boxed warning** or **indications** for
  display, alerting, or expectedness checks.
- You need **NDC** package codes, dosage form, or route for a product.
- You want to know if a drug/lot is under an **open recall** (enforcement).
- You want to map a brand name to its generic ingredient and **RxCUI** via the
  label's `openfda` block.

## The three endpoints

| 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.

## Quick start (real OpenFDA queries)

```python
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])
```

## Workflow

1. **Normalize the drug name first.** Use `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.
2. **Query `/drug/label`** with `openfda.generic_name:"<ingredient>"` (or
   `openfda.rxcui:"<rxcui>"` for an exact product). Read `boxed_warning`,
   `indications_and_usage`, `warnings_and_cautions`.
3. **Query `/drug/ndc`** for package-level codes, dosage form, and route.
4. **Query `/drug/enforcement`** filtered to `status:"Ongoing"` to surface open
   recalls; gate alerts on `classification` (Class I = most serious).
5. **Cache** results — labels change rarely; you do not need to re-query per note.
6. **Attach the facts to the extracted drug** keyed by RxCUI/NDC for traceability.

## Hand-off to / from OpenMed

OpenMed's `analyze_text` returns a `dict`; `result["entities"]` items carry
`text`, `label`, `confidence`, `start`, `end`.

- **From** `extracting-clinical-entities`: Pharmaceutical/Chemical entities are
  the query seeds. **From** `normalizing-rxnorm`: pass the RxCUI to
  `openfda.rxcui:"..."` for an exact label match.
- **To** `reporting-adverse-events`: the boxed warning / indications support an
  **expectedness** judgment (is this reaction labeled?). **To**
  `detecting-pv-signals`: confirm whether a disproportionality signal is already
  on-label before escalating.
- OpenMed runs NER **on-device**; only a **de-identified drug name or RxCUI**
  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.

## Edge cases & gotchas

- **OpenFDA returns 404 for an empty result set**, not an empty `results` list —
  handle it as "no match" (the helper above does).
- **Multi-value fields are lists.** `boxed_warning`, `indications_and_usage`, and
  most SPL sections are arrays of strings (`rec["boxed_warning"][0]`). Many
  products have *no* boxed warning — the key is simply absent.
- **Brand vs generic.** `openfda.brand_name` and `openfda.generic_name` differ;
  query the generic (ingredient) for coverage, the brand for a specific product.
- **Labels are SPL snapshots, not real-time.** OpenFDA mirrors DailyMed SPL; a
  brand-new labeling change may lag. For the definitive current label, cross-check
  DailyMed.
- **NDC formats vary** (`product_ndc` is the 2-segment labeler-product code;
  package NDCs add a third segment). Normalize before joining to claims data.
- **Recall `status`** is one of `Ongoing`, `Completed`, `Terminated` — filter to
  `Ongoing` for active risk; `classification` Class I > II > III by severity.
- **Public and free, but rate-limited.** Register a free key and cache; do not
  hammer the API per-note in a batch pipeline.

## Standards & references

- OpenFDA drug label API: https://open.fda.gov/apis/drug/label/
- OpenFDA NDC directory API: https://open.fda.gov/apis/drug/ndc/
- OpenFDA drug enforcement (recalls) API: https://open.fda.gov/apis/drug/enforcement/
- OpenFDA query syntax & rate limits: https://open.fda.gov/apis/query-syntax/ , https://open.fda.gov/apis/authentication/
- FDA Structured Product Labeling (SPL): https://www.fda.gov/industry/structured-product-labeling-resources
- DailyMed (authoritative labels): https://dailymed.nlm.nih.gov/dailymed/

