HCRIS Hospital Financials
Description
Full financial and operational detail from the Healthcare Cost Report Information System (HCRIS) — the annual cost report every Medicare-certified hospital in the US is required to file. One row per (CCN × fiscal year); a hospital with 20 years of filing history has 20 rows. This is the underlying cost-report line-item table (charges, costs, revenues, bed counts, workforce, balance sheet), not a search-index summary — it's the full financial history CMS/NBER-cleaned files provide, preferred here over a "latest per CCN" view because bulk-license buyers generally want the time series, not just the current snapshot.
- Source: CMS Healthcare Cost Report Information System, via CMS/NBER cleaned cost-report files.
- Refresh cadence: rolling, as CMS posts new/amended cost reports; ingestion cadence is not fixed-calendar.
- Row count: 173,959
- Vintage: 2026-05-26 (max
ingested_atobserved at export time) - Coverage: fiscal years 1996–2025, 10,138 distinct CCNs; 846 of them map to a publicly traded operator across 14 tickers (2026-08-29 crosswalk).
Table: hcris_hospital_financials
Snowflake: HEALTHPARSE_DATA.HCRIS_HOSPITAL_FINANCIALS.HCRIS_HOSPITAL_FINANCIALS
| Column | Type | Description | Notes |
|---|---|---|---|
ccn |
VARCHAR | CMS Certification Number — the 6-character federal ID for the hospital (facility-level primary identifier across virtually every CMS dataset). | Not unique alone; unique with fy_end. |
npi |
VARCHAR | Linked organizational National Provider Identifier, when a match to the hospital's NPI record was made. | Populated on 49,696 of 173,959 rows (~29%). Absent doesn't mean the hospital lacks an NPI — it means the ingestion-time link wasn't made. |
provider_name |
VARCHAR | Hospital name as filed on the cost report. | 7,244 rows (~4%) hold the literal placeholder Unknown where the source file didn't carry a resolvable name. |
street / city / state |
VARCHAR | Mailing address on file with the cost report, as filed. | city/state are null wherever provider_name is Unknown. |
zip |
VARCHAR | Verified 5-digit US ZIP, or NULL. Never a ZIP+4, never a trailing hyphen: the source ZIP+4 box is reduced to its 5-digit prefix and the +4 is dropped. The value is then checked against the Census 2020 ZCTA universe and against this row's own state; anything that fails either check is NULL. |
161,378 of 173,959 rows populated (92.8%); 100% of those are a real ZIP consistent with state. 12,581 NULL = 7,281 blank at source + 4,584 CCN-in-ZIP + 359 ZIP/state contradiction + 351 not a real ZIP for that state + 6 unrecoverable. NULL means "not verified", not "no address" — city and state are still populated on most of those rows. Rule and reference file: see the zip section of the dictionaries README. |
fy_begin / fy_end |
DATE | Cost-report fiscal year start/end dates (hospital fiscal years are not calendar-aligned). | fy_end is never null; fy_begin can be. |
fy_end_year / fy_end_month |
SMALLINT | Denormalized year/month of fy_end, for filtering without a date function. |
— |
control_type |
VARCHAR | Ownership/control category from the cost report (e.g. "Voluntary non-profit - Church", "Proprietary", "Government - State"). | Mixed encoding: ~96% of rows (166,185) store the raw CMS numeric control-type code (1–13) rather than the text label — no crosswalk table ships with this export. ~1.4% (2,496) are null. Only ~3% carry the human-readable text shown in the description above. |
hospital_type |
VARCHAR | Facility type (e.g. "Acute Care Hospitals", "Critical Access Hospitals", "Psychiatric", "Long-term", "Childrens", "Rural Emergency Hospital"). | Mostly missing/coded: ~55% of rows (95,044) are null, ~27% (46,206) hold a raw numeric code instead of text, leaving only ~19% with a resolved text label. |
total_beds / available_beds |
INTEGER | Licensed / staffed-and-available bed counts as filed. | — |
total_inpatient_days / medicare_inpatient_days / medicaid_inpatient_days |
BIGINT | Total patient-days for the fiscal year, and the Medicare/Medicaid-attributed subsets. | — |
total_outpatient_visits / total_discharges |
BIGINT | Outpatient visit count and total inpatient discharges for the fiscal year. | — |
total_charges / total_costs / total_revenues |
DOUBLE | Hospital-wide gross charges, total costs, and total revenues for the fiscal year, in dollars. | Charges ≠ what payers actually pay — see HPT Negotiated Rates for actual negotiated/cash prices. |
medicare_charges / medicare_costs / medicaid_charges |
DOUBLE | Charges and costs attributable specifically to Medicare and Medicaid patients. | Thinly filed: medicare_costs on 17,430 and medicaid_charges on 3,163 of 173,959 rows. |
medicaid_costs |
DOUBLE | Costs attributable specifically to Medicaid patients. | Present in the schema, not populated in this release. Null on all 173,959 rows. No cost report in the loaded history carries a value on the worksheet line it maps to. Use medicaid_charges (3,163 rows) if a Medicaid figure is essential, and note it is charges, not costs. |
bad_debt |
DOUBLE | Bad debt expense recognized for the fiscal year. | — |
charity_care_charges / uncompensated_care |
DOUBLE | Charges written off as charity care, and total uncompensated care (charity care + bad debt, per CMS worksheet S-10 methodology). | — |
net_income |
DOUBLE | Total revenues minus total costs for the fiscal year. | Used to compute operating margin (net_income / total_revenues) in the source app; not pre-computed in this export. |
total_assets / total_liabilities / fund_balance |
DOUBLE | Fiscal-year-end balance sheet: total assets, total liabilities, and fund balance (assets − liabilities). | — |
total_salaries / total_fte |
DOUBLE | Total salary expense and total full-time-equivalent staff count for the fiscal year. | — |
resident_count_inpatient |
DOUBLE | Medical resident FTE count on inpatient rotation, for teaching hospitals. | Populated on 4,621 of 173,959 rows; null for the large majority of non-teaching hospitals. |
resident_count_outpatient |
DOUBLE | Medical resident FTE count on outpatient rotation. | Present in the schema, not populated in this release. Null on all 173,959 rows. No cost report in the loaded history carries a value on the outpatient rotation line; only the inpatient count above is filled. |
ownership_change_flag |
BOOLEAN | Whether CMS recorded an ownership/control change (CHOW) affecting this cost-report period. | Defaults false; only a small minority are true. |
ingested_at |
TIMESTAMP WITH TIME ZONE | When this row was last (re-)ingested into the source database. | Distinct from _vintage (a fixed constant) and _exported_at (this Parquet snapshot's export time) — see the README. |
Known limits
Read these before you build on the numeric columns.
total_assets carries implausible outliers. 44 rows across 12 distinct
CCNs report total assets above $20bn — impossible for a single hospital and
inconsistent with the same row's revenue. The worst case is CCN 420004
(Medical University of South Carolina) FY2025: $85.69bn of total assets on
$2.51bn of total revenues, a 34× asset-to-revenue ratio against a sector norm
of roughly 1–2×. Others in the same shape include CCN 370093 (OU Medical
Center) FY2024 at $85.31bn on $1.97bn, and CCN 100012 (Lee Memorial Hospital)
FY2022 at $54.66bn on $1.28bn. These are as-filed values — CMS does not
validate the balance-sheet worksheet, and a misplaced decimal or a
whole-system balance sheet filed against one CCN survives ingestion. We ship
what was filed rather than silently correcting it.
What to do: screen before aggregating. Any sum, mean or percentile of
total_assets (or a ratio built on it, such as return on assets) is dominated
by these rows. A practical guard is to drop rows where
total_assets > 50 * total_revenues, or simply cap at $20bn; a national
sum(total_assets) without a guard is wrong by tens of billions.
zip was not trustworthy before the 2026-09-11 export, and is now
verified or NULL. Three separate defects were found and fixed in that export:
- CCN in the ZIP field — 4,584 rows. A 6-character CCN passes a 5-digit
ZIP check once the leading zero is dropped, so the value geocoded to a real
but wrong location (CCN 381310, Pioneer Memorial Hospital, Heppner OR
shipped
zip = '381310'; the true ZIP is 97836). The cause was a transposed line/column key in the loader that backfilled FY2022–FY2025 filings from the HCRIS ALPHA worksheet. - Format — 49,434 rows. The cost-report header field is a ZIP+4 box, so
30,356 rows shipped as
36302-(a valid ZIP with an empty +4 and a dangling hyphen), 18,680 as36302-1234, and 398 in other shapes (191044303,17055-20). All now ship as the bare 5-digit36302; the +4 is deliberately dropped so the column is one clean join key. - ZIP contradicting
state— 710 rows. 359 carried a ZIP the Census ZCTA reference assigns to a different state (e.g.state = 'LA'with76901, San Angelo TX), and 351 carried a value that is not a real ZIP in that state at all (including00000). Where the two disagree the ZIP is NULLed and the state kept — the state is corroborated by the CCN, NPI and city.
What you get now: 161,378 non-null ZIPs, of which 100% are a real
5-digit US ZIP whose state matches the state column; 12,581 NULL, of which
7,281 were blank at source. A NULL zip means we could not verify it, not
the hospital has no address. The full rule and the reference file
(data/reference/zcta-state-2020.csv, Census 2020 ZCTA-to-county, retrieved
2026-09-11) are documented in the dictionaries README.
If you hold an export dated before 2026-09-11: discard zip wherever it
equals ccn, strip anything after the first five digits, and do not trust the
remainder against state.
The crosswalk to operators is neither complete nor unique. About 30% of FY2019+ rows resolve to a parent operator at all, and the crosswalk itself holds duplicate CCNs — see the Known limits section of the Operator Crosswalk dictionary before joining.
Example queries (Snowflake)
-- 1. Financial-distress screen: hospitals with negative net income and
-- high uncompensated care, most recent fiscal year on file per CCN.
SELECT "ccn", "provider_name", "state", "fy_end_year", "total_revenues", "net_income",
"uncompensated_care"
FROM HEALTHPARSE_DATA.HCRIS_HOSPITAL_FINANCIALS.HCRIS_HOSPITAL_FINANCIALS
WHERE "net_income" < 0
QUALIFY ROW_NUMBER() OVER (PARTITION BY "ccn" ORDER BY "fy_end" DESC) = 1
ORDER BY "uncompensated_care" DESC
LIMIT 100;
-- 2. Operating margin trend for a single hospital across all filed years.
SELECT "ccn", "provider_name", "fy_end_year",
"total_revenues", "net_income",
CASE WHEN "total_revenues" > 0 THEN "net_income" / "total_revenues" END AS operating_margin
FROM HEALTHPARSE_DATA.HCRIS_HOSPITAL_FINANCIALS.HCRIS_HOSPITAL_FINANCIALS
WHERE "ccn" = '340001'
ORDER BY "fy_end_year";
-- 3. State-level rollup: total beds and aggregate net income by state,
-- latest fiscal year available per CCN.
SELECT "state", count(DISTINCT "ccn") AS hospitals, sum("total_beds") AS total_beds,
sum("net_income") AS aggregate_net_income
FROM (
SELECT *
FROM HEALTHPARSE_DATA.HCRIS_HOSPITAL_FINANCIALS.HCRIS_HOSPITAL_FINANCIALS
QUALIFY ROW_NUMBER() OVER (PARTITION BY "ccn" ORDER BY "fy_end" DESC) = 1
)
GROUP BY "state"
ORDER BY hospitals DESC;