OpenBilldrug price receipt

NADAC week of 2026-10-14

How OpenBill works out its numbers

This page is generated from the rules the OpenBill page runs (src/core/rules.js) and from data/drugs.json, built from the NADAC file as of 2026-10-14. Every count on it is computed from that file.

What OpenBill shows

Search for a drug and OpenBill shows, for each product:

Every one of those numbers carries its state, the file it came from and its dates.

What it does not show:

There is no warranty. The figures come from other people’s data, which can be wrong, and from rules that can have mistakes. If you find a wrong number, tell us.

How to read a card

A card is one product: every NDC (the 11-digit National Drug Code printed on the package) that NADAC lists under the same description. This build has 5,936 products made of 30,031 NDCs.

Every number on a card has a state label and a provenance line, always visible: the source, the file it was read from, the date it applies from (the effective date) and the date OpenBill downloaded the file (retrieved). A figure that covers a period, such as a Part D data year or a Part B quarter, shows the period; its effective date is the first day of that period. A figure OpenBill calculates from others, such as a spread, shows each input’s own date or period beside it (for example “NADAC eff. 2026-09-23 · Part D 2024-01-01 to 2024-12-31”), never only the oldest. The provenance panel, opened from the card, adds the reason for the state, the NADAC explanation codes, the NDCs and the formula.

When a figure is missing the card says “We don’t know yet” and why. It never shows a zero instead. When a source is older than its limit (freshness), the card labels it stale and still shows it.

The four states

Strongest first:

filed
Read from a published government file (NADAC, ASP, a fee schedule). Strongest.
published
From a payer's or provider's own published rate.
derived
Computed from a published rule (e.g. Medicare pays 80% of the fee schedule).
estimated
Inferred, with the reason recorded.

In this build all 38,503 records in data/drugs.json are filed: each is a number printed in a government file. The numbers the page calculates from them, such as a Part B price per NADAC unit or a spread, are derived or estimated: of the 19,302 spreads on cards, 25 are derived and 19,277 estimated, for the reasons in the rules. A pack total is the exception: multiplying a price by your quantity is arithmetic, so the total keeps the state of the price it multiplies.

An example from this build

ARIPIPRAZOLE 10 MG TABLET, 31 NDCs:

What pharmacies pay
$0.11173 per EA filed CMS NADAC · nadac-national-average-drug-acquisition-cost-20261014.csv · effective 2026-09-23 · retrieved 2026-10-17
For 30 EA
$3.35 filed (the state of the price it multiplies: pack total)
Medicare Part D average spending
$1.54994 per EA filed Medicare Part D · DSD_PTD_RY26_P04_V10_DY24_BGM.csv · 2024-01-01 to 2024-12-31 · retrieved 2026-10-18
The spread
Medicare paid 13.9× what pharmacies pay. estimated (why it is estimated or derived)
Cost-plus estimate for 30 EA
$8.85 estimated (how it is worked out)

The rules

These are the rules in src/core/rules.js, in the words the page uses. Every number OpenBill calculates links to its rule here.

Pack total

The price for the quantity you enter is the price per unit times that quantity, worked out exactly and then rounded to the nearest cent, with a half cent rounding up. That is your own arithmetic on a published price, not a new estimate, so the total keeps the label (filed, derived or estimated), the source and the dates of the price per unit it multiplies, and its explanation shows the multiplication. A total above zero but under half a cent is shown as “<$0.01”, never as $0.00.

Formula: pack total = quantity × price per unit, rounded half-up to the cent

Starting quantity

Before you type a quantity, each card fills one in for you. NADAC (National Average Drug Acquisition Cost, a weekly survey by CMS, the Centers for Medicare & Medicaid Services, of what retail pharmacies pay for drugs) prices each drug per EA (each: one tablet, capsule, patch or other single unit), per ML (milliliter) or per GM (gram). For an oral tablet or capsule priced per EA it starts at 30, a common one-month supply of a once-a-day medicine, with buttons for 30, 60, 90. That means every NDC of the product the FDA NDC Directory (the U.S. Food and Drug Administration’s list of drug packages, each identified by its NDC, the National Drug Code printed on the package) lists has a tablet or capsule dosage form of any kind (film coated, extended or delayed release, orally disintegrating, chewable), or is a lozenge or troche, with the route ORAL, or is a tablet or film with the route SUBLINGUAL or BUCCAL (taken under the tongue or against the cheek, and counted like one that is swallowed). When more than half of those NDCs are such a form and the product’s NADAC description says tablet or capsule, the others are treated as errors in FDA’s listing: the card starts at 30 and its explanation names each of them. For a product FDA does not list, its NADAC description must name such a form (TABLET, CAPSULE, CAPLET, SOFTGEL, ODT …) and no other form or route, except SL, SUBLINGUAL or BUCCAL next to a tablet or film. Anything else priced per EA, such as a kit, pen, syringe, vial, patch, inhaler or suppository, starts at 1, with buttons for 1, 2, 3: 30 of them could be many months of supply, and for some of them NADAC’s EA is the whole package, for others one dose in it. When neither FDA nor the NADAC description says what the product is, the card starts at 1 too and says so. For ML and GM it starts at the amount in one package, read from the package description in the FDA NDC Directory: “5 SYRINGE in 1 CARTON / 3 mL in 1 SYRINGE” is 15 mL. When that package is a wholesale pack (a shipping case, or a carton, box, package or tray of at least 10 identical bottles, tubes, vials, jars or ampules), it counts the amount in one of them instead: “40 CARTON in 1 CASE / 1 BOTTLE in 1 CARTON / 60 g in 1 BOTTLE” is 60 g, not 2,400 g. That is used only when every NDC of the product listed there gives the same amount; otherwise the whole packages decide, and when those differ too the card starts at 1. It is a starting point, not your prescription: change it to match what you were given.

Formula: per EA: 30 for an oral tablet, capsule, lozenge or troche, or a tablet or film taken under the tongue or against the cheek, else 1; per ML or GM: the amount in one package (in one inner unit of a wholesale case or multi-pack), when every package description of the product agrees; otherwise 1

Which Medicare price is compared

NADAC (National Average Drug Acquisition Cost) measures what retail pharmacies pay for a drug, and Medicare Part D is the part of Medicare that pays for drugs filled at pharmacies, so Part D comes first: the average Part D spent per unit in its latest data year, for the brand and generic name this drug matched. It is used only when it is per the same unit as NADAC and CMS (the Centers for Medicare & Medicaid Services, which publishes both files) has not flagged it as an outlier, meaning a figure distorted by a few unusual records. When the outlier flag is blank in the CMS file, OpenBill counts it as not flagged and says so. Otherwise OpenBill uses Medicare Part B, the part of Medicare that pays for drugs given in a clinic or doctor’s office: the current quarter’s payment limit, matched by the exact NDC (National Drug Code, the number on the package) through the CMS crosswalk and converted to the NADAC unit (the next rule). Otherwise there is no Medicare price, and the card says why. When both exist, the card shows Part D and its details also show Part B, with a note that Part B pays for a pharmacy drug only in specific settings, such as a drug given in a clinic.

Formula: Medicare price = Part D average per unit if usable, else Part B payment limit per unit if usable, else none

What the provenance panel says with every Part B figure: “Medicare Part B pays for a drug like this only in specific settings, such as a drug given in a clinic or doctor’s office or used with certain medical equipment; most prescriptions filled at a pharmacy are paid by Part D.”

And with a Part D figure whose outlier flag is blank: “The Part D file leaves the outlier flag for this drug blank. OpenBill counts a blank flag as not flagged, so the figure is used, but CMS has not said whether unusual records distort it.”

Part B payment limit per NADAC unit

Medicare Part B sets a payment limit per HCPCS billing unit. HCPCS is the coding system Medicare uses for billing, and a billing unit is an amount such as “10 MG”. NADAC is per tablet (EA), milliliter (ML) or gram (GM) instead. To compare the two, OpenBill uses the CMS NDC-HCPCS crosswalk, a CMS table that gives, for each NDC (National Drug Code, the number on the package), the billing code it is billed under and how many billing units the whole package holds. The price per NADAC unit is the limit times the billing units in the whole package, divided by the amount in the package (the amount in one item times the number of items). This is done only when OpenBill could confirm, from the package description in the FDA NDC Directory (the U.S. Food and Drug Administration’s list of drug packages), that the package amount is in the same unit NADAC uses.

Formula: price per unit = payment limit × billing units in the package ÷ (amount in one item × number of items)

Part B coinsurance

Coinsurance is the share of the Part B payment limit a patient owes after meeting the deductible: the payment limit per billing unit times the coinsurance percentage printed for that billing code in the CMS payment limit file (usually 20%). Deductibles, supplemental insurance and other coverage change what a person actually owes.

Formula: coinsurance = payment limit × coinsurance percentage ÷ 100

The spread

The spread compares the two prices: the Medicare price per unit divided by what pharmacies pay per unit (NADAC). 4.9× means Medicare paid 4.9 times what pharmacies pay; 0.8× means Medicare paid 20% less. Every number on a card is labelled filed (read from a government file), derived (calculated by a fixed rule from published figures) or estimated (inferred, with the reason written down). A spread computed from Part D is always labelled estimated, because Part D puts every strength and form sold under one name into a single average, covers a past calendar year rather than this week, and counts spending the way CMS defines it (including pharmacy dispensing fees, and before the discounts drug makers pay back). A spread computed from Part B is labelled derived only when a single company sells under that billing code (the next rule); otherwise it is estimated too.

Formula: spread = Medicare price per unit ÷ NADAC price per unit

When a Part B spread counts as derived

Part B sets one payment limit per billing code, worked out from the average sales price of every product billed under that code. A labeler is the company named on a drug’s label. When the CMS crosswalk lists only one labeler for the code, the limit describes that company’s own product, so the spread is derived. CMS sometimes writes one company’s name more than one way on the same code (“UCB, INC.” and “UCB, Inc.”); names that differ only in capitals, punctuation, spacing or a closing Inc, LLC, Ltd, Corp or Co count as one labeler, and the explanation quotes each spelling. When several labelers share the code, the limit is an average across all their products and can sit well above or below the price of this one package, so the spread is estimated, and its explanation says how many labelers share the code.

Formula: derived when exactly one labeler (one company, however its name is spelled) is listed for the billing code on the crosswalk; otherwise estimated

Very large or very small spreads

An estimated spread of 20× or more, or 0.2× or less, also carries a warning that the two figures may not describe the same unit or product: for example, Part D may count a unit differently from NADAC, or average in strengths that cost far more or far less than this one. The number is still shown, with the warning; it is never hidden. But the card does not lead with a sentence such as “Medicare paid 99% less than pharmacies pay” for it: its headline becomes “These two figures may not measure the same thing”, the multiple is shown beside it, and the warning says why.

Formula: estimated spread ≥ 20× or ≤ 0.2× → add a warning; the card’s headline becomes “These two figures may not measure the same thing”

The thresholds are tested on the ratio at full precision, before it is rounded for display. A ratio of 0.2046 is above 0.2, so it carries no warning and keeps its sentence, and the card shows it as 0.205×: near a threshold the multiple gets the digits that keep it from reading as the threshold itself.

A product far from the others under its Part D name

Part D gives one average for every product sold under a brand and generic name, weighted by claims. When one product costs far more than the others under that name (a specialty formulation or an uncommon strength of a cheap generic, for example), the average can mostly reflect what the others cost, and a spread against it then says little about this product. So OpenBill compares the middle NADAC price of this product’s NDCs with the middle of the other products’ own middle prices under the same name, per the same unit (the lower of the two middle prices when there is an even number, so the comparison is between real filed prices). When this product is 20 or more times the others, the spread says so with both prices. The spread is still shown; the join is not refused, because many such names really do average strengths that sit close together.

Formula: middle NADAC of this product ÷ middle of the other products’ middle NADACs under the same Part D name ≥ 20 → add a warning

Cost-plus estimate

No free source of the prices pharmacies charge people paying cash may be reused: the companies and pharmacies that publish those prices forbid storing or copying them. So OpenBill shows no one’s cash price. Instead it works out what a pharmacy would charge if it priced the way Cost Plus Drugs, an online pharmacy, publicly describes (what it pays for a drug, plus a 15% markup, plus a flat pharmacy fee) and paid what NADAC says pharmacies pay: the pack total at NADAC, rounded to the cent, plus 15% of it, rounded to the cent, plus $5.00 per fill. For example, if pharmacies pay $10.00 for a pack, the estimate is $10.00 + $1.50 + $5.00 = $16.50. It is always labelled estimated. It is a model, not a price any pharmacy quoted, and not a typical price: Cost Plus Drugs’ own costs differ from NADAC, other pharmacies price differently, and shipping is not included. OpenBill copies no price from Cost Plus Drugs. There is no spread against this estimate, because it is built from NADAC itself: for a cheap generic the $5.00 fee alone can make it many times NADAC.

Formula: cost-plus estimate = quantity × NADAC price per unit, rounded half-up to the cent, + 15% of that, rounded half-up to the cent, + $5.00 per fill (shipping not included)

The parameters (COST_PLUS in src/core/rules.js)
Applied towhat pharmacies pay for the pack: quantity × NADAC per unit, rounded half-up to the cent
Markup15% of that, rounded half-up to the cent
Pharmacy fee$5.00 per fill
Shippingnot included
Stateestimated, always

Where the parameters come from: Cost Plus Drugs publicly describes how it sets its prices: what it pays for a drug, plus a 15% markup, plus a pharmacy labor fee, plus shipping. KFF Health News coverage of the company (February 2023) describes the same method, with a lower labor fee than today’s. The $5.00 fee is the one the company’s own website showed on 2026-10-18. OpenBill takes only those two parameters, the 15% markup and the $5.00 fee per fill, and applies them to NADAC in place of Cost Plus Drugs’ own cost, which it does not publish per drug. It copies no price from Cost Plus Drugs.

The two sources do not give the same fee. The KFF Health News article of February 2023 reports a $3 labor fee per drug and $5 shipping per order; the $5.00 fee here is the one Cost Plus Drugs’ website showed on 2026-10-18. If the company changes its fee or markup, OpenBill changes COST_PLUS and this date together.

What the estimate is not:

  • This is a model, not a price any pharmacy quoted.
  • Cost Plus Drugs’ own costs differ from NADAC, and other pharmacies price differently.
  • Shipping is not included.
  • It is not a price from Cost Plus Drugs or from any other pharmacy. OpenBill copies, stores and shows none of their prices.
  • It is not compared with NADAC as a spread. It is NADAC plus a markup and a fee, so the multiple would only measure the fee. The headline multiple on a card is always Medicare’s price ÷ what pharmacies pay.

In this build, for 30 EA of ARIPIPRAZOLE 10 MG TABLET: pharmacies pay $3.35; 15% of that, rounded to the cent, is $0.50; with the $5.00 fee the estimate is $8.85.

5,924 of 5,936 products have a cost-plus estimate at their starting quantity. The others show “We don’t know yet”, for the same reason as their pack total.

What you paid

You can type what you were charged for the pack shown on a card. The card then shows it as a multiple of what pharmacies pay for that pack and of the cost-plus estimate, labelled as your number: 3.6× means you paid 3.6 times as much. When a product has several NDCs at different prices, the multiple is a range. The amount must be more than $0 and at most $100,000.00, in dollars and cents; otherwise the card says why and shows no multiple. Your number is worked out in your browser. OpenBill never sends or saves it; it is kept in this page’s address after the #, so your browser’s history keeps it like any other page you visit. Browsers do not send the part after the # to the website; it is there so you can share your own card if you choose to, and a link you share carries your amount. Insurance, coupons and discounts change what different people pay, so a multiple is a question to ask your pharmacy, not proof of a mistake.

Formula: your multiple = what you paid ÷ what pharmacies pay for the pack; and what you paid ÷ the cost-plus estimate for the pack

  • The amount must be more than $0 and at most $100,000.00, in dollars and cents (at most two digits after the point). A “$” and thousands commas are fine. For anything else the card says why and shows no multiple; it never repeats what you typed.
  • The card labels the multiples “your number”: the amount is yours, not a figure from a file.
  • The arithmetic runs in your browser. OpenBill never sends or saves it; it is kept in this page’s address after the #, so your browser’s history keeps it like any other page you visit. The amount is written after the # in the address as &paid=12.00; browsers do not send that part to the website. That lets you share your own card. A link you share carries your amount, so share it only if you mean to.
  • Only the interactive card has this box. The drug pages have no script.

For example, if you type $12.00 on the card for 30 EA of ARIPIPRAZOLE 10 MG TABLET, it says: “You paid 3.6× what pharmacies pay for this pack, and 1.4× the cost-plus estimate.” The amount is an illustration, not anyone’s price.

Freshness

Each figure’s age is counted in whole days up to today: for NADAC from the date of its weekly file (the “as of” date), for Medicare Part B from the first day of the quarter its payment limits apply to, and for Medicare Part D from the last day of its data year. A figure older than its source’s limit (14 days for NADAC, 100 days for Part B, 913 days for Part D) is labelled stale on the card; it is never hidden or replaced by a zero. If a date comes out later than today, which a device’s clock or time zone can cause, the age is shown as 0 days and the figure is not stale.

Formula: age = today − the source’s date, in whole days; stale when age > the source’s limit

How prices are presented

OpenBill holds every screen to these five rules, word for word:

The drug pages

On the website, each product also has a page of its own, and there is a page for each drug class and an A to Z index. These pages are built from data/drugs.json with the same rules as the card each time the site is published; they are not kept in the repository. They have no script. Their numbers are in the page itself, each with its state and source line, and each page links to the interactive card for its product, where you can change the quantity or enter what you paid. Start from the A to Z index of every drug.

Sources

Every number comes from one of these files. data/drugs.json records each file’s sha256, so you can check that you have the same bytes. The licences and terms, the fields read and the caveats of each file are in DATA_SOURCES.md.

CMS NADAC

The national average price retail pharmacies pay to buy a drug from their suppliers (their acquisition cost), per unit, from a CMS survey of pharmacy invoices.

DatasetNADAC (National Average Drug Acquisition Cost) 2026
Published byCenters for Medicare & Medicaid Services, on data.medicaid.gov
Publisher in the file’s metadatadata.medicaid.gov
Dataset pagedata.medicaid.gov
Filenadac-national-average-drug-acquisition-cost-20261014.csv (107,347,500 bytes)
sha25615335686bbc0…
Periodnone: not period data
File date (as of)2026-10-14
Retrieved (UTC)2026-10-17
New fileevery week
Stale after14 days, counted from the file date. NADAC is published every week. A file more than 14 days old means at least one weekly update was missed, so the price may have changed.
Licencewww.usa.gov/publicdomain/label/1.0/
Weekly files in the download41; OpenBill uses the newest

FDA NDC Directory

The FDA listing of each drug package: its NDC, brand and generic names, dosage form, strength, labeler and package description. It identifies drugs; it has no prices.

DatasetNational Drug Code Directory (NDC database file, text version)
Published byU.S. Food and Drug Administration
Publisher in the file’s metadataU.S. Food and Drug Administration (FDA), Center for Drug Evaluation and Research
Dataset pagewww.fda.gov
Filendctext.zip (10,865,102 bytes)
sha256ee609cef6e4e…
Periodnone: not period data
File date (as of)2026-10-16
Retrieved (UTC)2026-10-17
New fileevery day
Stale after30 days, counted from the file date. The FDA updates the directory every business day. It only supplies names and package details, which change slowly, so it is shown as stale after 30 days rather than after one missed day.
Licencewww.fda.gov/about-fda/about-website/website-policies
Directory size116,607 products, 219,166 packages

Medicare Part D

What Medicare Part D spent on a drug in one calendar year, averaged per dosage unit across every strength and form sold under the same brand and generic name.

DatasetMedicare Part D Spending by Drug, data year 2024
Published byCenters for Medicare & Medicaid Services, on data.cms.gov
Publisher in the file’s metadataCenters for Medicare & Medicaid Services (CMS), Office of Enterprise Data and Analytics (OEDA)
Dataset pagedata.cms.gov
FileDSD_PTD_RY26_P04_V10_DY24_BGM.csv (5,326,806 bytes)
sha2565dcc9d7bd7d7…
Period2024-01-01 to 2024-12-31
File date (as of)2026-06-25
Retrieved (UTC)2026-10-18
New fileonce a year
Stale after913 days, counted from the last day of the data year. Part D spending is published once a year, about 18 months after the data year ends (about 548 days), and the next year follows about a year after that. So a data year is the newest one available until roughly 2.5 years (913 days) after it ends; older than that, a newer year should exist.
Licencewww.usa.gov/government-works
CMS documentationmethodology, data dictionary

Medicare Part B

The most Medicare Part B pays for a drug given in a clinic or doctor’s office, per HCPCS billing unit, for one calendar quarter, and the coinsurance percentage for it.

DatasetOctober 2026 Medicare Part B Payment Limit Files
Published byCenters for Medicare & Medicaid Services, on cms.gov
Publisher in the file’s metadataCenters for Medicare & Medicaid Services (CMS)
Dataset pagewww.cms.gov
Fileoctober-2026-medicare-part-b-payment-limit-files-final.zip (85,594 bytes); read from section 508 version of October 2026 Medicare Part B Payment Limit File 091626.csv
sha256a9b7afa80aae…
Period2026-10-01 to 2026-12-31
File date (as of)2026-09-17
Retrieved (UTC)2026-10-18
New fileevery quarter
Stale after100 days, counted from the first day of the quarter. Part B payment limits are set for one calendar quarter (about 91 days). Once more than 100 days have passed since the quarter began, the next quarter’s file should be in use.
Licencewww.usa.gov/government-copyright

Medicare Part B crosswalk

Which HCPCS billing code each NDC is paid under in Medicare Part B, and how many billing units are in one package of that NDC.

DatasetOctober 2026 NDC-HCPCS Crosswalk
Published byCenters for Medicare & Medicaid Services, on cms.gov
Publisher in the file’s metadataCenters for Medicare & Medicaid Services (CMS)
Dataset pagewww.cms.gov
Fileoctober-2026-ndc-hcpcs-crosswalk-final.zip (426,375 bytes); read from section 508 version of October 2026 ASP NDC-HCPCS Crosswalk 091126.csv
sha256e6bbf7c61e6d…
Period2026-10-01 to 2026-12-31
File date (as of)2026-09-17
Retrieved (UTC)2026-10-18
New fileevery quarter
Stale after100 days, counted from the first day of the quarter. The NDC-HCPCS crosswalk is published with each quarter’s payment limits. Once more than 100 days have passed since the quarter began, the next quarter’s file should be in use.
Licencewww.usa.gov/government-copyright

How the files are joined

The NADAC file is the spine: one row per NDC in its newest week, 30,031 rows in this build. The other files are joined to it on exact keys. A join that is not certain is left empty rather than guessed; each empty join is counted below and explained on the card.

Identity: the FDA NDC Directory

Each NDC is looked up, digit for digit, in the FDA NDC Directory’s package list. FDA writes package NDCs with hyphens in three layouts (4-4-2, 5-3-2 and 5-4-1); the short segment is padded with a zero to make the 11 digits NADAC uses. A match gives the brand and generic names, dosage form, route, strength, labeler and package description.

27,083 of 30,031 rows (90.2%) match. The other 2,948 rows have no identity: their NDC is not in the directory’s package list (discontinued products leave it, for example), its package has no product record, or its FDA entries conflict with each other (0 NDCs in this build).

For the Part D name join only, a row with no identity can be matched using FDA’s record for its 9-digit product code, when that code has one product record and a sibling NDC of the same description is in the directory, or the NADAC description begins with FDA’s trade name or prints its name and strength. That record is not an identity: the row’s identity stays empty. Its route is used for one thing only: the multi-route rule below, for a Part D name marked *. In this build it was tried for 2,035 rows, and 1,385 of them joined a Part D name.

Medicare Part D: by name

Part D Spending by Drug has no NDCs. CMS lists each drug by brand name and generic name, with every strength and form together, so this join is by name: the NADAC description and, when the NDC has an FDA identity, FDA’s brand and generic names, against the names CMS uses. It is strict on purpose. A row gets no Part D figure when:

In this build 263 rows with no FDA identity took their route from the product record for a name marked *, and 146 of them kept the Part D figure because of it. Each of those rows says so in its provenance panel, naming the route, FDA’s product NDC as FDA writes it and, where FDA prints this NDC inside one of the product’s listed packages, that package; the name’s reason says how many of its NDCs took their route this way. The others were left out: 58 priced in a unit that is not the one nearly all of the name’s rows share, 55 on another route than the one the name’s rows share, 4 in a name whose rows tie between routes. 108 rows still have no known route and stay out.

19,277 of 30,031 rows (64.2%) match, pointing at 1,458 Part D names. For 17,111 rows CMS’s brand name is the generic name itself; for 2,166 rows it is a trade name. The exact rules are in tools/build_drugs.py, and the cases they were written for are in DATA_SOURCES.md.

Rows with no Part D figure, by reason
WhyRowsShare
NADAC prices the NDCs joined to this Part D name in more than one unit and this NDC’s unit is not the one nearly all of them share, so CMS’s average per dosage unit is not in this NDC’s unit2,7399.1%
no Part D row with this generic name and release form2,0406.8%
more than one Part D row carries this product name and release form1,6255.4%
OTC product sold under an FDA OTC monograph; Part D excludes OTC drugs, so a Part D row of this name is a different product1,3204.4%
OTC product with no Part D row of exactly this name (Part D excludes OTC drugs)1,0193.4%
the Part D name carries CMS’s * (an average over several routes of administration) and this NDC’s FDA route is not the one most NDCs joined to the name share5011.7%
a Part D row has this name but its generic name disagrees with FDA’s nonproprietary name3231.1%
brand product with no Part D row of this brand name and release form3031.0%
FDA’s dosage form or name states a release form (extended or delayed release) the NADAC description does not, or the reverse2961.0%
Part D has rows for this substance under different salts (for example Buprenorphine and Buprenorphine HCl) and neither the NADAC description nor FDA’s nonproprietary name settles which1750.6%
OTC product whose name matches only a Part D row named by the generic name, which is the prescription product (Part D excludes OTC drugs)1520.5%
more than one Part D generic row could match1080.4%
the Part D name carries CMS’s * (an average over several routes of administration) and this NDC has no FDA identity with a route, and no accepted FDA product record for its product code with one, so its route is unknown1080.4%
the Part D name carries CMS’s * (an average over several routes of administration) and the NDCs joined to it split evenly between routes, so no route is shared by most of them440.1%
Part D lists presentations of this name separately (for example vial and pen) and the NADAC description does not settle which one this NDC is1<0.1%

CMS flags 61 Part D names as outliers: the average is substantially affected by unusual records. The 742 rows pointing at them keep the figure in the data but do not use it as the reference price. The flag is blank for 1 name (1 row); a blank flag counts as not flagged, and the provenance panel says so in the figure’s reason.

Medicare Part B: by exact NDC

The CMS NDC-HCPCS crosswalk lists which NDCs are billed to Part B under which HCPCS code, and how many billing units one package holds. An NDC is joined to it digit for digit; an NDC on crosswalk lines that disagree with each other is left out. The HCPCS code then gives the payment limit and the coinsurance percentage from the payment limit file.

To compare a payment limit with NADAC, the package has to be measured in NADAC’s unit. The crosswalk does not say which unit its package size is in, so OpenBill accepts it only when FDA’s package description for the same NDC gives the same total (milliliters, grams, or a count of tablets, vials and so on) and the crosswalk’s two billing-unit columns agree with each other. A kit that NADAC prices per EA is counted as whole kits. Where the two billing-unit columns disagree, one is used only when FDA’s strength × the package amount ÷ the HCPCS dosage gives exactly that number. Otherwise the package size is shown as the crosswalk prints it, a number with no unit, and the NDC gets no Part B price per NADAC unit.

1,427 of 30,031 rows (4.8%) are on the crosswalk, under 138 HCPCS codes; 1,338 of them (93.8%) have the package unit reconciled and can be converted to a price per NADAC unit: 1,289 because FDA’s package description gives the same total as the crosswalk; 32 because the NDC is a kit NADAC prices per EA, so the count is of whole kits; 17 because the crosswalk’s billing-unit columns disagree and FDA’s strength × amount ÷ the HCPCS dosage settles them exactly.

Rows with no Part B figure, by reason
WhyRowsShare
the NDC is not on the current quarter’s NDC-HCPCS crosswalk (most drugs filled at a pharmacy are not billed to Part B)28,58795.2%
the NDC is on more than one crosswalk line, and the lines disagree on the HCPCS code or the package figures170.1%

A payment limit is one figure per code. 38 of the 138 codes have a single labeler (the company named on the label) on the crosswalk, and 77 rows point at them. For the others the limit averages every labeler’s product billed under the code (when a Part B spread counts as derived). CMS spells some labeler names more than one way on the crosswalk: 6 codes list one company under names that differ only in capitals, punctuation or a suffix such as Inc or LLC. OpenBill counts each company once, and the provenance panel quotes every spelling; 2 of those codes have a single labeler only once the spellings are counted together.

What the cards end up showing

With these joins and the reference rule: 18,535 rows (61.7%) have a Part D reference price, 767 (2.6%) a Part B one and 10,729 (35.7%) none. By product: 3,075 Part D, 180 Part B, 2,681 none, of 5,936. 571 rows with a Part D reference also show a Part B figure in the provenance panel.

Every Part D spread is estimated (18,535). Of the Part B spreads, 25 are derived and 742 estimated. 387 estimated spreads at or beyond 20× or 0.2× carry the large-gap warning; on those cards the headline is “These two figures may not measure the same thing” instead of the spread’s sentence, with the multiple beside it. The full tables are in DATA-QUALITY.md.

NADAC explanation codes

NADAC marks each price with one or more explanation codes. Their meanings below are from the NADAC data dictionary. Codes 2 and 4 mean the price was carried forward from an earlier file rather than set by the latest survey; OpenBill marks those rows and shows every code in the provenance panel.

NADAC explanation codes in this build
CodeMeaningRows
1calculated from the most recently completed pharmacy survey29,061
2the latest survey was within 2% of the current NADAC, so the NADAC was carried forward from the previous file (carried forward)45
3the survey-based NADAC was adjusted for changes in published pricing, or after a help-desk inquiry15
4carried forward from the previous file (carried forward)910
5calculated based on package size5,962
6the CMS Covered Outpatient Drug Product File S/I/N drug category was not applied when grouping drugs for the calculation1,295

Known limitations

Reproduce it

You need Python 3.10 or later (standard library only) and Node 20 or later. From a copy of the repository (on Windows the Python command is usually python rather than python3):

python3 tools/fetch_all.py
python3 tools/build_drugs.py
node build.js
node build.js --check
node --test

fetch_all.py downloads every source into tools/cache/ and records each file’s sha256; with --offline it only checks the files already there. build_drugs.py reads the cache, never the network, and writes data/drugs.json. build.js writes openbill.html, this page and docs/DATA-QUALITY.md. Nothing generated contains a build time, so the same files give the same bytes. build.js --check fails if a generated file is out of date, and node --test runs the unit, data, spot-check, quality and link tests.

Government sites replace their files. To reproduce these exact numbers, your 5 source files must have the sha256 values listed under sources. A newer file gives newer numbers; that is a data refresh, not a mismatch.

Report a wrong number

Every card has a “Wrong? Tell us” link. It opens the wrong-price issue form on GitHub with the card’s details filled in. You can also open the wrong-price form yourself.

The most useful report names the source file and the row in it that shows the right value. A receipt or a price from a website is useful context, but it does not show that a government file was read wrong. Please don’t include your own or anyone else’s health information.

How reports are checked and corrected: CONTRIBUTING.md and GOVERNANCE.md.