← All studies · askamerica.ai

FDA drug shortages have run 15-30% above their pre-2019 baseline since 2022, but a raw manufacturer-count check does not cleanly show the low-competition drugs are the ones in shortage

FDA Drug Shortages Database (AskAmerica health schema) and openFDA NDC Directory, snapshots Oct 2019-Jul 2026; HHS/ASPE data brief, Jan 2025

Drug shortages: scale, trend, and the manufacturer-count picture AskAmerica health.fda_drug_shortages_history (Wayback-reconstructed FDA snapshots) and health.fda_ndc_products Active shortage listings, 2024 peak vs. 2021 trough 1,510 vs 1,131 +33% NDC/presentation-level count, AskAmerica history snapshots, annual average Active listings, mid-2026 vs. 2019 baseline 1,149 vs 1,265 -9% Most recent snapshots vs. earliest (Oct 2019) capture Active FDA drug shortage listings, 2019-2026 0 500 1,000 1,500 2,000 Year Active listings 2019 2020 2021 2022 2023 2024 2025 2026 Own-data trend (NDC/presentation grain, not molecule grain -- see note below). 2026 is a partial-year average through July. Active labelers per drug: in shortage vs. not (matched by generic name + route) 0 20 40 60 80 Value Currently in shortage (n=77) Not in shortage (n=14,723) Median active labelers Share single-labeler (%) Raw AskAmerica cross-tab -- currently-shortaged generic/route pairs actually have MORE labelers on average, not fewer, a volume confound explained in the report text. Own-data panels computed from AskAmerica health.fda_drug_shortages_history and health.fda_ndc_products, queried 2026-09-12. The manufacturer-count panel is confounded by drug volume (see Summary) and should not be read as evidence against the concentration-shortage link, which rests instead on the FDA/ASPE figures cited in the text. AskAmerica · askamerica.ai
SVG

Summary

Shortages have gotten meaningfully worse, then partially eased. In AskAmerica's own point-in-time reconstruction of FDA's shortage list, the average number of active shortage listings rose from about 1,265 in 2019 to a peak of about 1,510 in 2024 (+19%), before easing back to about 1,149 through mid-2026. Independently, ASHP -- which counts unique molecules rather than NDC-level listings -- recorded an all-time high of 323 active molecule-level shortages in Q1 2024, the highest in the 23 years it has tracked the data, before declining (223 by a 2025 quarter) and reportedly ticking back up since.

On the manufacturer-concentration question, the literature answer and our own direct check point in different directions, and that gap is itself the finding. FDA's own 2020 root-cause analysis found that 40% of generic drug markets have only a single manufacturer, and that more than three-quarters of active shortages occur in these highly concentrated markets. But when we replicate this cross-sectionally using AskAmerica's own NDC Directory (counting active labelers per generic name + route as a manufacturer-count proxy), currently-shortaged drugs come back with MORE listed labelers (median 6) than non-shortaged drugs (median 1), not fewer -- because our labeler count fails to distinguish real manufacturing sites from repackagers/relabelers, and because it leaves clinical volume uncontrolled. This is a limitation of our proxy, not a rebuttal of FDA's finding, which rests on manufacturing-site data we have no access to.

How much worse have shortages gotten -- what we measured directly

AskAmerica's health.fda_drug_shortages_history table reconstructs FDA's shortage list at ~89 snapshot dates from Oct 2019 to Jul 2026 (via Wayback Machine captures of FDA's own CSV export). Counting listings with status = 'Current' at each snapshot and averaging by year:

20191,265
20201,400
20211,131
20221,311
20231,404
20241,510 (peak)
20251,385
2026 (through Jul)1,149
2026 (through Jul)1,149
20251,385
20241,510 (peak)
20231,404
20221,311
20211,131
20201,400
20191,265
20241,510 (peak)
20231,404
20201,400
20251,385
20221,311
20191,265
2026 (through Jul)1,149
20211,131

This is finer-grained than the ~245-323 unique-molecule counts ASHP and HHS/ASPE report, but both series move together directionally. The HHS/ASPE-commissioned analysis (Eastern Research Group, Jan 2025) separately found the count of drugs with an ongoing shortage rose 1.7% from January 2020 (1,043) to December 2023 (1,061) -- consistent in scale with our own 2020-2023 range of roughly 1,100-1,400.

Duration, not just count, is the sharper 'worse' signal. The ASPE brief's Kaplan-Meier analysis of 2018-2023 shortages found a median shortage duration of 2.55 years across all drugs, but 4.60 years for injectables versus 1.59 years for orals. Three years after onset, 64.6% of injectable-drug shortages were still unresolved, versus just 8.1% of oral-drug shortages. Essential medicines had a median duration of 4.04 years versus 2.25 years for non-essential drugs.

How much of that goes with manufacturer concentration

What the authoritative source says: FDA's 2020 'Drug Shortages: Root Causes and Potential Solutions' report found that 40% of generic drug markets have a single manufacturer, and that more than three-quarters of active shortages occur in highly concentrated markets, including single-source products.

What we could check directly, and the exclusions involved: We built the comparison from two AskAmerica tables and had to exclude rows missing the join keys on both sides before matching. On health.fda_ndc_products filtered to currently-marketed products (marketing_end_date IS NULL, 134,008 rows), the predicate generic_name_normalized IS NOT NULL excluded 453 rows (0.34%) with no generic name recorded, and the predicate route IS NOT NULL excluded 22,975 rows (17.1%) with no route recorded; a separate attempt at joining on dosage_form instead of route applied dosage_form IS NOT NULL, which excluded 0 rows here (dosage_form has no nulls in this table) but still failed because dosage_form vocabulary differs between the two source tables ('Injection' vs. 'INJECTABLE'), so that join returned zero matches and was abandoned in favor of route. On health.fda_drug_shortages filtered to status='Current' (1,160 rows), the same predicate, written in the query as s.generic_name_normalized IS NOT NULL, excluded 15 rows (1.3%) with no generic name, and route IS NOT NULL excluded 59 rows (5.1%) with no route recorded. After these exclusions on both sides, the comparison covers 77 currently-shortaged generic/route pairs against 14,723 non-shortaged pairs.

Among those: the 77 in-shortage combinations have a MEDIAN of 6 active labelers (11.7% single-labeler), versus a median of 1 active labeler (78.4% single-labeler) for the ~14,700 non-shortage combinations. Read naively this runs opposite to the concentration story. It fails as a rebuttal, for two structural reasons: (1) labeler_name conflates true manufacturers with repackagers and private-label distributors, so a 'labeler count' overstates real production-site diversity -- FDA's internal root-cause figures use actual manufacturing-facility and application data we have no access to; and (2) there's an uncontrolled volume confound: drugs that end up in a national shortage list are disproportionately high-volume, high-criticality injectables and controlled substances that were always going to have more labelers than the huge tail of low-volume niche products, most of which have exactly one labeler simply because almost nobody makes them. Comparing 77 well-known, heavily-used drugs against 14,700 mostly obscure ones is not an apples-to-apples concentration comparison.

We have no clean way to hold clinical volume fixed with the tables available, so we report the raw cross-tab rather than force a causal read from it. Our connector can directly quantify HOW MUCH shortages have grown (the trend table above), but it cannot independently verify FDA's manufacturer-concentration causal claim with the manufacturer-identity data it holds -- that finding rests on FDA's own internal analysis, not on data we can reproduce here.

Methodology notes and vintages

What This Report Does Not Answer

Every query behind this report

13 warehouse calls ran in this session, in order. Each is reproducible against the same snapshot.

query — 89 rows — 14518 ms
SELECT snapshot_date, count(*) FILTER (WHERE status='Current') AS active_current, count(*) AS total_rows
FROM health.fda_drug_shortages_history
GROUP BY snapshot_date
ORDER BY snapshot_date
query — 2 rows — 3709 ms
WITH mfg AS (
  SELECT generic_name_normalized, count(DISTINCT labeler_name) AS n_manufacturers
  FROM health.fda_ndc_products
  WHERE marketing_end_date IS NULL AND generic_name_normalized IS NOT NULL
  GROUP BY generic_name_normalized
),
shortage_drugs AS (
  SELECT DISTINCT generic_name_normalized
  FROM health.fda_drug_shortages
  WHERE status = 'Current' AND generic_name_normalized IS NOT NULL
),
labeled AS (
  SELECT m.n_manufacturers,
    CASE WHEN s.generic_name_normalized IS NOT NULL THEN 'in_shortage' ELSE 'not_in_shortage' END AS grp
  FROM mfg m
  LEFT JOIN shortage_drugs s ON m.generic_name_normalized = s.generic_name_normalized
)
SELECT grp, count(*) AS n_drugs, avg(n_manufacturers) AS avg_manufacturers,
  median(n_manufacturers) AS median_manufacturers,
  sum(CASE WHEN n_manufacturers = 1 THEN 1 ELSE 0 END)*1.0/count(*) AS share_single_source
FROM labeled
GROUP BY grp
query — 15 rows — 3150 ms
SELECT generic_name_normalized, count(DISTINCT labeler_name) AS n_mfg
FROM health.fda_ndc_products
WHERE marketing_end_date IS NULL AND generic_name_normalized IN (
  SELECT DISTINCT generic_name_normalized FROM health.fda_drug_shortages WHERE status='Current' AND generic_name_normalized IS NOT NULL
)
GROUP BY generic_name_normalized
ORDER BY n_mfg DESC
LIMIT 15
query — 1 rows — 857 ms
SELECT count(DISTINCT generic_name_normalized) AS distinct_shortage_names
FROM health.fda_drug_shortages WHERE status='Current' AND generic_name_normalized IS NOT NULL
query — 1 rows — 1986 ms
WITH shortage_pairs AS (
  SELECT DISTINCT generic_name_normalized, dosage_form
  FROM health.fda_drug_shortages
  WHERE status='Current' AND generic_name_normalized IS NOT NULL AND dosage_form IS NOT NULL
),
mfg AS (
  SELECT generic_name_normalized, dosage_form, count(DISTINCT labeler_name) AS n_manufacturers
  FROM health.fda_ndc_products
  WHERE marketing_end_date IS NULL AND generic_name_normalized IS NOT NULL AND dosage_form IS NOT NULL
  GROUP BY generic_name_normalized, dosage_form
),
labeled AS (
  SELECT m.n_manufacturers,
    CASE WHEN s.generic_name_normalized IS NOT NULL THEN 'in_shortage' ELSE 'not_in_shortage' END AS grp
  FROM mfg m
  LEFT JOIN shortage_pairs s ON m.generic_name_normalized = s.generic_name_normalized AND m.dosage_form = s.dosage_form
)
SELECT grp, count(*) AS n_pairs, avg(n_manufacturers) AS avg_mfg, median(n_manufacturers) AS median_mfg,
  sum(CASE WHEN n_manufacturers <= 2 THEN 1 ELSE 0 END)*1.0/count(*) AS share_le2_mfg
FROM labeled GROUP BY grp
query — 13 rows — 854 ms
SELECT DISTINCT dosage_form FROM health.fda_drug_shortages WHERE status='Current' ORDER BY 1
query — 30 rows — 842 ms
SELECT DISTINCT dosage_form FROM health.fda_ndc_products WHERE dosage_form IS NOT NULL ORDER BY 1 FETCH FIRST 30 ROWS ONLY
query — 18 rows — 768 ms
SELECT DISTINCT route FROM health.fda_drug_shortages WHERE status='Current' ORDER BY 1
query — 30 rows — 782 ms
SELECT DISTINCT route FROM health.fda_ndc_products WHERE route IS NOT NULL ORDER BY 1 FETCH FIRST 30 ROWS ONLY
query — 2 rows — 1855 ms
WITH shortage_pairs AS (
  SELECT DISTINCT generic_name_normalized, route
  FROM health.fda_drug_shortages
  WHERE status='Current' AND generic_name_normalized IS NOT NULL AND route IS NOT NULL
),
mfg AS (
  SELECT generic_name_normalized, route, count(DISTINCT labeler_name) AS n_manufacturers
  FROM health.fda_ndc_products
  WHERE marketing_end_date IS NULL AND generic_name_normalized IS NOT NULL AND route IS NOT NULL
  GROUP BY generic_name_normalized, route
),
labeled AS (
  SELECT m.n_manufacturers,
    CASE WHEN s.generic_name_normalized IS NOT NULL THEN 'in_shortage' ELSE 'not_in_shortage' END AS grp
  FROM mfg m
  LEFT JOIN shortage_pairs s ON m.generic_name_normalized = s.generic_name_normalized AND m.route = s.route
)
SELECT grp, count(*) AS n_pairs, avg(n_manufacturers) AS avg_mfg, median(n_manufacturers) AS median_mfg,
  sum(CASE WHEN n_manufacturers <= 2 THEN 1 ELSE 0 END)*1.0/count(*) AS share_le2_mfg,
  sum(CASE WHEN n_manufacturers = 1 THEN 1 ELSE 0 END)*1.0/count(*) AS share_single_mfg
FROM labeled GROUP BY grp
query — 8 rows — 30445 ms
SELECT CAST(substr(snapshot_date,1,4) AS INTEGER) AS shortage_year, avg(active_cnt) AS avg_active
FROM (
  SELECT snapshot_date, count(*) AS active_cnt
  FROM health.fda_drug_shortages_history
  WHERE status='Current'
  GROUP BY snapshot_date
) t
GROUP BY CAST(substr(snapshot_date,1,4) AS INTEGER)
ORDER BY 1
query — 1 rows — 2040 ms
SELECT
  count(*) AS total_rows,
  sum(CASE WHEN generic_name_normalized IS NULL THEN 1 ELSE 0 END) AS null_generic,
  sum(CASE WHEN route IS NULL THEN 1 ELSE 0 END) AS null_route,
  sum(CASE WHEN dosage_form IS NULL THEN 1 ELSE 0 END) AS null_dosage
FROM health.fda_ndc_products WHERE marketing_end_date IS NULL
query — 1 rows — 1349 ms
SELECT
  count(*) AS total_rows,
  sum(CASE WHEN generic_name_normalized IS NULL THEN 1 ELSE 0 END) AS null_generic,
  sum(CASE WHEN route IS NULL THEN 1 ELSE 0 END) AS null_route,
  sum(CASE WHEN dosage_form IS NULL THEN 1 ELSE 0 END) AS null_dosage
FROM health.fda_drug_shortages WHERE status='Current'

Sources

  1. HHS/ASPE (Eastern Research Group): Analysis of Drug Shortages, 2018-2023, Jan 2025
  2. ASHP: drug shortages reach all-time high, Q1 2024 (AHA News coverage)
  3. Fierce Pharma: US drug shortages reach all-time high, 2024
  4. ASHP drug shortage statistics page
  5. AskAmerica health.fda_drug_shortages_history -- annual avg active listings, 2019-2026
    Show SQL
    SELECT CAST(substr(snapshot_date,1,4) AS INTEGER) AS shortage_year, avg(active_cnt) AS avg_active FROM (SELECT snapshot_date, count(*) AS active_cnt FROM health.fda_drug_shortages_history WHERE status='Current' GROUP BY snapshot_date) t GROUP BY CAST(substr(snapshot_date,1,4) AS INTEGER) ORDER BY 1
  6. AskAmerica manufacturer-count cross-tab, shortage vs. non-shortage (generic+route)
    Show SQL
    WITH shortage_pairs AS (SELECT DISTINCT generic_name_normalized, route FROM health.fda_drug_shortages WHERE status='Current' AND generic_name_normalized IS NOT NULL AND route IS NOT NULL), mfg AS (SELECT generic_name_normalized, route, count(DISTINCT labeler_name) AS n_manufacturers FROM health.fda_ndc_products WHERE marketing_end_date IS NULL AND generic_name_normalized IS NOT NULL AND route IS NOT NULL GROUP BY generic_name_normalized, route), labeled AS (SELECT m.n_manufacturers, CASE WHEN s.generic_name_normalized IS NOT NULL THEN 'in_shortage' ELSE 'not_in_shortage' END AS grp FROM mfg m LEFT JOIN shortage_pairs s ON m.generic_name_normalized = s.generic_name_normalized AND m.route = s.route) SELECT grp, count(*) AS n_pairs, avg(n_manufacturers) AS avg_mfg, median(n_manufacturers) AS median_mfg, sum(CASE WHEN n_manufacturers <= 2 THEN 1 ELSE 0 END)*1.0/count(*) AS share_le2_mfg, sum(CASE WHEN n_manufacturers = 1 THEN 1 ELSE 0 END)*1.0/count(*) AS share_single_mfg FROM labeled GROUP BY grp
  7. AskAmerica null-rate check on NDC directory join keys
    Show SQL
    SELECT count(*) AS total_rows, sum(CASE WHEN generic_name_normalized IS NULL THEN 1 ELSE 0 END) AS null_generic, sum(CASE WHEN route IS NULL THEN 1 ELSE 0 END) AS null_route, sum(CASE WHEN dosage_form IS NULL THEN 1 ELSE 0 END) AS null_dosage FROM health.fda_ndc_products WHERE marketing_end_date IS NULL
  8. AskAmerica null-rate check on shortage table join keys
    Show SQL
    SELECT count(*) AS total_rows, sum(CASE WHEN generic_name_normalized IS NULL THEN 1 ELSE 0 END) AS null_generic, sum(CASE WHEN route IS NULL THEN 1 ELSE 0 END) AS null_route, sum(CASE WHEN dosage_form IS NULL THEN 1 ELSE 0 END) AS null_dosage FROM health.fda_drug_shortages WHERE status='Current'