# Medicare Part D Drug Spending Delta & Top Movers API (CMS) (`malonestar/cms-part-d-prescriber-delta`) Actor

Year-over-year Medicare Part D drug spending deltas and top movers from the keyless CMS API. Get spend change %, per-claim and per-beneficiary cost change, claims and beneficiary deltas, and CAGR per drug. Filter by drug, manufacturer, min spend or min change. No API key.

- **URL**: https://apify.com/malonestar/cms-part-d-prescriber-delta.md
- **Developed by:** [Kyle Maloney](https://apify.com/malonestar) (community)
- **Categories:** Lead generation, Developer tools, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.40 / 1,000 results

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/platform/actors/running/actors-in-store#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

## CMS Medicare Part D Spending by Drug - Year-over-Year Delta and Top Movers

Turns CMS's annual **Medicare Part D Spending by Drug** file into ranked
per-drug spending deltas: total spend, claims, beneficiaries, average spend per
claim / per beneficiary / per dosage unit, plus CMS's own change and CAGR
figures. Keyless, official CMS data-api.

**v1.1 makes drug launches visible.** Measured live on 2026-08-01: **197 of the
3,625 drugs (5.4%) have only one year of data** because they entered Part D in
the latest year. A percentage change from a base that does not exist is
undefined, so they can never appear on a percentage ranking - and in v1.0 they
sorted to the *top* of a "biggest decliners" list. Acthar Selfject spent
**$32,500,305.86 in its first Part D year** and would have been reported as one
of the programme's largest declines. Fixed, and launches are now labelled and
rankable on their own key.

### Who it's for

- **Pharma and payer analytics** teams tracking Part D spend shifts by drug.
- **Health-policy researchers** studying drug pricing and utilisation.
- **Investors and market-access teams** watching launches and losses of exclusivity.
- **AI agents** needing a "what is moving in Medicare Part D" tool.

### Example input

```json
{
  "mode": "top_movers",
  "sortBy": "spending_change_abs_incl_new_entrants",
  "sortDirection": "desc",
  "minSpendLatest": 100000000,
  "topN": 100
}
```

Just the launches:

```json
{ "newEntrantsOnly": true, "sortBy": "new_entrant_spending_usd", "topN": 50 }
```

Only genuine one-year deltas:

```json
{ "requireYearOverYear": true, "sortBy": "spending_change_pct", "topN": 100 }
```

### Inputs

| Input | Type | Notes |
|---|---|---|
| `mode` | string | `top_movers` or `search`. |
| `drugName` / `manufacturer` | string | Case-insensitive substring filters. |
| `minSpendLatest` | integer | Minimum latest-year spend, USD. |
| `minAbsChangePct` | number | Minimum absolute percentage change. |
| `sortBy` | string | See the note below on ranking launches. |
| `sortDirection` | string | `desc` or `asc`. Rows with **no value** for the chosen key sort **last in both directions**. |
| `requireLatestYear` | boolean | Keep only drugs present in the newest data year. |
| `includeManufacturerBreakdown` | boolean | Pull per-manufacturer rows as well as per-drug `Overall` rows. |
| `newEntrantsOnly` | boolean | **New in v1.1.** Only drugs in their first published Part D year. Default false. |
| `requireYearOverYear` | boolean | **New in v1.1.** Drop rows whose latest and prior years are more than one year apart. Default false. |
| `topN` / `maxResults` | integer | Result caps. |

#### Ranking a launch

A drug in its first Part D year has no `spending_change_pct` - the base does not
exist. Sort by **`spending_change_abs_incl_new_entrants`** to rank launches
alongside continuing drugs: for a launch it is the full latest-year spend, from
an implied zero base; for a continuing drug it is the ordinary absolute delta.

### Output fields

One row per drug (or per drug x manufacturer with the breakdown on). **63 declared fields.**

#### Drug identity

| Field | Description |
|---|---|
| `brand_name` | Drug brand (trade) name. |
| `generic_name` | Generic (chemical) drug name. |
| `manufacturer_name` | Manufacturer name, or "Overall" for the drug-level summary row. |
| `manufacturer_count` | Number of manufacturers CMS reports for this drug. |
| `manufacturer_breakdown_included` | True when per-manufacturer rows were pulled as well as the per-drug Overall rows. |

#### Years covered

| Field | Description |
|---|---|
| `latest_year` | Most recent data year available for this drug. |
| `prior_year` | Second-most-recent data year, used as the delta baseline. |
| `first_year` | Earliest data year available for this drug. |
| `years_covered` | Count of years with spending data for this drug. |
| `years_available` | Every data year CMS publishes a spending figure for on this drug. |
| `delta_span_years` | How many years separate latest\_year from prior\_year. 23 live rows span more than one year (Ayuna jumps 2020 to 2024), so their spending\_change\_pct is a multi-year change. |
| `delta_is_year_over_year` | True only when delta\_span\_years is exactly 1. When false, spending\_change\_pct is NOT a year-over-year figure and is not comparable with rows where it is. |
| `latest_data_year_available` | Newest data year present in the retrieved CMS rows. |
| `data_year_lag` | Calendar years between the newest CMS data year and today. Measured 2026-08-01: 2. |

#### New market entrants

| Field | Description |
|---|---|
| `is_new_entrant` | True when CMS publishes only ONE year of spending for this drug, i.e. it entered Medicare Part D in the latest year. 197 of the 3,625 drugs (5.4%) were new entrants in the 2024 edition. |
| `entrant_status` | new\_entrant, continuing, or unknown. |
| `new_entrant_spending_usd` | Latest-year spending for a new entrant, in USD. Null for continuing drugs. |
| `spending_change_abs_incl_new_entrants` | Absolute year-over-year spending change; for a new entrant it is the full latest-year spend, since a launch enters from an implied zero base. This is the only key on which a launch is rankable - the percentage from a non-existent base stays undefined. |

#### Spending

| Field | Description |
|---|---|
| `total_spending_latest` | Total Medicare Part D spending in USD for the latest year. |
| `total_spending_prior` | Total spending in USD for the prior year. |
| `total_spending_first_year` | Total spending in the earliest year CMS publishes for this drug. |
| `spending_change_pct` | Year-over-year percent change in total spending (latest vs prior). |
| `spending_change_abs` | Year-over-year absolute change in total spending, USD. |
| `spending_change_pct_full_span` | Percent change in spending from the first published year to the latest - the full multi-year trend, not just the last step. |
| `spending_trend` | Coarse trend label from the YoY spending change: rising, falling, or flat. |

#### Claims and beneficiaries

| Field | Description |
|---|---|
| `total_claims_latest` | Total Part D claims (fills) in the latest year. |
| `total_claims_prior` | Total claims in the prior year. |
| `claims_change_pct` | Year-over-year percent change in total claims. |
| `total_beneficiaries_latest` | Distinct Medicare beneficiaries in the latest year. |
| `total_beneficiaries_prior` | Distinct beneficiaries in the prior year. |
| `beneficiaries_change_pct` | Year-over-year percent change in beneficiaries. |

#### Unit economics

| Field | Description |
|---|---|
| `avg_spend_per_claim_latest` | Average Medicare spending per claim, latest year (USD). |
| `avg_spend_per_claim_prior` | Average spending per claim, prior year (USD). |
| `avg_spend_per_claim_change_pct` | Year-over-year percent change in average spend per claim. |
| `avg_spend_per_beneficiary_latest` | Average spending per beneficiary, latest year (USD). |
| `avg_spend_per_beneficiary_prior` | Average spending per beneficiary, prior year (USD). |
| `avg_spend_per_beneficiary_change_pct` | Year-over-year percent change in average spend per beneficiary. |
| `avg_spend_per_dosage_unit_latest` | Weighted average spending per dosage unit, latest year (USD). |
| `avg_spend_per_dosage_unit_prior` | Weighted average spending per dosage unit, prior year (USD). |
| `avg_spend_per_dosage_unit_change_pct` | Year-over-year percent change in average spend per dosage unit. |
| `total_dosage_units_latest` | Total dosage units dispensed in the latest year. |
| `total_dosage_units_prior` | Total dosage units in the prior year. v1.0 emitted only the latest year. |

#### CMS-supplied figures

| Field | Description |
|---|---|
| `cms_chg_avg_spend_per_dosage_unit` | CMS-supplied change in average spend per dosage unit (decimal fraction) over its reported year pair. |
| `cms_chg_year_pair` | Year pair CMS used for the above change figure. |
| `cms_cagr_avg_spend_per_dosage_unit` | CMS-supplied compound annual growth rate of average spend per dosage unit (decimal fraction). |
| `cms_cagr_year_range` | Year range CMS used for the CAGR figure. |
| `outlier_flag_latest` | CMS high-cost outlier flag for the latest year (1 = flagged). |
| `outlier_flag_prior` | CMS outlier flag for the prior year: 1 when CMS marks the drug an outlier, 0 when not, null when CMS published no flag. |

#### Full published series

| Field | Description |
|---|---|
| `spending_by_year_json` | JSON object of year to total spending USD, for every year CMS publishes. v1.0 fetched five years and emitted two. |
| `claims_by_year_json` | JSON object of year to total claims. |
| `beneficiaries_by_year_json` | JSON object of year to total beneficiaries. |
| `dosage_units_by_year_json` | JSON object of year to total dosage units. |

#### Completeness, status and provenance

| Field | Description |
|---|---|
| `cms_dataset_status` | Per-source outcome for the CMS data-api. Never blank on a healthy row. If CMS never answered, the run fails and emits nothing. |
| `cms_dataset_error` | Error text if the CMS pull degraded. Null on a healthy run, which is good news, not a dead column. |
| `cms_distribution_uuid` | The CMS data-api distribution that answered this run. |
| `cms_uuid_source` | catalog\_confirmed when the pinned distribution is the one CMS lists as latest, catalog when it was discovered, pinned\_fallback otherwise. The run fails if the distribution is not the current edition. |
| `cms_catalog_modified` | Last-modified date CMS publishes for the dataset in its catalog. |
| `source_rows_expected` | Exact row count CMS reports for this query via /data/stats found\_rows, which respects the same filter as the data call. |
| `source_rows_fetched` | Rows actually retrieved and analysed. |
| `source_rows_complete` | True when every row CMS reports for this query was retrieved. A ranking over an arbitrary subset would be wrong, not merely incomplete, so a shortfall fails the run. |
| `retrieved_at` | UTC timestamp when this run queried CMS. |
| `source_system` | Originating dataset. |
| `source_url` | CMS dataset landing page. |

### Reliability - what this Actor asserts live, on every run

An offline fixture cannot see a superseded edition, because the fixture was
shaped from the superseded data. Before any billable row is emitted, the run
fails if any of these regress. Every measured value is logged.

1. **Vintage** - the distribution being queried must be the one CMS's own
   catalogue lists as `latest`. v1.0 probed a pinned UUID first and returned it
   whenever it still had rows, so a superseded edition that stayed online would
   be answered from indefinitely. Three actors in this portfolio shipped exactly
   that failure.
2. **Row-count band** - measured 2026-08-01: 14,536 total rows, 3,625 `Overall`
   per-drug rows.
3. **Negative control** - an impossible manufacturer filter must return an empty
   array. If CMS ever stops applying `filter[Mftr_Name]`, an `Overall` pull would
   silently become the whole 14,536-row file with per-manufacturer rows
   masquerading as drug totals.
4. **Reconciliation** - `/data/stats` returns a `found_rows` that respects the
   same filter as the data call. Retrieving fewer rows than that fails the run:
   a ranking computed over an arbitrary subset is wrong, not merely incomplete.
5. **Freshness** - the newest data year must be within four calendar years of
   today (measured 2026-08-01: data year 2024, lag 2).

If the CMS data-api never answers, the run **fails** with an explicit message.
It never emits "no drugs matched", which is a different fact.

### Limits you should know

- CMS publishes this file annually, roughly 18 months after year end. The 2026
  edition covers data years **2020-2024**.
- 23 drugs have a gap of more than one year between their latest and prior
  published years. Their `spending_change_pct` is a multi-year change;
  `delta_is_year_over_year` is `false` on those rows, and `requireYearOverYear`
  removes them.
- `Overall` rows are per-drug totals across all manufacturers. Turn on
  `includeManufacturerBreakdown` for per-manufacturer detail.
- Spending is gross Part D spend before manufacturer rebates, which CMS does not
  publish at drug level.

### Use as an MCP tool

Available to AI agents through Apify's MCP server. An agent can read
`is_new_entrant` and `delta_is_year_over_year` to know whether a percentage is
even defined and comparable before quoting it.

### Pricing

Pay per result: one event per emitted drug record. A run that fails a drift
assertion or never reaches CMS emits nothing and bills nothing.

### FAQ

**Why is `spending_change_pct` null on some drugs?** Because CMS publishes only
one year for them - they launched. Sort by
`spending_change_abs_incl_new_entrants` to rank them.

**Did the v1.1 changes alter my existing results?** Only one ordering changed,
and only because it was wrong: rows with no value for the chosen sort key used
to rank as the extreme *low* value, which put launches at the top of an
ascending "biggest decliners" list. They now sort last in both directions. Every
output field keeps its v1.0 value; the new inputs default to off.

**Is this the same as the CMS quarterly file?** No. This is the annual
*Medicare Part D Spending by Drug* dataset.

### Related actors

- **CMS Open Payments Roll-up** - industry payments to prescribers.
- **CMS NADAC Price Movers** - pharmacy acquisition-cost movement.
- **Drug Shortage Delta Monitor** - openFDA shortage changes.

# Actor input Schema

## `mode` (type: `string`):

"top\_movers" ranks drugs by the size of their year-over-year spending change (best for finding price/utilization movers). "search" returns every drug matching your filters, still sorted by the chosen field.

## `drugName` (type: `string`):

Case-insensitive substring match against the brand name and generic name (e.g. "Ozempic", "Semaglutide", "insulin"). Leave empty to include all drugs.

## `manufacturer` (type: `string`):

Case-insensitive substring match against the manufacturer name (e.g. "Novo Nordisk", "Pfizer"). Supplying this automatically includes the per-manufacturer rows (not just the drug's "Overall" summary).

## `minSpendLatest` (type: `integer`):

Only include drugs whose most recent year of total Medicare Part D spending is at least this many US dollars. Use it to focus on high-impact drugs and filter out long-tail noise.

## `minAbsChangePct` (type: `integer`):

Only include drugs whose absolute year-over-year total-spending change is at least this percent (e.g. 25 keeps drugs that rose or fell by 25%+). 0 disables the filter.

## `sortBy` (type: `string`):

Metric used to rank results. NOTE: a drug in its FIRST Part D year has no percentage change (the base does not exist), so it cannot appear on a percentage ranking. Sort by "spending\_change\_abs\_incl\_new\_entrants" to rank launches alongside continuing drugs. Rows with no value for the chosen key always sort LAST, in both directions.

## `sortDirection` (type: `string`):

"desc" surfaces the biggest increases (or highest values) first; "asc" surfaces the biggest decreases (or lowest values) first.

## `requireLatestYear` (type: `boolean`):

When true (recommended), only include drugs whose most recent data year equals the dataset's newest year, so every result is the same fresh year-over-year comparison and discontinued drugs are excluded. Turn off to include drugs that stopped reporting in earlier years.

## `includeManufacturerBreakdown` (type: `boolean`):

When false (default), returns one "Overall" row per drug. When true, also returns a row per manufacturer for each drug (useful for multi-source generics).

## `topN` (type: `integer`):

In "top\_movers" mode, cap the output to the top N ranked drugs after filtering and sorting.

## `maxResults` (type: `integer`):

Hard cap on the number of records written to the dataset (billed as results).

## `newEntrantsOnly` (type: `boolean`):

Emit only drugs in their first published Medicare Part D year. 197 of 3,625 drugs (5.4%) were new entrants in the 2024 edition.

## `requireYearOverYear` (type: `boolean`):

Drop drugs whose latest and prior published years are more than one year apart, so every spending\_change\_pct in the result is a genuine year-over-year figure. 23 live rows span more than one year.

## Actor input object example

```json
{
  "mode": "top_movers",
  "minSpendLatest": 100000000,
  "minAbsChangePct": 0,
  "sortBy": "spending_change_pct",
  "sortDirection": "desc",
  "requireLatestYear": true,
  "includeManufacturerBreakdown": false,
  "topN": 100,
  "maxResults": 5000,
  "newEntrantsOnly": false,
  "requireYearOverYear": false
}
```

# Actor output Schema

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

The default dataset with one record per drug (or per manufacturer).

# 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 = {
    "mode": "top_movers",
    "minSpendLatest": 100000000,
    "sortBy": "spending_change_pct",
    "sortDirection": "desc",
    "requireLatestYear": true,
    "topN": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("malonestar/cms-part-d-prescriber-delta").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 = {
    "mode": "top_movers",
    "minSpendLatest": 100000000,
    "sortBy": "spending_change_pct",
    "sortDirection": "desc",
    "requireLatestYear": True,
    "topN": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("malonestar/cms-part-d-prescriber-delta").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "mode": "top_movers",
  "minSpendLatest": 100000000,
  "sortBy": "spending_change_pct",
  "sortDirection": "desc",
  "requireLatestYear": true,
  "topN": 100
}' |
apify call malonestar/cms-part-d-prescriber-delta --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=malonestar/cms-part-d-prescriber-delta",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/WM6s8eHBSlMI9QIpZ/builds/4ygP7YasltzaAqzeS/openapi.json
