# CMS Hospital Price Transparency Negotiated Payer Rates Scraper (`groundplanes/cms-hospital-price-transparency-mrf-parser`) Actor

Stream and parse CMS v2.0 and v3.0 Hospital Price Transparency Machine-Readable Files (JSON, JSON.gz, and CSV via cms-hpt.txt) to extract negotiated payer rates, cash prices, gross charges, and de-identified min/max rates by CPT, HCPCS, or DRG code.

- **URL**: https://apify.com/groundplanes/cms-hospital-price-transparency-mrf-parser.md
- **Developed by:** [Groundplane Labs](https://apify.com/groundplanes) (community)
- **Categories:** Business
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.10 / 1,000 rate records

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## CMS Hospital Price Transparency Negotiated Payer Rates Scraper

Pull payer-specific negotiated rates, cash prices, gross charges and de-identified min/max rates for the procedure codes you choose, straight from the price transparency files that U.S. hospitals must publish under 45 CFR Part 180.

### What it does

- Takes hospital root domains (for example `https://www.uhhospitals.org`), fetches each hospital's `/cms-hpt.txt` pointer file, and resolves it to the standard-charges Machine-Readable File (MRF). You can also pass direct MRF URLs.
- Streams each MRF without downloading it to disk. Files run from about 50 MB to 8 GB. Supported formats: CMS v2.0 and v3.0 JSON, gzip-compressed JSON (detected by magic bytes, not file extension), CSV in both the "tall" and "wide" CMS layouts, and single-file ZIP archives. CMS template v3.0 files (the current template, in effect since 2026-01-01) are supported and verified against live hospital files.
- Checks each file's header block (hospital name, last-updated date, version) before extracting anything. A missing or restructured header fails that hospital loudly as structure drift.
- Validates the fields the output depends on, not just the header. If a file renames the payer key inside `payers_information` (or the `payer_name` column in a tall CSV), renames or empties the item description, carries a price value that is present but cannot be parsed as a number, or uses a care setting outside inpatient / outpatient / both, the affected hospital fails loudly or the affected rows are counted and dropped, and the run reports `PARTIAL:` (or fails that hospital) with the count and the reason named. Drift never ships as a silent `Complete:`.
- Matches both the billing code and its code type. `27447` only matches records typed CPT or HCPCS, never a chargemaster (CDM), revenue code (RC) or DRG record that happens to share the same digits.
- Filters by payer name keywords and care setting (inpatient, outpatient or both).
- Reports scanned vs matched vs delivered rows and how much of each file was scanned in the run status, so caps and time budgets are always visible. A hospital that hits the scan budget or the row cap stops as a per-hospital `PARTIAL:` naming the cap; a hospital whose file structure has drifted fails that hospital loudly with the reason named.

### Who it's for

- **Self-funded employers, benefits consultants and TPAs** comparing what different insurers pay a hospital for the same procedure.
- **Health plans and provider network teams** benchmarking negotiated rates in a market.
- **Healthcare analysts and journalists** studying price variation between hospitals and payers.

### Input

Example input. The example run options use a default maximum charge of **$50** (`"maxTotalChargeUsd": 50`).

```json
{
  "hospitalMrfUrlsOrDomains": ["https://www.uhhospitals.org"],
  "billingCodesFilter": ["27447", "45378", "70553", "99285"],
  "payerNameKeywords": ["Aetna", "UnitedHealthcare", "Cigna", "Blue Cross"],
  "settingFilter": "BOTH",
  "maxRowsPerHospital": 5000,
  "maxMrfFilesPerDomain": 1,
  "maxScanSecondsPerFile": 1800,
  "maxItems": 1000
}
```

| Field | Description |
|---|---|
| `hospitalMrfUrlsOrDomains` | Hospital domains (resolved via `/cms-hpt.txt`) or direct MRF URLs. |
| `billingCodesFilter` | Plain 5-character codes match CPT/HCPCS; letter + 4 digits match HCPCS; 3 digits match MS-DRG. Use `TYPE:CODE` for anything else, e.g. `APR-DRG:540`. |
| `payerNameKeywords` | Case-insensitive; spaces and punctuation are ignored. Empty = all payers plus cash-only rows. |
| `settingFilter` | `INPATIENT`, `OUTPATIENT` or `BOTH`. Charges marked "both" are included in either setting. |
| `maxRowsPerHospital` | Row cap per MRF file, honored exactly. |
| `maxMrfFilesPerDomain` | Health systems often list one file per location. Only the first N unique files per domain are scanned. |
| `maxScanSecondsPerFile` | Streaming time budget per file, at most 1800 (the 30 minute per-hospital processing cap). Lower it for a quicker partial scan; a file that hits the budget is reported as a per-hospital PARTIAL naming the cap. |
| `maxItems` | Total row cap for the run. |

### Output

| Field | Type | Description |
|---|---|---|
| `hospitalName` | string | Hospital name from the MRF header |
| `cmsCertificationNumberCcn` | string|null | Facility identifier from the MRF header. MRFs have no CCN field, so this holds the reported state license (`LICENSE-OH:1497`) or, if missing, the Type 2 NPI (`NPI:1609189604`) |
| `lastUpdatedDate` | string | File's last-updated date (YYYY-MM-DD) |
| `billingCodeType` | string | CPT, HCPCS, MS-DRG, etc. |
| `billingCode` | string | The matched code |
| `procedureDescription` | string | Hospital's description of the item or service |
| `careSetting` | string | INPATIENT, OUTPATIENT or BOTH |
| `grossChargeUsd` | number|null | Chargemaster gross charge |
| `discountedCashPriceUsd` | number|null | Self-pay discounted cash price |
| `payerName` | string|null | Insurer name |
| `planName` | string|null | Plan or product name |
| `negotiatedDollarRateUsd` | number|null | Payer-specific negotiated dollar amount |
| `negotiatedMethodology` | string|null | fee schedule, case rate, per diem, percent of billed charges, etc. When no dollar amount exists, the hospital's algorithm text is appended |
| `deidentifiedMinRateUsd` | number|null | De-identified minimum negotiated charge |
| `deidentifiedMaxRateUsd` | number|null | De-identified maximum negotiated charge |
| `billingClass` | string|null | facility or professional |
| `modifiers` | string|null | Code modifiers on the charge (e.g. TC, 26) |
| `sourceMrfUrl` | string | The MRF actually streamed, after `cms-hpt.txt` resolution |

#### What is not emitted

- **Percentage-based negotiated charges carry no rate value.** When a hospital publishes a negotiated charge only as a percentage (a `standard_charge_percentage` / `negotiated_percentage` value with a methodology such as "percent of billed charges") and no dollar amount, the row is still emitted, with `negotiatedDollarRateUsd` set to null. The percentage value itself is not emitted in any output field. `negotiatedMethodology` still carries the methodology text the hospital published, so these rows are identifiable.
- **CMS v3.0 allowed amounts are not surfaced.** The CMS v3.0 template adds allowed-amount elements to negotiated charges (the median allowed amount, the 10th percentile and 90th percentile allowed amounts, and a count of allowed amounts). This Actor does not emit those elements; no output field carries them.

#### Sample row (synthetic)

```json
{
  "hospitalName": "Example Valley Medical Center",
  "cmsCertificationNumberCcn": "LICENSE-OH:0000",
  "lastUpdatedDate": "2026-07-01",
  "billingCodeType": "CPT",
  "billingCode": "27447",
  "procedureDescription": "TOTAL KNEE ARTHROPLASTY",
  "careSetting": "OUTPATIENT",
  "grossChargeUsd": 48250.0,
  "discountedCashPriceUsd": 36190.0,
  "payerName": "Example Health Plan",
  "planName": "Commercial PPO",
  "negotiatedDollarRateUsd": 21400.0,
  "negotiatedMethodology": "case rate",
  "deidentifiedMinRateUsd": 15800.0,
  "deidentifiedMaxRateUsd": 31250.0,
  "billingClass": "facility",
  "modifiers": null,
  "sourceMrfUrl": "https://www.example.com/price-transparency/000000000_example-valley-medical-center_standardcharges.json"
}
```

### How pricing works

Pay per result. You only pay for rows returned.

### Data source and terms

- **Source:** each hospital's own standard-charges file, published under the CMS Hospital Price Transparency rule ([45 CFR Part 180](https://www.ecfr.gov/current/title-45/subtitle-A/subchapter-E/part-180)) and located through its `/cms-hpt.txt` pointer file. See [CMS Hospital Price Transparency](https://www.cms.gov/priorities/key-initiatives/hospital-price-transparency).
- **Update frequency:** hospitals must update their files at least once a year; many update monthly or quarterly. Each row carries the file's own `lastUpdatedDate`.
- Data is returned as the hospital published it. This Actor does not correct or audit hospital pricing.
- This Actor is not affiliated with or endorsed by CMS, HHS or any hospital.

### Limits and FAQ

- **Large files take time.** A 3.5 GB CSV takes several minutes to stream end to end. The default budget is the full 30 minute per-hospital cap; the run status shows rows scanned, matched and delivered plus how many MB of each file were scanned. Lower `maxScanSecondsPerFile` for a quicker partial scan, which is reported as PARTIAL.
- **Multi-location systems.** University Hospitals lists 14 locations covering 13 unique files in its `cms-hpt.txt` (one file serves two locations; duplicates are de-duplicated). Raise `maxMrfFilesPerDomain` to scan more of them, or pass specific MRF URLs.
- **Blocked or missing files.** If a hospital's pointer file or MRF can't be reached, returns HTML, or uses an unsupported format, that hospital is reported by name in a `PARTIAL:` status while the other hospitals still deliver rows.
- **Multiple rates for one payer.** Hospitals often list several rates for the same payer and code (different modifiers, billing classes or settings). Check `modifiers`, `billingClass` and `careSetting`.
- **No personal data.** Contact names and emails inside `cms-hpt.txt` are never output.
- Found a problem or need a field? Open an issue on the Issues tab; we respond quickly.

### Changelog

- **v0.1.3**: Red Team final-GO fix round. The JSON payer sentinel now fires on every scan exit path, not only after a full scan: the per-hospital row cap, both scan-time-budget exits, an interrupted stream, and normal completion all evaluate it. A renamed `payer_name` key inside `payers_information` therefore fails that hospital as structure drift at any file size and on any exit path, with the Issues CTA, and rows built from `payers_information` entries are dropped instead of being delivered with a null payer name. Rows from charges with no `payers_information` at all (cash-only rows) still deliver normally, and a hospital whose payer key is intact is unaffected on every path (a row-cap stop on a healthy file still reports a cap-only PARTIAL with no CTA).
- **v0.1.2**: Red Team fix round. Nested and value-level drift no longer ships silently: a renamed `payer_name` key inside `payers_information` (JSON) or a renamed `payer_name` column (tall CSV) fails that hospital as structure drift at any file size; rows with an empty or missing procedure description are counted as invalid rows under the existing 20 percent machinery; non-empty price values that cannot be parsed as a number (dollar, gross, cash, min, max) are counted per hospital and reported as a source-caused PARTIAL with the count and first reason named (legitimately absent rates still ship as null, silently); care-setting values outside inpatient / outpatient / both are counted as drift instead of passing through or being silently filtered out. The Output section now discloses that percentage-based negotiated charges ship with `negotiatedDollarRateUsd` null and the percentage value is not emitted, and that the CMS v3.0 allowed-amount elements are not surfaced. CMS template v3.0 support is named explicitly (v3.0.0 files verified live). University Hospitals is described as 14 locations covering 13 unique files. When exact-duplicate rows are dropped by de-duplication, the per-source status line now states the duplicate count, so a matched count above the delivered count is explained.
- **v0.1.1**: Review fixes. Default hospital is University Hospitals only (the Mount Sinai default removed in spec cross-check is gone from the schema, code and examples). Scan budget default is now the 30 minute per-hospital cap, and hitting the scan budget or a row cap reports a per-hospital PARTIAL instead of Complete. Zero matches on a healthy scanned file is a successful Complete with 0 rows. Renamed code_information or standard_charges structures, empty charge arrays and header-only CSVs now fail that hospital loudly as structure drift. Rows scanned are reported per hospital. Fetches retry 6 times with jittered backoff and Retry-After honored. Default run options set: 1024 MB memory, $50 max total charge, 7200 s timeout.
- **v0.1**: Initial release. cms-hpt.txt resolution, streaming JSON / JSON.gz / tall and wide CSV / ZIP parsing, code-type-aware matching, per-hospital header validation and status reporting.

# Actor input Schema

## `billingCodesFilter` (type: `array`):

Billing codes to extract. Plain 5-character codes (27447) match CPT or HCPCS records only; letter+4 digits (C1713) match HCPCS; 3 digits match MS-DRG. Use TYPE:CODE (e.g. APR-DRG:540, RC:0450) for anything else. Both the code and its type must match, so DRG, revenue-code and internal chargemaster codes never collide.

## `hospitalMrfUrlsOrDomains` (type: `array`):

Hospital root domains (e.g. https://www.uhhospitals.org) whose /cms-hpt.txt pointer file is resolved to standard-charges MRF URLs, or direct URLs to CMS v2.0 and v3.0 .json, .json.gz, .csv (tall or wide) or single-file .zip MRFs.

## `maxItems` (type: `integer`):

Maximum total rows to return across all hospitals in the run. Rows are sorted newest file first (lastUpdatedDate).

## `maxMrfFilesPerDomain` (type: `integer`):

Health systems often list one MRF per hospital location in cms-hpt.txt (University Hospitals lists 14 locations covering 13 unique files of about 3.5 GB each). Only the first N unique files per domain are scanned. Direct MRF URLs are always scanned.

## `maxRowsPerHospital` (type: `integer`):

Maximum matching rows to keep from each hospital MRF file. Honored exactly.

## `maxScanSecondsPerFile` (type: `integer`):

Streaming time budget per MRF file, capped at 1800 seconds (the 30 minute per-hospital processing cap). Files run from 50 MB to 8 GB; a 3.5 GB CSV takes several minutes to stream end to end. A file that hits the budget stops as a per-hospital PARTIAL naming the cap, and the run status reports rows scanned, matched and delivered plus how many MB of each file were scanned.

## `payerNameKeywords` (type: `array`):

Case-insensitive payer name keywords (spaces and punctuation ignored, so 'Blue Cross' also matches 'BlueCross'). Leave empty to include all payers plus cash-only rows.

## `settingFilter` (type: `string`):

INPATIENT returns inpatient and 'both' charges, OUTPATIENT returns outpatient and 'both' charges, BOTH returns everything.

## Actor input object example

```json
{
  "billingCodesFilter": [
    "27447",
    "45378",
    "70553",
    "99285"
  ],
  "hospitalMrfUrlsOrDomains": [
    "https://www.uhhospitals.org"
  ],
  "maxItems": 5000,
  "maxMrfFilesPerDomain": 1,
  "maxRowsPerHospital": 5000,
  "maxScanSecondsPerFile": 1800,
  "payerNameKeywords": [
    "Aetna",
    "UnitedHealthcare",
    "Cigna",
    "Blue Cross"
  ],
  "settingFilter": "BOTH"
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "billingCodesFilter": [
        "27447",
        "45378",
        "70553",
        "99285"
    ],
    "hospitalMrfUrlsOrDomains": [
        "https://www.uhhospitals.org"
    ],
    "maxItems": 5000,
    "maxMrfFilesPerDomain": 1,
    "maxRowsPerHospital": 5000,
    "maxScanSecondsPerFile": 1800,
    "payerNameKeywords": [
        "Aetna",
        "UnitedHealthcare",
        "Cigna",
        "Blue Cross"
    ],
    "settingFilter": "BOTH"
};

// Run the Actor and wait for it to finish
const run = await client.actor("groundplanes/cms-hospital-price-transparency-mrf-parser").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "billingCodesFilter": [
        "27447",
        "45378",
        "70553",
        "99285",
    ],
    "hospitalMrfUrlsOrDomains": ["https://www.uhhospitals.org"],
    "maxItems": 5000,
    "maxMrfFilesPerDomain": 1,
    "maxRowsPerHospital": 5000,
    "maxScanSecondsPerFile": 1800,
    "payerNameKeywords": [
        "Aetna",
        "UnitedHealthcare",
        "Cigna",
        "Blue Cross",
    ],
    "settingFilter": "BOTH",
}

# Run the Actor and wait for it to finish
run = client.actor("groundplanes/cms-hospital-price-transparency-mrf-parser").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "billingCodesFilter": [
    "27447",
    "45378",
    "70553",
    "99285"
  ],
  "hospitalMrfUrlsOrDomains": [
    "https://www.uhhospitals.org"
  ],
  "maxItems": 5000,
  "maxMrfFilesPerDomain": 1,
  "maxRowsPerHospital": 5000,
  "maxScanSecondsPerFile": 1800,
  "payerNameKeywords": [
    "Aetna",
    "UnitedHealthcare",
    "Cigna",
    "Blue Cross"
  ],
  "settingFilter": "BOTH"
}' |
apify call groundplanes/cms-hospital-price-transparency-mrf-parser --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,groundplanes/cms-hospital-price-transparency-mrf-parser"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/gb28qUbM2v7f3WElB/builds/qdbviaRFeLbqP57IH/openapi.json
