# Drug safety status

URL: https://docs.healthcare.dev/recipes/drug-safety

|                  |                                                                                                                                                                    |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Plan             | **Free** for each lookup. **Pro** (or a preview on Free and Builder) for the one-call answer with J-codes, ASP and NADAC ([Joined answers](/joined-answers#drugs)) |
| Datasets         | [NDC Directory](/datasets/ndc), [RxNorm](/datasets/rxnorm), FDA drug shortages, FDA drug recalls, [FDA drug labels](/datasets/drug-labels)                         |
| Calls per answer | 4 on Free (NDC, shortages, recalls, labels; a fifth for the label text), 1 on Pro                                                                                  |

Any NDC format works: a product NDC (`0003-0894`), a package NDC in any 10-digit layout
(`0003-0894-21`) or 11 digits (`00003089421`).

## Free: one lookup per dataset

```sh
NDC=0003-0894-21
H="Authorization: Bearer $HDEV_API_KEY"

# 1. The FDA product and its RxNorm concepts
curl -s "https://api.healthcare.dev/v1/drugs/ndc/$NDC" -H "$H" | jq '.data | {brand_name, generic_name, rxnorm}'

# 2. Current FDA shortage list entries for the product
curl -s "https://api.healthcare.dev/v1/drugs/shortages?ndc=$NDC&status=current" -H "$H" | jq '[.data[] | {presentation, availability, update_date}]'

# 3. FDA recalls of the product FDA hasn't terminated (ongoing, then completed)
curl -s "https://api.healthcare.dev/v1/drugs/recalls?ndc=$NDC&status=ongoing" -H "$H" | jq '[.data[] | {recall_number, classification, report_date}]'
curl -s "https://api.healthcare.dev/v1/drugs/recalls?ndc=$NDC&status=completed" -H "$H" | jq '[.data[] | {recall_number, classification, report_date}]'

# 4. The current label, then its boxed warning
SET_ID=$(curl -s "https://api.healthcare.dev/v1/drugs/labels?ndc=$NDC" -H "$H" | jq -r '.data[0].set_id')
curl -s "https://api.healthcare.dev/v1/drugs/labels/$SET_ID?sections=boxed_warning" -H "$H" | jq '.data | {title, effective_date, sections, url}'
```

Filtering by `status` leaves only what you need to check. Lists are paged, newest first: follow
`links.next` when there are more (the SDKs' `iterate*` helpers do). An empty shortage or recall list is an answer: FDA lists none for the product. It is not a
statement that the product is safe.

**TypeScript**

```ts
import { HealthcareDev } from "@healthcare.dev/sdk";

const hdev = new HealthcareDev();
const ndc = "0003-0894-21";

const all = async <T>(items: AsyncIterable<T>) => {
  const out: T[] = [];
  for await (const item of items) out.push(item);
  return out;
};

const [product, shortages, recalls, labels] = await Promise.all([
  hdev.drugs.ndc(ndc),
  all(hdev.drugs.iterateShortages({ ndc, status: "current" })),
  all(hdev.drugs.iterateRecalls({ ndc })), // every page
  hdev.drugs.labels({ ndc }),
]);

const label = labels.data[0]
  ? await hdev.drugs.label(labels.data[0].set_id, { sections: ["boxed_warning"] })
  : undefined;

console.log({
  rxcuis: product.data.rxnorm.map((c) => c.rxcui),
  current_shortage: shortages.length > 0,
  open_recalls: recalls.filter((r) => r.status !== "terminated").map((r) => r.recall_number),
  boxed_warning: label?.data.sections.find((s) => s.key === "boxed_warning")?.text ?? null,
  label_url: label?.data.url,
});
```

**Python**

```python
from healthcare_dev import HealthcareDev

hdev = HealthcareDev()
ndc = "0003-0894-21"

product = hdev.drugs.ndc(ndc)["data"]
shortages = list(hdev.drugs.iterate_shortages(ndc=ndc, status="current"))
recalls = list(hdev.drugs.iterate_recalls(ndc=ndc))  # every page
labels = hdev.drugs.labels(ndc=ndc)["data"]
label = hdev.drugs.label(labels[0]["set_id"], sections=["boxed_warning"])["data"] if labels else None

print([c["rxcui"] for c in product["rxnorm"]])
print(len(shortages) > 0)
print([r["recall_number"] for r in recalls if r["status"] != "terminated"])
print(label and label["url"])
```

## Pro: one call

`GET /v1/drugs/ndc/{ndc}/full` joins the same datasets, plus the J-codes and Medicare Part B
payment limit (ASP) and the NADAC rate, in one billed call. Each component says whether it has
an answer, and why not.

<Callout title="Try it before you upgrade">
  Free and Builder keys can call it as a preview: 50 preview calls a month on Free, 200 on Builder,
  per feature. A preview answer is the real answer, with `meta.preview` (calls `remaining` this
  month and the plan that `unlocks` it) and `links.upgrade`. Preview calls count toward your
  included calls. Once they're used up, the `403` `plan-upgrade-required` adds `upgrade_url` and a
  sample answer for a fixed demo subject. See [Plans](/plans#previews).
</Callout>

```sh
curl "https://api.healthcare.dev/v1/drugs/ndc/0003-0894-21/full?view=compact" \
  -H "Authorization: Bearer $HDEV_API_KEY"
```

```json
{
  "data": {
    "ndc": "00003089421",
    "product_ndc": "0003-0894",
    "rxcuis": ["..."],
    "hcpcs_codes": [],
    "label_set_ids": ["..."],
    "flags": { "active_shortage": false, "open_recall": false, "boxed_warning": true },
    "missing": [{ "component": "asp", "status": "not_listed", "reason": "..." }]
  }
}
```

```ts
const { data } = await hdev.drugs.full("0003-0894-21", { view: "compact" });
console.log(data.flags, data.missing);
```

```python
full = hdev.drugs.full("0003-0894-21", view="compact")["data"]
print(full["flags"], full["missing"])
```

## Ask an agent

> Is NDC 0003-0894-21 in shortage or under an open FDA recall, and does its label have a boxed
> warning? Give the RxNorm concept too, and cite each source's data version.

The agent uses `map_ndc_to_rxnorm`, `check_drug_shortages`, `check_drug_recalls` and
`get_drug_label`, or `get_drug_full` on Pro.

## Read the answer

- **Recalls:** `status` is `ongoing`, `completed` or `terminated`; only `terminated` recalls are
  closed. `classification` `I` is the most serious hazard class. On Pro, `open_recall` is true for
  any recall FDA hasn't terminated, however old.
- **Shortages:** `status` is `current`, `resolved` or `to_be_discontinued`. Entries are per
  presentation; with a package NDC, `full` matches at the package level where FDA names the
  package.
- **Flags of `null`** (Pro) mean the data to answer isn't available, never `false`. `missing`
  lists every component without an answer, with its reason.
- **Label text** is the FDA label, verbatim. Show NLM's credit line (`meta.license_terms`) and
  the label's version and date, and link to DailyMed (`url`) for the full label.

## What it is not

Reference data, not clinical advice. No shortage or recall listed means FDA lists none, not that
a product is safe or available. Dosing and interactions are out of scope: read the label.
