healthcare.dev docs

Drug safety status

For an NDC: the RxNorm concept, whether FDA lists a current shortage, any recall FDA hasn't terminated, and the label's boxed warning, each with its source and release.

PlanFree for each lookup. Pro for the one-call answer with J-codes, ASP and NADAC (Joined answers)
DatasetsNDC Directory, RxNorm, FDA drug shortages, FDA drug recalls, FDA drug labels
Calls per answer4 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

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

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

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.

curl "https://api.healthcare.dev/v1/drugs/ndc/0003-0894-21/full?view=compact" \
  -H "Authorization: Bearer $HDEV_API_KEY"
{
  "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": "..." }]
  }
}
const { data } = await hdev.drugs.full("0003-0894-21", { view: "compact" });
console.log(data.flags, data.missing);
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.

On this page