---
name: "dailymed-database"
description: "Query FDA drug labels (DailyMed) via REST API. Search structured product labels (SPLs) by name, NDC, set ID, or RxCUI; get indications, dosage, warnings, adverse reactions, packaging. No auth. For adverse events use fda-database; for DDIs use ddinter-database."
license: "CC0-1.0"
---

# DailyMed Drug Label Database

## Overview

DailyMed is the National Library of Medicine's official repository of FDA-approved drug labeling information, containing 140,000+ structured product labels (SPLs) for prescription drugs, OTC medications, biologics, and vaccines. The REST API (v2) provides structured JSON/XML access to the full label content including indications, dosage, warnings, contraindications, adverse reactions, and packaging data — with no authentication required.

## When to Use

- Retrieving official FDA-approved prescribing information for a drug by name, NDC code, or set ID
- Extracting structured label sections (indications, warnings, dosage, adverse reactions) for pharmacological research
- Looking up all marketed formulations of an active ingredient with packaging and NDC codes
- Cross-referencing drug labels using RxCUI identifiers from RxNorm integration
- Building drug information pipelines that require authoritative FDA label content (not user-reported data)
- Comparing label content across brand name and generic formulations of the same drug
- For adverse event reports from FAERS, use `fda-database` instead; DailyMed contains label text, not post-market safety signals
- For drug-drug interaction severity data, use `ddinter-database`; DailyMed label text is unstructured for interactions

## Prerequisites

- **Python packages**: `requests`, `pandas`, `matplotlib`
- **Data requirements**: drug names, NDC codes, set IDs, or RxCUI identifiers
- **Environment**: internet connection; no API key required
- **Rate limits**: no officially published limit; ~100 requests/minute is safe for polite access; add `time.sleep(0.3)` in batch loops

```bash
pip install requests pandas matplotlib
```

## Quick Start

```python
import requests

BASE = "https://dailymed.nlm.nih.gov/dailymed/services/v2"

# Search drug labels by name
r = requests.get(f"{BASE}/spls.json", params={"drug_name": "metformin", "pagesize": 5})
r.raise_for_status()
data = r.json()
print(f"Total labels found: {data['metadata']['total_elements']}")
for spl in data["data"][:3]:
    print(f"  {spl['title']!r:60s}  setid={spl['setid']}")
```

## Core API

### Query 1: Search Drug Labels by Name

Search for structured product labels (SPLs) using drug name. Returns paginated list of matching labels with set IDs.

```python
import requests
import pandas as pd

BASE = "https://dailymed.nlm.nih.gov/dailymed/services/v2"

def search_spls(drug_name, pagesize=20, page=1):
    """Search DailyMed SPLs by drug name. Returns list of label summaries."""
    r = requests.get(f"{BASE}/spls.json",
                     params={"drug_name": drug_name, "pagesize": pagesize, "page": page},
                     timeout=15)
    r.raise_for_status()
    return r.json()

result = search_spls("atorvastatin", pagesize=10)
meta = result["metadata"]
print(f"Search: 'atorvastatin' → {meta['total_elements']} labels across {meta['total_pages']} pages")

df = pd.DataFrame(result["data"])
print(df[["setid", "title", "published_date"]].to_string(index=False))
# setid                                  title                                   published_date
# 8f6c7c7c-...  ATORVASTATIN CALCIUM tablet                               2024-03-15
# a4b7d3e1-...  ATORVASTATIN CALCIUM tablet, film coated                  2023-11-20
```

### Query 2: Retrieve Full Label by Set ID

Fetch the complete structured product label for a specific drug using its set ID. Returns all label sections including indications, warnings, dosage, and adverse reactions.

```python
import requests

BASE = "https://dailymed.nlm.nih.gov/dailymed/services/v2"

def get_spl(setid):
    """Retrieve full SPL document by set ID. Returns label metadata and XML/JSON."""
    r = requests.get(f"{BASE}/spls/{setid}.json", timeout=20)
    r.raise_for_status()
    return r.json()

# Use a known set ID from search results
setid = "8f6c7c7c-1f7f-4f1a-af86-8b2eef2a8b2c"   # example atorvastatin label
label = get_spl(setid)
data = label["data"]

print(f"Title: {data.get('title')}")
print(f"Set ID: {data.get('setid')}")
print(f"Published: {data.get('published_date')}")
print(f"Version: {data.get('version')}")

# Access structured sections
if "sections" in data:
    sections = data["sections"]
    print(f"\nLabel sections ({len(sections)} total):")
    for sec in sections[:5]:
        print(f"  [{sec.get('loinc_code', 'N/A')}] {sec.get('title', 'Untitled')}")
```

### Query 3: Search by NDC Code

Look up drug labels by National Drug Code (NDC) — useful when you have a product barcode or dispensing record.

```python
import requests

BASE = "https://dailymed.nlm.nih.gov/dailymed/services/v2"

def search_by_ndc(ndc_code):
    """Find SPL by NDC code (formatted as XXXXX-XXXX-XX or without dashes)."""
    r = requests.get(f"{BASE}/spls.json",
                     params={"ndc": ndc_code},
                     timeout=15)
    r.raise_for_status()
    return r.json()

# NDC for Lipitor 10mg (atorvastatin)
result = search_by_ndc("0071-0155-23")
if result["data"]:
    spl = result["data"][0]
    print(f"Drug: {spl['title']}")
    print(f"Set ID: {spl['setid']}")
    print(f"Published: {spl['published_date']}")
else:
    print("No label found for this NDC")
```

### Query 4: Retrieve Packaging Information

Get detailed packaging data (NDC codes, package types, quantities) for a specific drug label by set ID.

```python
import requests
import pandas as pd

BASE = "https://dailymed.nlm.nih.gov/dailymed/services/v2"

def get_packaging(setid):
    """Retrieve packaging information for a label (NDC codes, dosage forms, quantities)."""
    r = requests.get(f"{BASE}/spls/{setid}/packaging.json", timeout=15)
    r.raise_for_status()
    return r.json()

setid = "8f6c7c7c-1f7f-4f1a-af86-8b2eef2a8b2c"   # example setid
pkg = get_packaging(setid)

if pkg["data"]:
    packages = pkg["data"]
    print(f"Packaging variants: {len(packages)}")
    df = pd.DataFrame(packages)
    # Common fields: ndc, dosage_form, route, marketing_status
    for col in ["ndc", "dosage_form", "route", "marketing_status"]:
        if col in df.columns:
            print(f"\n{col.upper()}:")
            print(df[col].value_counts().head(5))
```

### Query 5: Look Up by RxCUI

Retrieve drug labels using RxNorm Concept Unique Identifier (RxCUI) for integration with RxNorm-based clinical systems.

```python
import requests
import time

BASE = "https://dailymed.nlm.nih.gov/dailymed/services/v2"

def get_spls_by_rxcui(rxcui):
    """Get all SPLs associated with an RxCUI. Returns list of set IDs and titles."""
    r = requests.get(f"{BASE}/rxcuis/{rxcui}/spls.json", timeout=15)
    r.raise_for_status()
    return r.json()

# RxCUI for atorvastatin: 83367
rxcui = "83367"
result = get_spls_by_rxcui(rxcui)
print(f"SPLs for RxCUI {rxcui} (atorvastatin):")
for spl in result["data"][:5]:
    print(f"  {spl['setid']} | {spl['title'][:70]}")
print(f"  Total: {result['metadata']['total_elements']} labels")
```

### Query 6: Search Drug Names Index

List all standardized drug names in DailyMed — useful for name normalization and autocomplete in drug lookup pipelines.

```python
import requests

BASE = "https://dailymed.nlm.nih.gov/dailymed/services/v2"

def search_drug_names(query, pagesize=20):
    """Search the DailyMed drug name index for name normalization."""
    r = requests.get(f"{BASE}/drugnames.json",
                     params={"drug_name": query, "pagesize": pagesize},
                     timeout=15)
    r.raise_for_status()
    return r.json()

result = search_drug_names("metformin")
print(f"Drug name matches for 'metformin': {result['metadata']['total_elements']}")
for entry in result["data"][:8]:
    print(f"  {entry['drug_name']}")
# METFORMIN HYDROCHLORIDE
# METFORMIN HYDROCHLORIDE AND SITAGLIPTIN PHOSPHATE
# METFORMIN HYDROCHLORIDE AND SAXAGLIPTIN
```

### Query 7: Batch Label Section Extraction

Extract specific label sections (e.g., indications, warnings) from multiple drug labels for comparative analysis.

```python
import requests
import time
import pandas as pd

BASE = "https://dailymed.nlm.nih.gov/dailymed/services/v2"

# LOINC codes for common SPL sections
SECTION_LOINC = {
    "34067-9": "Indications and Usage",
    "34068-7": "Dosage and Administration",
    "34071-1": "Warnings",
    "34084-4": "Adverse Reactions",
    "34070-3": "Contraindications",
    "43685-7": "Warnings and Precautions",
}

def get_label_sections(setid):
    """Extract structured sections from an SPL by set ID."""
    r = requests.get(f"{BASE}/spls/{setid}.json", timeout=20)
    r.raise_for_status()
    data = r.json()["data"]
    sections = {}
    for sec in data.get("sections", []):
        loinc = sec.get("loinc_code")
        if loinc in SECTION_LOINC:
            sections[SECTION_LOINC[loinc]] = sec.get("text", "")
    return sections

# Get indications for two statin labels
statins = [
    ("Atorvastatin", "8f6c7c7c-1f7f-4f1a-af86-8b2eef2a8b2c"),
    ("Rosuvastatin", "a3b2c4d5-1234-5678-abcd-ef0123456789"),   # example
]
records = []
for drug_name, setid in statins:
    try:
        sections = get_label_sections(setid)
        records.append({"drug": drug_name, "indications_length": len(sections.get("Indications and Usage", ""))})
    except Exception as e:
        print(f"Warning: {drug_name} failed — {e}")
    time.sleep(0.3)

df = pd.DataFrame(records)
print(df.to_string(index=False))
```

## Key Concepts

### Structured Product Label (SPL) and Set ID

An SPL is the official FDA-approved drug label in XML format, structured using Health Level 7 (HL7) Clinical Document Architecture (CDA). Each unique drug product label has a globally unique **set ID** (UUID format). Multiple versions of the same label share the same set ID but have different version numbers. Always use the most recent published version for current prescribing information.

### LOINC Section Codes

DailyMed SPLs use standardized LOINC codes to identify label sections, enabling consistent programmatic extraction across all labels:

| LOINC Code | Section Name |
|------------|-------------|
| `34067-9` | Indications and Usage |
| `34068-7` | Dosage and Administration |
| `34070-3` | Contraindications |
| `34071-1` | Warnings |
| `43685-7` | Warnings and Precautions |
| `34084-4` | Adverse Reactions |
| `34073-7` | Drug Interactions |
| `34076-0` | Patient Counseling Information |
| `42229-5` | Mechanism of Action |
| `34069-5` | How Supplied/Storage |

### NDC Code Format

National Drug Codes (NDCs) identify drug products with a three-segment numeric format: `labeler-product-package` (e.g., `0071-0155-23`). NDCs can also appear without dashes in some systems. DailyMed accepts both formats in API queries.

## Common Workflows

### Workflow 1: Drug Label Comparison by Active Ingredient

**Goal**: Retrieve and compare label content for all formulations of an active ingredient — useful for generic vs. brand name comparison.

```python
import requests
import time
import pandas as pd

BASE = "https://dailymed.nlm.nih.gov/dailymed/services/v2"

def search_spls(drug_name, pagesize=50):
    r = requests.get(f"{BASE}/spls.json",
                     params={"drug_name": drug_name, "pagesize": pagesize},
                     timeout=15)
    r.raise_for_status()
    return r.json()

def get_packaging(setid):
    r = requests.get(f"{BASE}/spls/{setid}/packaging.json", timeout=15)
    r.raise_for_status()
    return r.json()

# Search for all metformin labels
result = search_spls("metformin", pagesize=20)
labels = result["data"]
print(f"Found {len(labels)} metformin labels")

rows = []
for label in labels[:10]:   # limit for demo
    setid = label["setid"]
    try:
        pkg = get_packaging(setid)
        for item in pkg["data"][:2]:
            rows.append({
                "title": label["title"][:60],
                "setid": setid,
                "ndc": item.get("ndc"),
                "dosage_form": item.get("dosage_form"),
                "route": item.get("route"),
                "marketing_status": item.get("marketing_status"),
                "published_date": label.get("published_date"),
            })
    except Exception as e:
        print(f"Skipping {setid}: {e}")
    time.sleep(0.3)

df = pd.DataFrame(rows)
print(f"\nFormulations with packaging data: {len(df)}")
print(df[["title", "dosage_form", "route", "marketing_status"]].drop_duplicates().to_string(index=False))
df.to_csv("metformin_formulations.csv", index=False)
print("\nSaved: metformin_formulations.csv")
```

### Workflow 2: Extract Warnings and Adverse Reactions for Safety Analysis

**Goal**: Systematically extract structured warning and adverse reaction text from a set of drug labels for pharmacovigilance research.

```python
import requests
import time
import pandas as pd

BASE = "https://dailymed.nlm.nih.gov/dailymed/services/v2"

TARGET_SECTIONS = {
    "34071-1": "Warnings",
    "43685-7": "Warnings_and_Precautions",
    "34084-4": "Adverse_Reactions",
    "34070-3": "Contraindications",
}

def search_and_get_sections(drug_name, max_labels=5):
    """Search for labels and extract safety-relevant sections."""
    search_r = requests.get(f"{BASE}/spls.json",
                            params={"drug_name": drug_name, "pagesize": max_labels},
                            timeout=15)
    search_r.raise_for_status()
    labels = search_r.json()["data"][:max_labels]

    records = []
    for label in labels:
        setid = label["setid"]
        try:
            label_r = requests.get(f"{BASE}/spls/{setid}.json", timeout=20)
            label_r.raise_for_status()
            spl_data = label_r.json()["data"]

            row = {"drug_name": drug_name, "title": label["title"][:80], "setid": setid}
            for loinc, col in TARGET_SECTIONS.items():
                text = ""
                for sec in spl_data.get("sections", []):
                    if sec.get("loinc_code") == loinc:
                        text = sec.get("text", "")[:500]   # first 500 chars
                        break
                row[col] = text
            records.append(row)
        except Exception as e:
            print(f"  Skipping {setid}: {e}")
        time.sleep(0.3)

    return pd.DataFrame(records)

# Compare safety sections across ACE inhibitor labels
drugs = ["lisinopril", "enalapril"]
all_records = []
for drug in drugs:
    print(f"Processing: {drug}")
    df = search_and_get_sections(drug, max_labels=3)
    all_records.append(df)

combined = pd.concat(all_records, ignore_index=True)
print(f"\nExtracted safety sections: {len(combined)} labels")
print(combined[["drug_name", "title"]].to_string(index=False))
combined.to_csv("ace_inhibitor_safety_sections.csv", index=False)
print("Saved: ace_inhibitor_safety_sections.csv")
```

### Workflow 3: Visualize Label Distribution by Drug Class

**Goal**: Query multiple drugs in a class, count their labeled formulations, and visualize the distribution.

```python
import requests
import time
import pandas as pd
import matplotlib.pyplot as plt

BASE = "https://dailymed.nlm.nih.gov/dailymed/services/v2"

def count_labels(drug_name):
    """Return total SPL count for a drug name."""
    r = requests.get(f"{BASE}/spls.json",
                     params={"drug_name": drug_name, "pagesize": 1},
                     timeout=15)
    r.raise_for_status()
    return r.json()["metadata"]["total_elements"]

# Count labels for common statins
statins = {
    "atorvastatin": "Atorvastatin",
    "rosuvastatin": "Rosuvastatin",
    "simvastatin": "Simvastatin",
    "pravastatin": "Pravastatin",
    "lovastatin": "Lovastatin",
    "fluvastatin": "Fluvastatin",
    "pitavastatin": "Pitavastatin",
}

counts = {}
for query, label in statins.items():
    try:
        counts[label] = count_labels(query)
        print(f"  {label}: {counts[label]} labels")
    except Exception as e:
        print(f"  {label}: error — {e}")
    time.sleep(0.3)

# Visualization
df = pd.DataFrame(list(counts.items()), columns=["Drug", "Label_Count"])
df = df.sort_values("Label_Count", ascending=True)

fig, ax = plt.subplots(figsize=(9, 5))
bars = ax.barh(df["Drug"], df["Label_Count"], color="#2196F3", edgecolor="white")
ax.bar_label(bars, fmt="%d", padding=4, fontsize=9)
ax.set_xlabel("Number of DailyMed SPL Entries")
ax.set_title("DailyMed: FDA Drug Label Count by Statin\n(includes brand + generic formulations)")
ax.set_xlim(0, df["Label_Count"].max() * 1.15)
plt.tight_layout()
plt.savefig("statin_label_counts.png", dpi=150, bbox_inches="tight")
print(f"Saved: statin_label_counts.png  (total labels: {df['Label_Count'].sum()})")
```

## Key Parameters

| Parameter | Endpoint | Default | Range / Options | Effect |
|-----------|----------|---------|-----------------|--------|
| `drug_name` | `/spls`, `/drugnames` | — | any drug name string | Filter labels by drug name (partial match supported) |
| `ndc` | `/spls` | — | NDC code string | Filter labels by National Drug Code |
| `pagesize` | `/spls`, `/drugnames` | `20` | `1`–`100` | Results per page; max 100 |
| `page` | `/spls`, `/drugnames` | `1` | positive integer | Page number for pagination |
| `setid` | `/spls/{setid}`, `/spls/{setid}/packaging` | — | UUID string | Unique label identifier; required for direct access |
| `rxcui` | `/rxcuis/{rxcui}/spls` | — | RxNorm concept ID | Look up labels by RxCUI for clinical system integration |
| `format` | any endpoint | `json` | `json`, `xml` | Response format; JSON is default and preferred |

## Best Practices

1. **Resolve set IDs before batch processing**: Use the `/spls` search to get set IDs, then fetch full labels by set ID. Do not construct set IDs from drug names — they are UUID identifiers assigned by FDA.

2. **Add `time.sleep(0.3)` in batch loops**: DailyMed has no published rate limits but is public NIH infrastructure. Polite delays prevent throttling.
   ```python
   import time
   for drug in drug_list:
       result = search_spls(drug)
       time.sleep(0.3)   # 200 requests/minute safe upper bound
   ```

3. **Use LOINC codes for section extraction, not text parsing**: Label sections are indexed by LOINC code, providing consistent section access across all SPL documents without brittle regex on section headers.

4. **Pagination for comprehensive results**: The default `pagesize=20` may miss formulations. Use `metadata["total_elements"]` to detect if pagination is needed:
   ```python
   meta = result["metadata"]
   total_pages = meta["total_pages"]
   if total_pages > 1:
       for p in range(2, total_pages + 1):
           result = search_spls(drug_name, page=p)
   ```

5. **Prefer RxCUI for clinical integration**: When integrating with clinical systems (EHR, dispensing), use RxCUI-based lookups (`/rxcuis/{rxcui}/spls`) for standardized, unambiguous drug identification.

## Common Recipes

### Recipe: Check if a Drug Has Black Box Warnings

When to use: Quickly determine whether a drug carries the FDA's strongest warning level.

```python
import requests

BASE = "https://dailymed.nlm.nih.gov/dailymed/services/v2"
BLACK_BOX_LOINC = "34066-1"

def has_black_box_warning(setid):
    """Return True and warning text if drug label has a black box (boxed) warning."""
    r = requests.get(f"{BASE}/spls/{setid}.json", timeout=20)
    r.raise_for_status()
    sections = r.json()["data"].get("sections", [])
    for sec in sections:
        if sec.get("loinc_code") == BLACK_BOX_LOINC:
            return True, sec.get("text", "")[:300]
    return False, ""

# Example: check warfarin label
setid = "8b7c3d4e-5678-90ab-cdef-1234567890ab"   # example warfarin setid
has_warning, text = has_black_box_warning(setid)
print(f"Black box warning: {has_warning}")
if has_warning:
    print(f"Warning text (first 300 chars): {text}")
```

### Recipe: Multi-Page Search with All Results

When to use: Retrieve all matching labels for a drug when total count exceeds one page.

```python
import requests
import time

BASE = "https://dailymed.nlm.nih.gov/dailymed/services/v2"

def search_all_spls(drug_name, pagesize=100, delay=0.3):
    """Retrieve all SPLs for a drug name across multiple pages."""
    all_labels = []
    page = 1
    while True:
        r = requests.get(f"{BASE}/spls.json",
                         params={"drug_name": drug_name, "pagesize": pagesize, "page": page},
                         timeout=15)
        r.raise_for_status()
        data = r.json()
        all_labels.extend(data["data"])
        if page >= data["metadata"]["total_pages"]:
            break
        page += 1
        time.sleep(delay)
    return all_labels

labels = search_all_spls("ibuprofen")
print(f"Total ibuprofen labels: {len(labels)}")
```

### Recipe: Export Label Summary to CSV

When to use: Build a flat reference table of drug labels for offline analysis.

```python
import requests
import pandas as pd

BASE = "https://dailymed.nlm.nih.gov/dailymed/services/v2"

def export_drug_labels(drug_name, pagesize=50):
    """Export label summaries for a drug name to DataFrame."""
    r = requests.get(f"{BASE}/spls.json",
                     params={"drug_name": drug_name, "pagesize": pagesize},
                     timeout=15)
    r.raise_for_status()
    data = r.json()
    df = pd.DataFrame(data["data"])
    df["drug_query"] = drug_name
    return df, data["metadata"]["total_elements"]

df, total = export_drug_labels("amoxicillin")
print(f"Retrieved {len(df)} of {total} amoxicillin labels")
df[["setid", "title", "published_date"]].to_csv("amoxicillin_labels.csv", index=False)
print(f"Saved: amoxicillin_labels.csv  (columns: {list(df.columns[:5])})")
```

## Troubleshooting

| Problem | Cause | Solution |
|---------|-------|----------|
| `404 Not Found` on `/spls/{setid}` | Invalid or retired set ID | Re-search by drug name to get current set IDs |
| Empty `data` list from search | Drug name not matching any labels | Try shorter name (e.g., `"metformin"` not `"metformin HCl tablets"`); check spelling |
| Missing sections in SPL JSON | Older labels may not have all LOINC-coded sections | Check `len(sections)` before iterating; fall back to XML format for legacy labels |
| `ConnectionError` / timeout | DailyMed server overload | Retry with exponential backoff; increase `timeout=30` |
| Pagination returning duplicates | Page boundary race condition | De-duplicate results by `setid` after all pages collected |
| Packaging endpoint returns empty | Label exists but has no packaging records | Some biologics and vaccines lack packaging data; use label data directly |
| RxCUI lookup returns no results | RxCUI not mapped in DailyMed | Verify RxCUI via RxNorm API before querying; some experimental drugs are not mapped |

## Related Skills

- `fda-database` — openFDA for adverse event reports (FAERS), drug recalls, and enforcement actions
- `ddinter-database` — drug-drug interaction severity and mechanisms from DDInter
- `drugbank-database-access` — comprehensive drug information including targets, pathways, and chemical properties
- `clinicaltrials-database-search` — ClinicalTrials.gov for clinical trial data on drugs

## References

- [DailyMed](https://dailymed.nlm.nih.gov/dailymed/) — official drug label repository (NLM/NIH)
- [DailyMed Web Services API v2](https://dailymed.nlm.nih.gov/dailymed/webservices.cfm) — complete REST API documentation
- [SPL Standard (HL7)](https://www.hl7.org/implement/standards/product_brief.cfm?product_id=4) — Structured Product Labeling standard documentation
- [LOINC Document Ontology](https://loinc.org/document-ontology/) — LOINC codes for SPL section identification
- [NLM DailyMed Overview](https://dailymed.nlm.nih.gov/dailymed/about-dailymed.cfm) — data coverage and update frequency
