Query the DailyMed REST API for FDA-approved drug labeling (SPL), drug names, RXCUI mappings, UNII ingredient identifiers, drug classes, NDC packaging, and label version history. Trigger for any mention of DailyMed, drug labeling, prescribing information, SPL, structured product labeling, package insert, drug label lookup, RXCUI, UNII, drug class, NDC packaging, labeling history, labeling version, medication guide, boxed warning text, or FDA-approved labeling. Also trigger when the user needs to find which products contain a specific ingredient, look up a drug's NDC codes, check label revision history, identify drug classes for a product, or retrieve images associated with drug packaging. Complements the openFDA skill (adverse events, recalls) by providing the official labeling text that openFDA does not carry in full.
The DailyMed API (https://dailymed.nlm.nih.gov/dailymed/services/v2/) provides free, no-auth access to FDA-approved drug labeling data in structured product labeling (SPL) format. DailyMed is maintained by the National Library of Medicine (NLM) and serves as the official repository for FDA label information used by hospital information systems, electronic prescribing, and healthcare software.
Base URL: https://dailymed.nlm.nih.gov/dailymed/services/v2/
Documentation: https://dailymed.nlm.nih.gov/dailymed/app-support.cfm
Web UI: https://dailymed.nlm.nih.gov/dailymed/
No API key required. No registration, no auth headers. Supports both JSON and XML responses (append or to endpoint paths).
.json.xmlWhat this data is: The official FDA-approved labeling for prescription drugs, OTC drugs, biologics, and some medical devices. Labels include prescribing information, medication guides, patient package inserts, and boxed warnings. Data is submitted by manufacturers via SPL (Structured Product Labeling, an HL7 standard) and published by NLM. Updated daily.
What this data is NOT: Not adverse event data (use openFDA FAERS for that). Not approval history (use openFDA Drugs@FDA). Not market wage or pricing data. DailyMed is specifically the labeling text and associated metadata (NDCs, ingredients, drug classes, images).
Relationship to openFDA: openFDA's drug/label endpoint carries a subset of DailyMed data (searchable labeling sections like boxed_warning, indications_and_usage). DailyMed provides the complete SPL record, version history, NDC packaging details, pill images, and structured ingredient/drug class data that openFDA does not expose.
No published rate limit, but DailyMed is a shared NLM resource. Add time.sleep(0.5) between requests in batch operations. Excessive automated traffic may be throttled.
import urllib.request, urllib.parse, json
BASE = "https://dailymed.nlm.nih.gov/dailymed/services/v2"
def dailymed_query(endpoint, params=None):
"""Query DailyMed API v2. Returns parsed JSON."""
url = f"{BASE}/{endpoint}.json"
if params:
params = {k: v for k, v in params.items() if v is not None}
url += "?" + urllib.parse.urlencode(params)
req = urllib.request.Request(url, headers={"Accept": "application/json"})
with urllib.request.urlopen(req, timeout=15) as resp:
return json.loads(resp.read().decode())
Endpoint: GET /spls.json
Search for drug labels by name, RXCUI, UNII code, setid, or drug class.
Parameters:
| Parameter | Description | Example |
|---|---|---|
drug_name | Drug name (brand or generic) | drug_name=metformin |
rxcui | RxNorm Concept Unique Identifier | rxcui=312962 |
unii_code | UNII ingredient identifier | unii_code=R16CO5Y76E |
setid | SPL Set ID (unique label identifier) | setid=8f55d5de-... |
drug_class_code | Pharmacologic class code (NDF-RT). MUST be paired with drug_class_coding_system | Used with drug_class_coding_system |
drug_class_coding_system | Coding system for drug class. Required when using drug_class_code. | 2.16.840.1.113883.6.345 (NDF-RT/FDA EPC) |
pagesize | Results per page (default 100, max 100) | pagesize=10 |
page | Page number (default 1) | page=2 |
def search_labels(drug_name=None, rxcui=None, unii_code=None, setid=None,
pagesize=10, page=1):
"""Search DailyMed drug labels."""
params = {"pagesize": pagesize, "page": page}
if drug_name: params["drug_name"] = drug_name
if rxcui: params["rxcui"] = rxcui
if unii_code: params["unii_code"] = unii_code
if setid: params["setid"] = setid
return dailymed_query("spls", params)
Response: Returns paginated list of labels with setid, spl_version, title, and published_date.
Once you have a setid from search, you can access detailed sub-resources:
Endpoint: GET /spls/{setid}/ndcs.json
Returns all NDC codes associated with a label.
def get_ndcs(setid):
"""Get NDC codes for a specific label."""
return dailymed_query(f"spls/{setid}/ndcs")
Response includes title, spl_version, and ndcs array with ndc strings.
Endpoint: GET /spls/{setid}/packaging.json
Returns detailed packaging information including active ingredients and strengths.
def get_packaging(setid):
"""Get packaging details including ingredients and strengths."""
return dailymed_query(f"spls/{setid}/packaging")
Response includes products array, each with packaging (NDC + package descriptions) and active_ingredients (name + strength).
Endpoint: GET /spls/{setid}/media.json
Returns images associated with a label (pill photos, packaging images).
def get_media(setid):
"""Get images for a drug label."""
return dailymed_query(f"spls/{setid}/media")
Response includes media array with name, mime_type, and url.
Endpoint: GET /spls/{setid}/history.json
Returns the publication history of a label, showing when each version was published.
def get_history(setid):
"""Get label version history."""
return dailymed_query(f"spls/{setid}/history")
Response includes spl (title and setid) and history array with spl_version and published_date for each version.
Endpoint: GET /drugnames.json
Search for drug names and identify brand vs. generic.
def search_drug_names(drug_name, pagesize=20):
"""Search drug names. Returns brand (B) and generic (G) name types."""
return dailymed_query("drugnames", {"drug_name": drug_name, "pagesize": pagesize})
Response field name_type: B = brand name, G = generic name.
Endpoint: GET /rxcuis.json
List or search RxNorm Concept Unique Identifiers mapped to DailyMed labels.
| Parameter | Description |
|---|---|
rxcui | Specific RXCUI to look up |
rxtty | Term type: PSN (Prescribable Name), SBD (Semantic Branded Drug), SCD (Semantic Clinical Drug), BPCK (Brand Name Pack), GPCK (Generic Pack), SY (Synonym) |
rxstring | RxNorm string to search (partial match) |
pagesize / page | Pagination |
def search_rxcuis(rxstring=None, rxtty=None, rxcui=None, pagesize=20):
"""Search RXCUIs in DailyMed."""
params = {"pagesize": pagesize}
if rxstring: params["rxstring"] = rxstring
if rxtty: params["rxtty"] = rxtty
if rxcui: params["rxcui"] = rxcui
return dailymed_query("rxcuis", params)
Endpoint: GET /uniis.json
Search Unique Ingredient Identifiers. UNIIs are FDA's standard for identifying active ingredients.
| Parameter | Description |
|---|---|
active_moiety | Active moiety name |
unii_code | Specific UNII code |
rxcui | Filter by RXCUI |
drug_class_code | Filter by drug class |
drug_class_coding_system | Coding system |
def search_uniis(active_moiety=None, unii_code=None, pagesize=20):
"""Search UNII ingredient identifiers."""
params = {"pagesize": pagesize}
if active_moiety: params["active_moiety"] = active_moiety
if unii_code: params["unii_code"] = unii_code
return dailymed_query("uniis", params)
Endpoint: GET /drugclasses.json
List pharmacologic drug classes (Established Pharmacologic Class, or EPC, from NDF-RT).
def list_drug_classes(pagesize=100):
"""List all drug classes."""
return dailymed_query("drugclasses", {"pagesize": pagesize})
Response fields: code, codingSystem, type (EPC), name (class name).
All endpoints return this wrapper:
{
"metadata": {
"total_elements": 544,
"elements_per_page": 10,
"total_pages": 55,
"current_page": 1,
"current_url": "...",
"next_page": 2,
"next_page_url": "...",
"db_published_date": "Mar 11, 2026 07:38:49PM EST"
},
"data": [ ... ]
}
Sub-resource endpoints (/ndcs, /packaging, /media, /history) return data as an object (not array) containing the label-specific details.
def find_label(drug_name):
"""Find labels for a drug by name."""
r = search_labels(drug_name=drug_name, pagesize=5)
labels = r.get("data", [])
return [{
"setid": l["setid"],
"title": l["title"],
"version": l["spl_version"],
"published": l["published_date"]
} for l in labels]
import time
def product_detail(drug_name):
"""Get label, NDCs, active ingredients, and images for a drug."""
# Find the label
r = search_labels(drug_name=drug_name, pagesize=1)
if not r.get("data"):
return None
setid = r["data"][0]["setid"]
title = r["data"][0]["title"]
time.sleep(0.5)
packaging = get_packaging(setid)
time.sleep(0.5)
ndcs = get_ndcs(setid)
time.sleep(0.5)
media = get_media(setid)
products = packaging.get("data", {}).get("products", [])
ingredients = []
for p in products:
for ai in p.get("active_ingredients", []):
ingredients.append(f"{ai['name']} {ai['strength']}")
ndc_list = [n["ndc"] for n in ndcs.get("data", {}).get("ndcs", [])]
image_urls = [m["url"] for m in media.get("data", {}).get("media", [])]
return {
"title": title,
"setid": setid,
"active_ingredients": ingredients,
"ndcs": ndc_list,
"images": image_urls
}
def label_history(drug_name):
"""Get the revision history for a drug's labeling."""
r = search_labels(drug_name=drug_name, pagesize=1)
if not r.get("data"):
return None
setid = r["data"][0]["setid"]
history = get_history(setid)
return {
"title": history.get("data", {}).get("spl", {}).get("title"),
"versions": history.get("data", {}).get("history", [])
}
def products_by_ingredient(unii_code, pagesize=20):
"""Find all labeled products containing a specific ingredient."""
return search_labels(unii_code=unii_code, pagesize=pagesize)
def labels_by_rxcui(rxcui, pagesize=10):
"""Find all DailyMed labels mapped to a specific RXCUI."""
return search_labels(rxcui=rxcui, pagesize=pagesize)
Standard page-based pagination. All list endpoints support pagesize (max 100) and page parameters.
import time
def paginate_labels(max_results=500, **search_kwargs):
"""Paginate through label search results."""
all_labels = []
page = 1
while len(all_labels) < max_results:
r = search_labels(**search_kwargs, pagesize=100, page=page)
data = r.get("data", [])
if not data:
break
all_labels.extend(data)
total_pages = r.get("metadata", {}).get("total_pages", 1)
if page >= total_pages:
break
page += 1
time.sleep(0.5)
return all_labels[:max_results]
The /spls/{setid}.json endpoint (fetching a single SPL record directly) returns HTTP 415 Unsupported Media Type. There is no JSON detail view for individual SPLs. Instead, use the sub-resource endpoints (/ndcs, /packaging, /media, /history) to get detailed information, or use the search endpoint with the setid parameter.
All endpoints support both JSON and XML. Append .json or .xml to the endpoint path. Default without extension varies. Always specify .json explicitly.
Searching drug_name=metformin returns all labels containing metformin in the title, including combination products (e.g., "GLIPIZIDE AND METFORMIN HYDROCHLORIDE"). Use the response title to filter for exact matches.
The setid (a UUID) is the permanent identifier for a drug label. It persists across version updates. SPL versions increment as the label is revised, but the setid stays the same. Always reference labels by setid, not by title.
The /spls/{setid}/ndcs.json response has data as an object (with title, spl_version, ndcs array inside), not as a top-level array. Same for /packaging, /media, and /history.
Drug class codes follow the NDF-RT (National Drug File Reference Terminology) system. The codes are NUI identifiers like N0000175580. The type field is typically EPC (Established Pharmacologic Class).
The /uniis.json endpoint with drug_name param searches across ALL UNIIs, not just those in products matching that name. Use unii_code for specific lookups, or search /spls.json by unii_code to find products containing a specific ingredient.
Dates appear as formatted strings like "Apr 03, 2025", not ISO format. Parse with care if doing date comparisons.
Unlike some APIs with 1000-record pages, DailyMed caps at 100 per page. Plan pagination accordingly for large result sets.
The drug_name parameter does not handle apostrophes. Searching drug_name=Children's Motrin returns 0 results. Drop the apostrophe (Childrens Motrin returns 6) or use just the base name (Motrin returns 22). This also applies to names like "Crohn's" or "Graves'".
Searching /spls.json?drug_class_code=N0000175589 without also passing drug_class_coding_system=2.16.840.1.113883.6.345 returns zero results with no error message. The coding system parameter is mandatory, not optional. Always include both:
/spls.json?drug_class_code=N0000175589&drug_class_coding_system=2.16.840.1.113883.6.345
The /ndcs sub-resource includes total_elements, total_pages, etc. in its metadata. The /packaging, /media, and /history sub-resources do NOT. They only return db_published_date and current_url in metadata. Check for empty arrays in the data object (ndcs: [], products: [], media: [], history: []) rather than relying on total_elements to detect empty results.
| Error | Cause | Fix |
|---|---|---|
| 415 Unsupported Media Type | Requested /spls/{setid}.json directly | Use sub-resources (/ndcs, /packaging, /media, /history) or search with setid param instead |
| 404 | Bad setid or nonexistent resource | Verify setid from a search result; check endpoint path spelling |
| Empty results | Drug name not found | Try alternate names (brand vs. generic); check spelling |
data is object, not array | Sub-resource endpoint | Access nested fields: data["ndcs"], data["products"], data["media"], data["history"] |
| Too many results | Broad drug name search | Add more specific terms or filter by RXCUI/UNII |
| Bad setid returns 200 with empty data | Nonexistent setid in sub-resource | The API does NOT return 404 for invalid setids. It returns HTTP 200 with empty fields and total_elements: 0. Check for empty title or empty ndcs/media arrays to detect this. |
| Drug class not found in first page | 1,216 classes, pagesize max 100 | Paginate through /drugclasses or use a known NDF-RT code directly with /spls?drug_class_code=CODE&drug_class_coding_system=2.16.840.1.113883.6.345. Common code: N0000175589 = HMG-CoA Reductase Inhibitor (statins). The coding_system param is REQUIRED or you get 0 results. |