PriceTransparency Rate Changes
Status: work in progress. This table is not included in evaluation packages.
Description
The same hospital, code and payer, seen in two monthly snapshots, and what the price did. Every US hospital publishes a machine-readable file (MRF) of its standard charges; each of those files is a level — a price as of the day it was posted. Nobody publishes the difference. This product is that difference: one row per negotiated-rate fact that our crawl has observed in two or more snapshots of the hospital's MRF, carrying the first and last rate, the change in dollars and percent, the second-to-last observation (so the most recent move is separable from the cumulative one), and which way it went.
It is derived entirely from PriceTransparency Negotiated Rates — no new source. A buyer who already ingests the raw MRFs has every input to this table and still has to build it, because building it requires a retained history of prior snapshots; the hospital's own file only ever shows today.
A second file rolls the diff up to the listed operator, so the series can be read as a repricing signal per ticker rather than per CCN.
- Source: derived from
hpt_rates(hospital-published MRFs, crawled from each hospital's own domain). No central government feed exists. - Refresh cadence: recomputed on every marketplace export, from whatever snapshots the rolling re-crawl has accumulated. The panel deepens with each refresh — a fact only enters this table on its second observation.
- Row count (measured, 2026-08-29 export): 696 facts in
hpt_rate_changes, across 3 CCNs, 145 billing codes and 25 payers; 402 rows inhpt_rate_changes_by_operator(1 ticker). This is small because the crawl has not yet re-visited most hospitals, not because the query is narrow — see Known limits. - Vintage: 2026-08-29 (max
snapshot_datein the underlying fact table). - Observed range:
first_snapshot2026-05-22 →last_snapshot2026-08-02. Every fact currently hasn_snapshots= 2.
Grain and the payer key
The fact grain is (ccn, billing_code, billing_code_type, payer,
plan_name, setting, rate_type) — the full identity of a price line in
the source table.
payer is coalesce(payer_name_canonical, payer_name_raw).
payer_name_canonical is null on 17.1% of source rows (1,843,851 of
10,752,221) — either an alias not yet in the mapping table, or a gross /
cash / de-identified min-max row where no payer applies.
payer_name_raw is null on 6.8% (728,021), and those raw nulls are a strict
subset of the canonical nulls. Coalescing canonical → raw therefore keys
93.2% of rows on a name and leaves 6.8% keyed on a NULL payer, which is the
correct value for a chargemaster or cash-price row. The canonical name is
preferred first so a payer that is spelled two ways in two snapshots of the
same file does not read as two different facts.
Rows with a null rate_amount are excluded before the diff (the source
carries none today, but the rule is explicit). Where the same key appears
more than once inside a single snapshot — 14.3% of keys, from differing
modifiers / rate_methodology / description off the same file — those
rows are collapsed to avg(rate_amount) for that snapshot, so a fact has
exactly one rate per snapshot before any comparison.
Table: hpt_rate_changes
One row per fact observed in two or more snapshots. 696 rows.
| Column | Type | Description | Notes |
|---|---|---|---|
ccn |
VARCHAR | CMS Certification Number of the hospital publishing the rate. | Joins to HCRIS Hospital Financials, CareLens Facility Quality and the Operator Crosswalk on ccn. |
billing_code |
VARCHAR | The procedure/service code the rate applies to. | Only the curated ~708-code target list is extracted upstream. |
billing_code_type |
VARCHAR | Code system: CPT, MS-DRG, CMG, RC. |
— |
payer |
VARCHAR | Payer name, canonical where mapped, else the hospital's raw string. | Null on gross / cash rows where no payer applies. Part of the key. |
plan_name |
VARCHAR | Plan within the payer, when the hospital publishes that granularity. | Frequently null. Part of the key — see Known limits, a snapshot that stops publishing plan detail breaks the match. |
setting |
VARCHAR | Care setting (inpatient, outpatient, both, clinic, hospital, specialty). |
Same caveat as plan_name. |
rate_type |
VARCHAR | gross, negotiated, cash, min, max. |
Only compare like with like; a gross change and a negotiated change mean different things. |
first_snapshot |
DATE | Crawl date of the earliest observation of this fact. | — |
last_snapshot |
DATE | Crawl date of the most recent observation. | — |
n_snapshots |
BIGINT | Number of distinct snapshots this fact was observed in. | Always ≥ 2 by construction. All rows are 2 today. |
first_rate |
DOUBLE | Rate at first_snapshot, in dollars. |
Mean of the snapshot's rows for this key where the file carried more than one. |
last_rate |
DOUBLE | Rate at last_snapshot. |
Same. |
abs_change |
DOUBLE | last_rate - first_rate, in dollars. |
— |
pct_change |
DOUBLE | (last_rate - first_rate) / first_rate * 100. |
Null when first_rate = 0 (no meaningful base). Never null for any other reason. |
prior_rate |
DOUBLE | Rate at the second-to-last observation. | Equals first_rate when n_snapshots = 2, which is every row today. |
prior_snapshot |
DATE | Crawl date of the second-to-last observation. | Equals first_snapshot when n_snapshots = 2. |
latest_change_pct |
DOUBLE | (last_rate - prior_rate) / prior_rate * 100 — the most recent move only. |
Null when prior_rate = 0. Equals pct_change while n_snapshots = 2. |
changed |
BOOLEAN | last_rate <> first_rate. |
— |
direction |
VARCHAR | up, down or flat. |
flat is last_rate = first_rate exactly. |
source_mrf_changed |
BOOLEAN | True when the MRF URL the fact was read from differs between first_snapshot and last_snapshot. |
Data-quality flag, not a signal. A hospital reposting at a new quarterly URL trips it harmlessly, but so does a CCN whose crawl resolved to a different facility's file. Filter it out before reading the table as a repricing series — see Known limits. |
Table: hpt_rate_changes_by_operator
The same diff rolled up to (ticker, payer, billing_code,
billing_code_type). 402 rows.
Each CCN contributes through exactly one crosswalk row: the same rule the
A-7 by_operator panel applies (ticker-bearing row first, then largest
ownership_pct, then operator_key), restricted to rows with a non-null
ticker. CCNs with no listed operator are absent from this file entirely.
| Column | Type | Description | Notes |
|---|---|---|---|
ticker |
VARCHAR | Exchange ticker of the operator that owns the hospital. | From operator_crosswalk; the CCN→owner link is CMS's, the owner→ticker link is ours. |
payer |
VARCHAR | Payer name, as in the fact table. | Null for gross / cash / de-identified rows, which group together. |
billing_code |
VARCHAR | The procedure/service code. | — |
billing_code_type |
VARCHAR | Code system. | — |
hospitals |
BIGINT | Distinct CCNs behind this group. | 1 for every row today. |
facts |
BIGINT | Number of fact rows aggregated. | The denominator for the three share columns. |
median_first_rate |
DOUBLE | Median first_rate across the group's facts. |
— |
median_last_rate |
DOUBLE | Median last_rate. |
— |
median_pct_change |
DOUBLE | Median pct_change. |
Facts whose pct_change is null (zero base) are excluded from the median but counted in facts. |
share_up |
DOUBLE | Share of the group's facts with direction = 'up'. |
0–1. |
share_down |
DOUBLE | Share with direction = 'down'. |
0–1. |
share_flat |
DOUBLE | Share with direction = 'flat'. |
0–1. The three shares sum to 1. |
first_snapshot |
DATE | Earliest first_snapshot in the group. |
— |
last_snapshot |
DATE | Latest last_snapshot in the group. |
— |
Known limits
- The panel starts 2026-05-22, so only a few snapshots exist today. The
source has seven distinct
snapshot_datevalues (2026-05-22, -05-23, -05-24, -07-29, -07-30, -08-02, -08-29), and they are two crawl waves, not seven monthly reporting periods. Of 2,421 CCNs in the source, 21 have been crawled on more than one date and only 8 across the May → July/August boundary. No fact yet has more than two observations, solatest_change_pctequalspct_changeon every row andprior_*equalsfirst_*. The columns are there because the table deepens with each refresh; the depth is not there yet. Anything read off this table today is a shape demonstration, not a market measurement. snapshot_dateis a crawl date, not a reporting period. Some pairs are two days apart within one wave (2026-05-22 → -05-23) rather than month-over-month. Filter onfirst_snapshot/last_snapshotif a minimum elapsed interval matters; 540 of the 696 facts span two different calendar months.- A rate present in one snapshot and absent in the next is not a change.
Hospitals repost their files with different code coverage from quarter to
quarter, and our extractor's own coverage of
settingandplan_namehas changed between crawls. Disappearance is therefore silent by design: a fact that stops appearing simply never enters this table, and nothing here should be read as a rate being withdrawn. The cost is real — at the (ccn,billing_code,billing_code_type,rate_type,payer) grain 7,714 keys recur across snapshots, but requiringsettingandplan_nameto match as well drops that to 696. CCN 230141 is the clearest case: its 2026-05-22 rows carry a nullsettingfor all 8,860 rows, its 2026-05-24 rows carryinpatient/outpatient/both, and not one fact survives the join. source_mrf_changedmarks facts that may not be the same hospital. 536 of the 696 facts (all of CCN 444030) have it set: the May crawl readstrhs-winchester_standardcharges.csvand the July crawl readriverview-regional-medical-center_standardcharges.csv.zipunder the same CCN. Those 536 rows are a file-attribution change, not a repricing. The remaining 160 facts (CCNs 050222 and 141327, allrate_type = 'negotiated') are same-URL comparisons. Always filterWHERE NOT source_mrf_changedbefore drawing a conclusion about prices.- Nulls.
pct_changeandlatest_change_pctare null only when their base rate is exactly 0.payer,plan_nameandsettingare nullable and are part of the key, so NULL is a distinct group value, not a wildcard — a NULLplan_namefact and a named-plan fact for the same code are two rows. Rows with a nullrate_amountnever enter the table. Inhpt_rate_changes_by_operator,median_pct_changecan be null if every fact in the group had a zero base. direction = 'flat'is currently 0 rows. Given the two points above, that is a statement about how little comparable data exists, not evidence that hospital prices always move. Do not quote it.
Example queries (Snowflake)
-- 1. Negotiated rates that moved, excluding file-attribution changes.
SELECT "ccn", "billing_code", "payer", "first_rate", "last_rate", "pct_change"
FROM HEALTHPARSE_DATA.HPT_RATE_CHANGES.HPT_RATE_CHANGES
WHERE "rate_type" = 'negotiated'
AND NOT "source_mrf_changed"
AND "changed"
ORDER BY abs("pct_change") DESC;
-- 2. Where a payer repriced hardest, by operator.
SELECT "ticker", "payer", sum("facts") AS facts,
APPROX_PERCENTILE("median_pct_change", 0.5) AS median_pct_change
FROM HEALTHPARSE_DATA.HPT_RATE_CHANGES.HPT_RATE_CHANGES_BY_OPERATOR
WHERE "payer" IS NOT NULL
GROUP BY "ticker", "payer"
ORDER BY abs(median_pct_change) DESC;
-- 3. Only the most recent move, once the panel is more than two deep.
SELECT "ccn", "billing_code", "payer", "prior_snapshot", "last_snapshot",
"prior_rate", "last_rate", "latest_change_pct"
FROM HEALTHPARSE_DATA.HPT_RATE_CHANGES.HPT_RATE_CHANGES
WHERE "n_snapshots" >= 3
ORDER BY abs("latest_change_pct") DESC;