# Medicare Tools: Fee Schedule & LCD Lookup (`iceni-data/medicare-fee-coverage-lookup`) Actor

Medicare physician fee schedule payment, RVUs, GPCIs, and MUE limits for any CPT/HCPCS by locality, plus LCD coverage matches. Verified to the cent against CMS; 2026 files.

- **URL**: https://apify.com/iceni-data/medicare-fee-coverage-lookup.md
- **Developed by:** [Martyn Gross](https://apify.com/iceni-data) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $20.00 / 1,000 fee schedule rows

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

*Part of Iceni Data · Medicare Tools*

## Medicare Tools: Fee Schedule & LCD Lookup

**Prices HCPCS/CPT codes against the current CMS Medicare Physician Fee Schedule, by locality, and attaches any matching NCD/LCD coverage determinations.** Built on the [CMS PFS Relative Value Files](https://www.cms.gov/medicare/payment/fee-schedules/physician/pfs-relative-value-files), [NCCI MUE tables](https://www.cms.gov/medicare/coding-billing/national-correct-coding-initiative-ncci-edits/medicare-ncci-medically-unlikely-edits-mues), and the [Medicare Coverage Database](https://www.cms.gov/medicare-coverage-database/). Runs on the [Apify platform](https://apify.com), so you get scheduling, an API, webhooks, and monitoring for free.

### What does Medicare Fee Coverage Lookup do?

For every (HCPCS/CPT code, locality) pair you give it, this computes the base Medicare Physician Fee Schedule payment — `(work RVU × work GPCI + PE RVU × PE GPCI + MP RVU × MP GPCI) × conversion factor` — using the non-facility or facility practice-expense RVU depending on where the service is furnished, and the standard or QP-differential conversion factor depending on `apm_qualifying`. It also attaches the code's NCCI Medically Unlikely Edit limits, any NCD/LCD coverage determination whose own HCPCS code list includes the code, and any Billing & Coding Article that references it (informational only — see [What this Actor does *not* do](#what-this-actor-does-not-do)). The first run downloads and normalizes the source CMS files into a cached lookup table (see [Refresh cadence](#refresh-cadence--how-caching-works) below); every run after that reads from the cache, so most runs finish in seconds.

### Why use Medicare Fee Coverage Lookup?

- **Billing/RCM teams** can batch-price a code list across every locality a multi-site practice operates in, instead of clicking through CMS's own locality-by-locality fee schedule lookup tool one code at a time.
- **Health-tech and RCM software builders** get a normalized JSON feed of RVUs, GPCIs, MUEs, and coverage matches to embed in their own product, instead of maintaining their own CMS file-parsing pipeline.
- **Compliance/coding teams** can spot-check whether a code has an NCD, LCD, or Billing & Coding Article they should be aware of before billing it, without manually searching the Medicare Coverage Database per code.

### How to use Medicare Fee Coverage Lookup

1. Click **Try for free** (or **Start**) on this Actor's page.
2. In the **Input** tab, set `hcpcs_codes` (e.g. `["99213", "96900"]`), `localities` (e.g. `["CA"]` for every California locality, or `["CA-18"]` for just Los Angeles), and `site_of_service` (`office` or `facility`).
3. Click **Start**. The first run for a given cache period downloads and normalizes the CMS source files (typically 1-3 minutes); every run after that within the 30-day cache window is fast.
4. Open the **Dataset** tab to view, filter, or export the results (JSON, CSV, Excel, HTML, and more). Use the **Payments** view for just the RVU/GPCI/payment breakdown.

### Input

All fields are described with an example in the **Input** tab. Full schema: [`.actor/input_schema.json`](.actor/input_schema.json).

| Field | Type | Description |
|---|---|---|
| `hcpcs_codes` | array of strings | HCPCS/CPT codes to price. |
| `localities` | array of strings | Locality codes in `STATE-LOCALITYNUMBER` form (e.g. `"CA-18"`), or a bare two-letter state/territory code (e.g. `"CA"`) to expand to every locality CMS defines within it. |
| `site_of_service` | string | `"office"` (non-facility PE RVU) or `"facility"` (facility PE RVU). Default `"office"`. |
| `apm_qualifying` | boolean | Price with the 2026 QP-differential conversion factor (for clinicians who are Qualifying APM Participants) instead of the standard one. Default `false`. |
| `force_refresh` | boolean | Maintenance mode: reload all CMS source files and rebuild the cache even if it's under 30 days old. Default `false`. |

#### Three worked inputs

**1. Price a couple of common codes across every California locality (the prefill):**

```json
{
  "hcpcs_codes": ["99213", "96900"],
  "localities": ["CA"],
  "site_of_service": "office"
}
```

**2. Compare office vs. facility payment for one code in one locality:**

```json
{
  "hcpcs_codes": ["99214"],
  "localities": ["NY-01"],
  "site_of_service": "facility"
}
```

**3. A single narrow lookup, e.g. to check coverage matches for one code:**

```json
{
  "hcpcs_codes": ["96900"],
  "localities": ["TX-00"],
  "site_of_service": "office"
}
```

**APM-qualifying pricing** — same code/locality as example 1, but with the 2026 QP-differential conversion factor:

```json
{
  "hcpcs_codes": ["99213"],
  "localities": ["CA-18"],
  "apm_qualifying": true
}
```

**Maintenance run** — force a fresh pull of the CMS source files:

```json
{
  "hcpcs_codes": ["99213"],
  "localities": ["CA-18"],
  "force_refresh": true
}
```

### What this Actor does *not* do

- **No MAC-specific fee schedule adjustments.** This computes the base national PFS formula. Some Medicare Administrative Contractors apply their own further adjustments for specific codes/situations that aren't captured here.
- **No sequestration or other claims-processing reductions.** The payment figure is the pre-sequestration PFS allowed amount, not the actual amount a claim would remit.
- **No anesthesia base-unit pricing.** Anesthesia codes (`001xx`-`019xx`, status `J`) are priced by anesthesia base units × a separate anesthesia conversion factor, not the RVU formula this Actor uses — anesthesia codes will show implausible figures if priced through this endpoint.
- **Billing & Coding Articles are informational only, not a coverage determination.** They're returned in `related_articles`, separate from `coverage_matches`, and are never charged for (`coverage_returned` only fires when `coverage_matches` is non-empty). A code can have related Articles and still show zero `coverage_matches` — that's expected, not a bug.
- **`coverage_matches` only counts a code as covered when it's directly enumerated in that NCD or LCD's own HCPCS code list** — never because it's merely referenced from a related document. In practice this means `coverage_matches` will almost always be empty for standard office-visit E/M codes like 99213: NCDs carry no structured per-code list at all in CMS's downloads (so no code path in this Actor can currently produce an NCD match), and Durable Medical Equipment LCDs — the only LCD type CMS still attaches codes to directly — mostly cover DME/supply codes, not E/M visits. A code that does appear in a DME LCD's own code list will still show a real LCD match.

### Output

One flat JSON object per (HCPCS code, locality) pair, pushed to the default dataset. You can download it as JSON, CSV, Excel, HTML, XML, or RSS from the **Export results** button. Missing values are `null`, never empty strings (see `my_actor/common/models.py`); `partial_data: true` marks a row where the code, the locality, or a payment component couldn't be resolved.

#### Example output row

```json
{
  "hcpcs": "99213",
  "description": "Office o/p est low 20 min",
  "status_code": "A",
  "locality_code": "CA-18",
  "locality_name": "LOS ANGELES-LONG BEACH-ANAHEIM (LOS ANGELES/ORANGE CNTY)",
  "state": "CA",
  "site_of_service": "office",
  "work_rvu": 1.30,
  "pe_rvu": 1.46,
  "mp_rvu": 0.09,
  "work_gpci": 1.041,
  "pe_gpci": 1.183,
  "mp_gpci": 0.664,
  "conversion_factor": 33.4009,
  "conversion_factor_type": "non_qualifying",
  "payment": 104.89,
  "mue_practitioner": 1,
  "mue_facility": 1,
  "coverage_matches": [],
  "related_articles": [
    {
      "type": "Article",
      "id": "A57654",
      "title": "Billing and Coding: Evaluation and Management Services",
      "contractor": "Noridian Healthcare Solutions, LLC",
      "effective_date": "2026-01-01",
      "url": "https://www.cms.gov/medicare-coverage-database/view/article.aspx?articleId=57654"
    }
  ],
  "data_as_of": "2026-09-04",
  "source_urls": [
    "https://downloads.cms.gov/medicare-coverage-database/downloads/exports/current_article.zip",
    "https://downloads.cms.gov/medicare-coverage-database/downloads/exports/current_lcd.zip",
    "https://www.cms.gov/files/zip/medicare-ncci-2026-q4-facility-outpatient-hospital-services-mue-table.zip",
    "https://www.cms.gov/files/zip/medicare-ncci-2026-q4-practitioner-services-mue-table.zip",
    "https://www.cms.gov/files/zip/rvu26d-updated-08-26-2026.zip"
  ],
  "partial_data": false
}
```

Note the code in this example (99213, a standard office-visit E/M code) has zero `coverage_matches` and one `related_articles` entry — see [What this Actor does *not* do](#what-this-actor-does-not-do) for why that's the expected shape, not a bug.

#### Data table

| Field | Type | Description |
|---|---|---|
| `hcpcs` | string | HCPCS/CPT code priced. |
| `description` | string | null | Short CMS description of the code. |
| `status_code` | string | null | PFS status code (e.g. `A` = active/payable). |
| `locality_code` | string | `STATE-LOCALITYNUMBER` locality identifier. |
| `locality_name` | string | null | CMS locality name. |
| `state` | string | null | Two-letter state/territory code. |
| `site_of_service` | string | `"office"` or `"facility"` — which PE RVU was used. |
| `work_rvu`, `pe_rvu`, `mp_rvu` | number | null | Work, practice expense (site-specific), and malpractice RVUs. |
| `work_gpci`, `pe_gpci`, `mp_gpci` | number | null | The three Geographic Practice Cost Indices for this locality. |
| `conversion_factor` | number | null | The conversion factor actually used (matches `conversion_factor_type`). |
| `conversion_factor_type` | string | `"non_qualifying"` or `"qualifying_apm"` — which 2026 conversion factor was used. |
| `payment` | number | null | Computed base PFS payment amount. |
| `mue_practitioner`, `mue_facility` | integer | null | NCCI Medically Unlikely Edit unit limits. |
| `coverage_matches` | array | `{type, id, title, contractor, effective_date, url}` for each NCD/LCD whose own code list includes this code. Never Articles. |
| `related_articles` | array | Same shape, for Billing & Coding Articles that reference this code — informational only, never charged for. |
| `data_as_of` | string (date) | Date the row's data was current as of. |
| `source_urls` | array of strings | CMS source file URL(s) this row was built from. |
| `partial_data` | boolean | `true` if the code, locality, or a payment component couldn't be resolved. |

### Refresh cadence & how caching works

On first use, this Actor downloads the current-quarter CMS PFS RVU release (both the standard and QP-differential conversion factor files), the current NCCI MUE practitioner/facility tables, and the Medicare Coverage Database LCD/Article exports, normalizes them into one lookup table, and caches it for **30 days** in a store shared across runs. The MUE table's own quarter (e.g. `2026-q4`) is read straight out of its filename and folded into the cache's version key, alongside the PFS release label, so the cache key always reflects the actual source files behind it. Every run within that window reuses the cache — CMS's own quarterly release schedule (RVU/MUE updates every ~3 months) means the underlying numbers rarely change more often than that anyway. Each run also does a cheap check for a newer quarterly PFS release and refreshes early if one has appeared, even inside the 30-day window. Set `force_refresh: true` to rebuild the cache immediately regardless of its age.

### Pricing / Cost estimation

This Actor uses the [Pay Per Event](https://docs.apify.com/platform/actors/publishing/monetize/pay-per-event) model:

- `lookup_returned` — charged once per (HCPCS code, locality) row returned.
- `coverage_returned` — charged once per row whose `coverage_matches` is non-empty (an NCD or LCD directly lists the code). Related Articles (`related_articles`) are never charged for.

Free-plan users are capped at 25 rows per run (see `my_actor/common/free_tier.py`); paying users are not capped. A run pricing 2 codes across 18 California localities (36 rows) with, say, half of them having a coverage match costs 36 `lookup_returned` events + 18 `coverage_returned` events.

### Tips / Advanced options

- Pass a bare two-letter state (`"CA"`, `"TX"`, `"NY"`) in `localities` to price a code everywhere in that state without looking up every locality number yourself.
- Batch as many `hcpcs_codes` and `localities` as you need in one run — the cache means the marginal cost of a big run is just the per-row charges, not extra fetch time.
- Use `force_refresh: true` sparingly — it re-downloads and re-parses ~150MB of CMS zip files, which takes real time. The 30-day cache plus the automatic new-release check cover the normal case.

### FAQ, disclaimers, and support

This Actor reads only from public CMS data downloads (CMS Physician Fee Schedule, NCCI, and Medicare Coverage Database file exports); it does not scrape any paywalled or ToS-restricted site. It is not legal, billing, or coding advice, and CMS payment rules change — always confirm against the source files linked in `source_urls` before relying on a number for actual billing. Found a code or locality that looks wrong, or a CMS file structure change this Actor doesn't handle? Open an issue on the Actor's **Issues** tab.

### Related tools

Part of the **Medicare Tools** family on Apify:

- [Medicare Reimbursement Calculator](https://apify.com/iceni-data/medicare-reimbursement-calculator) — prices a single HCPCS/CPT code with modifiers, participation status, and sequestration, one code at a time.

# Actor input Schema

## `hcpcs_codes` (type: `array`):

HCPCS or CPT codes to price, e.g. "99213" (office visit) or "96900" (actinotherapy).

## `localities` (type: `array`):

Medicare locality codes in `STATE-LOCALITYNUMBER` form (e.g. "CA-18" for Los Angeles), or a bare two-letter state/territory code (e.g. "CA") to expand to every locality CMS defines within it.

## `site_of_service` (type: `string`):

Which practice-expense RVU to price: "office" uses the non-facility PE RVU, "facility" uses the facility PE RVU (e.g. hospital outpatient, ASC).

## `apm_qualifying` (type: `boolean`):

Beginning 2026, CMS pays a higher PFS conversion factor to clinicians who are Qualifying Alternative Payment Model (APM) Participants (QPs) for the year. Set true to price with that differential conversion factor instead of the standard one.

## `force_refresh` (type: `boolean`):

Reload the PFS RVU, GPCI, NCCI MUE, and MCD LCD/Article files from CMS and rebuild the cached lookup table even if the cache is under 30 days old.

## Actor input object example

```json
{
  "hcpcs_codes": [
    "99213",
    "96900"
  ],
  "localities": [
    "CA"
  ],
  "site_of_service": "office",
  "apm_qualifying": false,
  "force_refresh": false
}
```

# Actor output Schema

## `dataset` (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 = {
    "hcpcs_codes": [
        "99213",
        "96900"
    ],
    "localities": [
        "CA"
    ],
    "site_of_service": "office"
};

// Run the Actor and wait for it to finish
const run = await client.actor("iceni-data/medicare-fee-coverage-lookup").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 = {
    "hcpcs_codes": [
        "99213",
        "96900",
    ],
    "localities": ["CA"],
    "site_of_service": "office",
}

# Run the Actor and wait for it to finish
run = client.actor("iceni-data/medicare-fee-coverage-lookup").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 '{
  "hcpcs_codes": [
    "99213",
    "96900"
  ],
  "localities": [
    "CA"
  ],
  "site_of_service": "office"
}' |
apify call iceni-data/medicare-fee-coverage-lookup --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,iceni-data/medicare-fee-coverage-lookup"
        }
    }
}

```

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/cRZSbBvQCEYo8MpKi/builds/RTVxhs2rgjgvBlUHR/openapi.json
