Skip to main content

mrfkit

mrfkit reads US hospital and insurer price transparency files and turns them into clean, normalized tables.

Every US hospital must publish its prices in a machine-readable file (MRF). The files are huge, and no two look alike. Columns get renamed, payers sit in rows or in columns, codes come glued to modifiers, and plenty of JSON files are not valid JSON. mrfkit handles that mess, so you get the same tables from any file.

It was extracted from a production system that parsed files from about 3,000 hospitals. Its parsing tests, and the fixes behind them, came along.

Install

pip install mrfkit

For Parquet output:

pip install "mrfkit[parquet]"

Python 3.11 or newer.

Command line

mrfkit hospital_standardcharges.csv -o out/

This writes one CSV per table into out/. Use -f parquet for Parquet. Give several files and each one gets its own subdirectory. Plain, gzip and zip files all work, and the format is detected from the content.

Python

import mrfkit

stats = mrfkit.ParseStats()
for record in mrfkit.iter_records("hospital_standardcharges.json", stats=stats):
    if isinstance(record, mrfkit.PayerRate):
        print(record.code, record.payer_name, record.plan_name, record.negotiated_rate)

print(stats.rows_read, stats.rejected_codes, stats.warnings)

Records stream one at a time, so memory stays flat for multi-gigabyte files. To write them to disk:

with mrfkit.open_sink("out/", "parquet") as sink:
    for record in mrfkit.iter_records("hospital_standardcharges.csv"):
        sink.write(record)

Output

Table One row per Main columns
charge_items billable item (code, type, billing class, setting, modifiers) code, code_type, description, billing_class, setting, modifiers, drug units
standard_charges item price row gross_charge, discounted_cash_price, min_negotiated_rate, max_negotiated_rate
payer_rates negotiated rate for one payer and plan payer_name, raw_payer_name, plan_name, plan_category, negotiated_rate, negotiated_percentage, methodology_type, estimated_amount, median_amount
unmapped_cells value in a column mrfkit could not map source_column, value
file_metadata file hospital name, locations, license, NPI, last updated, version, attestation
header_mappings source column what the column was read as
modifiers modifier definition (CMS 3.0 JSON) code, description, payer-specific notes

Every data row carries its item key (code, code_type, billing_class, setting, modifiers), so the tables join without a database.

Insurer files (Transparency in Coverage)

Insurers publish their negotiated rates as Transparency in Coverage (TiC) JSON files. mrfkit and iter_records recognize them by their root keys and read two kinds:

  • In-network rates files (provider_references and in_network), into two main tables. tic_provider_groups has one row per provider group (a TIN and its NPIs) behind each of the file's provider references. tic_rates has one row per distinct price for a billing code: identical prices are merged, and provider_group_ids lists every group that has the price, so a code priced the same for 5,000 providers is one row, not 5,000. tic_file_metadata holds the reporting entity, last-updated date, version and network name.
  • Tables of contents (reporting_structure), into tic_index: one row per plan and in-network file, with the file's URL. mrfkit.group_tic_networks groups those rows into networks, one per in-network file name.

Both stream: memory holds one item or plan structure at a time, plus the provider groups' ids, never the whole file. Item names and descriptions are never read into a row, and a key mrfkit does not know is counted by its JSON path only (ParseStats.unmapped_paths, or mrfkit -v), never with its value.

for record in mrfkit.iter_records("2026-09-01_network_in-network-rates.json.gz"):
    if isinstance(record, mrfkit.TicRate):
        print(record.billing_code, record.negotiated_rate, record.provider_group_ids)

Allowed-amount and prescription-drug files are not supported, and provider groups kept in a separate file behind a URL are reported but not fetched.

What it handles

  • CMS templates, versions 2 and 3, as CSV (tall and wide) and JSON
  • Older and vendor layouts: one column per payer, payer and setting packed into column names, separate inpatient and outpatient price columns
  • More than 130 header spellings, plus your own (extra_synonyms)
  • Metadata rows with the data header glued on, Key: Value preambles, several tables stacked in one CSV
  • Pipe- and tab-delimited files, stray quotes that would fuse rows
  • Deflate64 zips, gzip, UTF-16, Windows-1252 bytes, byte-order marks
  • Broken JSON: double and trailing commas, control characters
  • Codes: CPT, HCPCS, NDC, revenue codes, MS-DRG, APR-DRG; modifiers baked into codes (73721TC); corrupt codes are dropped and counted
  • Payer and plan names: about 1,200 known payer spellings, normalized to a canonical payer, a plan category and a network
  • Self-pay rows: read as the cash price, not as a payer

What it does not do

  • Download files. Point it at files you already have.
  • Fix hospital errors. mrfkit reports what the file says.
  • Insurer allowed-amount and prescription-drug files.

Disclaimer

The output is for information only. It is not medical, legal or financial advice. Hospitals publish mistakes, and a price in a file is not a quote.

CPT codes are numbers published by hospitals under the federal price transparency rule. mrfkit ships no CPT descriptions.

License

Apache License 2.0. See LICENSE and NOTICE.

Metadata

Release files for mrfkit 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mrfkit 0.1.0
File Size Uploaded
mrfkit-0.1.0.tar.gz 321.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for mrfkit 0.1.0
File Interpreter ABI Platform
mrfkit-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 441.9 kB

Release files / mrfkit-0.1.0.tar.gz

Download URL mrfkit-0.1.0.tar.gz
Size 321.6 kB
Tags Source
SHA-256 checksum
How to use checksums
32904602bbf4c60bc90d298284b32a2e51447eda56d353a4c58e867e3df65104
BLAKE2b-256 checksum
How to use checksums
2ad4e80107755ce8910412ae23849f78b50b4ee18fe99edaeecaeaf1ff21ce05
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 9, 2026.

Transparency log

Release files / mrfkit-0.1.0-py3-none-any.whl

Download URL mrfkit-0.1.0-py3-none-any.whl
Size 120.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
352ddcb01fb7b124be4003abbb9106ba32e0d7038648c9999119171325d36567
BLAKE2b-256 checksum
How to use checksums
54a7113bd6b14fbae3177acb212acb60a9ab0f101358aa5fa3f83fda2a149a7a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 9, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page