health: every healthcare provider in the United States (NPPES) - #143
Merged
Merged
Conversation
An NPI is the number on every insurance claim filed in the US. Everyone who bills insurance has one, and NPPES is the register: about eight million clinicians and organizations with their specialty, licence state and location. CMS publishes it in full every month, public domain, no key and no terms. That is why it is here rather than scraped. A provider directory is normally assembled out of somebody's licensed database; this one is given away by the agency that maintains it. `/c/health` had openFDA recalls and clinical trials, about 3,200 items. The file was measured rather than assumed, by reading the zip's central directory and inflating only the first member's opening -- no 1.16 GB download to learn its shape. It holds a 4.29 GB CSV of 330 columns plus separate practice-location, endpoint and other-name files. V1 was retired 2026-03-03; V2 is the only one published. The walk is the CourtListener shape because the problem is identical: download with Range resume (http.download already does this), unzip once, walk a batch at a time saving the line offset after each, across as many runs as it takes. Columns are read by INDEX, because the header is 330 columns of prose and matching on it is slow and fragile. All sixteen indices were checked against the real header before being written down. Since a government file of that width is exactly the sort that gains a column, the header is re-checked on every run and a shift throws -- reading a first name out of the position that now holds a middle name would otherwise be invisible in the output. Two things the real data taught, both now tested. The archive ships a `_fileheader.csv` next to the data with the same stem, holding only the header row; picking it would look like an empty but successful walk. And 2,353 of the first 24,000 rows have a blank entity type and no name -- NPIs deactivated and stripped -- so they are dropped rather than stored nameless. The privacy decision is deliberate and defaults to cautious. Most records are organizations, but a large minority are sole practitioners who registered a HOME address as their practice location. It is public by law and CMS publishes it for reuse, so there is no legal question -- but republishing several million people's home addresses in a searchable index is a different act from republishing a hospital's, and it should be a decision rather than a side effect. Individuals keep city, state and a five-digit ZIP (the file carries ZIP+4, which narrows a home to roughly a building) and lose the street lines and phone; organizations keep everything. `includeIndividualStreet` turns it off and defaults to no. A test asserts a built item never carries an individual's street. Taxonomy codes are resolved through NUCC's published code set, fetched per run rather than vendored so a stale copy cannot quietly degrade every row to a bare `207X00000X`. 34 new tests, 2042 across the suite, lint clean. The usual 5 Redis/Postgres integration failures this box cannot run. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
An NPI is the number on every insurance claim filed in the US. Everyone who bills insurance has one, and NPPES is the register: ~8 million clinicians and organizations with their specialty, licence state and location. CMS publishes it in full monthly, public domain, no key, no terms.
That's why this is ingested rather than scraped. A provider directory is normally assembled out of somebody's licensed database — this one is given away by the agency that maintains it.
/c/healthpreviously had openFDA recalls and clinical trials, ~3,200 items.The file was measured, not assumed
I read the zip's central directory and inflated only the first member's opening — so its shape was established without downloading 1.16 GB:
npidata_pfile_*.csvpl_pfile_*.csvendpoint_pfile_*.csvothername_pfile_*.csvV1 was retired 2026-03-03; V2 is the only one published.
The walk
CourtListener's shape, because the problem is identical:
http.downloadwith Range resume (already in the repo),unziponce (already in the Dockerfile), then walk a batch at a time saving the line offset after each, across as many runs as it takes.Columns by index, checked on every run
The header is 330 columns of prose (
Provider Business Practice Location Address City Name) so matching on it is slow and fragile. All sixteen indices were verified against the real header before being written down.A government file of that width is exactly the sort that gains a column, so the header is re-checked every run and a shift throws — reading a first name out of a position that now holds a middle name would otherwise be silently wrong in every row.
Two things the real data taught
_fileheader.csvnext to the data with the same stem, holding only the header row. Picking it would look like an empty but successful walk.Both are tested.
The privacy decision, defaulting to cautious
Most records are organizations, but a large minority are sole practitioners who registered a home address as their practice location. It's public by law and CMS publishes it for reuse, so there's no legal question — but republishing several million people's home addresses in a searchable index is a different act from republishing a hospital's, and it should be a decision rather than a side effect.
The file carries ZIP+4, which narrows a home to roughly a building, hence the truncation.
includeIndividualStreetturns it off and defaults tono. A test asserts a built item never carries an individual's street.Also
Taxonomy codes resolve through NUCC's published code set, fetched per run rather than vendored — a stale copy would quietly degrade every row to a bare
207X00000X.Checks
🤖 Generated with Claude Code