Drugs and formularies
formulary_churn
Formulary Churn·v1.0.0·L2·read-only·MCP + REST·admitted 2026-09-19
Inputs
92 required
Sources
2
Known limits
4
Real answer
15.26scomplete
What it answers
- 1Given a quarter-pair (report_quarter_from, report_quarter_to) AND a formulary or plan filter, returns the formulary delta: drugs added, drugs dropped, drugs re-tiered, and optionally drugs whose control flags (prior auth / step therapy / quantity limit) changed.
- 2Diffs drug_silver.fact_drug_formulary across two SPUF report_quarters on (formulary_id, drug) keys, where a drug is its RxCUI by default (grain='rxcui'), so a manufacturer or package NDC swap of the same drug is not churn; grain='ndc' gives the package-level diff.
- 3Pass rxcui_filter=[...] to ask whether coverage of specific drugs changed.
- 4Returns: per-category row lists (cap 5000 each), aggregates (formularies_compared, n_added, n_dropped, n_tier_changed, n_control_changed, n_unchanged, churn_rate, tier_movement_distribution), truncated_by_category flags.
- 5READING churn_rate: it is (added + dropped + re-tiered) divided by the number of drugs on the formulary in the FROM quarter — CHANGE EVENTS PER FROM-QUARTER DRUG, not a percentage.
- 6It is >= 0 and CAN EXCEED 1 (a formulary that drops everything and adds as many new drugs scores 2.0), because added drugs are not members of the denominator.
- 7The answer carries churn_rate_definition saying exactly this, the two denominators (n_in_from_quarter, n_in_to_quarter), and two bounded companions that ARE 0-1: share_dropped (dropped / FROM-quarter drugs) and share_added (added / TO-quarter drugs).
- 8Use cases: year-over-year formulary stability tracking, drop investigations for a specific drug class, IRA-era churn rate (crossing 2025 → 2026 will surface the redesign-driven churn).
- 9KNOWN GAPS: spuf_quarterly_publication_lag (CMS publishes 6 weeks after quarter end; very recent quarter changes won't show); ira_era_break_2026 (IRA Part D redesign in 2026 changed formulary structures; churn comparison crossing this boundary mixes regime changes with normal churn); formulary_churn_truncated (per-category lists capped at 5000).
Inputs (9)
| Name | Type | Required | Default | Accepts |
|---|---|---|---|---|
report_quarter_from | string | required | ||
report_quarter_to | string | required | ||
acknowledge_era_break | boolean | optional | false | |
contract_id_filter | string | optional | ||
formulary_id_filter | array | optional | ||
grain | string | optional | "rxcui" | |
include_control_changes | boolean | optional | false | |
plan_id_filter | string | optional | ||
rxcui_filter | array | optional |
Call it
# MCP (https://mcp.vladahealth.com/mcp): the tool by name formulary_churn(report_quarter_from="2024-Q3", report_quarter_to="2024-Q4", contract_id_filter="H1036")
curl -X POST https://api.vladahealth.com/v1/tools/formulary_churn \
-H "Authorization: Bearer $VLADA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"report_quarter_from":"2024-Q3","report_quarter_to":"2024-Q4","contract_id_filter":"H1036"}'
# the schema, no key
curl https://api.vladahealth.com/v1/tools/formulary_churn/schemaA real answer
customer seat, 2026-11-09·over REST·15.26s·status
complete·first 6,000 of 62,515 chars·verbatim{
"value": {
"headline": 2.0,
"report_quarter_from": "2024-Q3",
"report_quarter_to": "2024-Q4",
"formulary_ids_compared": [
"00019434",
"00019435",
"00019436",
"00019440",
"00019442",
"00019444",
"00019445",
"00019449",
"00019450",
"00020457",
"00020458",
"00020459",
"00020460",
"00020461",
"00020462",
"00020463",
"00020467",
"00020468",
"00021450",
"00021454",
"00021456",
"00021457",
"00021460",
"00021461",
"00021462",
"00022520",
"00022524",
"00022525",
"00022526",
"00022530",
"00022531",
"00022532",
"00023492",
"00023493",
"00023496",
"00023497",
"00023503",
"00023505",
"00023506",
"00023511",
"00024492",
"00024493",
"00024495",
"00024496",
"00024499",
"00024500",
"00024502",
"00024504",
"00024507",
"00025456",
"00025458",
"00025460",
"00025461",
"00025462",
"00025463",
"00025464",
"00026408",
"00026409",
"00026411",
"00026412"
],
"truncated_by_category": {
"added": true,
"dropped": true,
"tier_changed": false,
"control_changed": false
},
"include_control_changes": false,
"churn_rate_definition": "churn_rate = (drugs added + drugs dropped + drugs re-tiered) / drugs on the formulary in the FROM quarter. It counts CHANGE EVENTS per drug that was on the formulary at the start, so it is >= 0 and CAN EXCEED 1: a formulary that drops everything and adds an equal number of new drugs scores 2.0. It is not a percentage of the formulary and must not be read as one. For bounded readings use share_dropped (dropped / FROM-quarter drugs, 0-1) and share_added (added / TO-quarter drugs, 0-1).",
"churn_rate_range": {
"minimum": 0.0,
"maximum": null,
"unit": "change events per FROM-quarter drug"
},
"aggregates": {
"grain": "rxcui",
"formularies_compared": 60,
"n_added": 10000,
"n_dropped": 10000,
"n_tier_changed": 0,
"n_control_changed": 0,
"n_unchanged": 0,
"churn_rate": 2.0,
"churn_rate_definition": "churn_rate = (drugs added + drugs dropped + drugs re-tiered) / drugs on the formulary in the FROM quarter. It counts CHANGE EVENTS per drug that was on the formulary at the start, so it is >= 0 and CAN EXCEED 1: a formulary that drops everything and adds an equal number of new drugs scores 2.0. It is not a percentage of the formulary and must not be read as one. For bounded readings use share_dropped (dropped / FROM-quarter drugs, 0-1) and share_added (added / TO-quarter drugs, 0-1).",
"n_in_from_quarter": 10000,
"n_in_to_quarter": 10000,
"share_dropped": 1.0,
"share_added": 1.0,
"tier_movement_distribution": {
"up_1": 0,
"up_2_or_more": 0,
"down_1": 0,
"down_2_or_more": 0
}
},
"added_drugs": [
{
"formulary_id": "00025456",
"ndc11": "00093500456",
"rxcui": "1000001",
"tier_level_value": 4
},
{
"formulary_id": "00025456",
"ndc11": "59651017301",
"rxcui": "1000048",
"tier_level_value": 3
},
{
"formulary_id": "00025456",
"ndc11": "54838051240",
"rxcui": "1000054",
"tier_level_value": 4
},
{
"formulary_id": "00025456",
"ndc11": "59651017701",
"rxcui": "1000058",
"tier_level_value": 3
},
{
"formulary_id": "00025456",
"ndc11": "49884022201",
"rxcui": "1000064",
"tier_level_value": 3
},
{
"formulary_id": "00025456",
"ndc11": "59651017401",
"rxcui": "1000070",
"tier_level_value": 3
},
{
"formulary_id": "00025456",
"ndc11": "59651017501",
"rxcui": "1000076",
"tier_level_value": 3
},
{
"formulary_id": "00025456",
"ndc11": "00378537501",
"rxcui": "1000097",
"tier_level_value": 3
},
{
"formulary_id": "00025456",
"ndc11": "00555077902",
"rxcui": "1000114",
"tier_level_value": 2
},
{
"formulary_id": "00025456",
"ndc11": "55150032901",
"rxcui": "1000126",
"tier_level_value": 2
},
{
"formulary_id": "00025456",
"ndc11": "00555087202",
"rxcui": "1000135",
"tier_level_value": 2
},
{
"formulary_id": "00025456",
"ndc11": "00555087302",
"rxcui": "1000141",
"tier_level_value": 2
},
{
"formulary_id": "00025456",
"ndc11": "66993037179",
"rxcui": "1000153",
"tier_level_value": 2
},
{
"formulary_id": "00025456",
"ndc11": "00009470913",
"rxcui": "1000158",
"tier_level_value": 3
},
{
"formulary_id": "00025456",
"ndc11": "68462030450",
"rxcui": "1000405",
"tier_level_value": 3
},
{
"formulary_id": "00025456",
"ndc11": "61314020315",
"rxcui": "1000647",
"tier_level_value": 3
},
{
"formulary_id": "00025456",
"ndc11": "00574419035",
"rxcui": "1000673",
"tier_level_value": 3
},
{
"formulary_id": "00025456",
"ndc11": "61314020415",
"rxcui": "1000862",
"tier_level_value": 3
},
{
"formulary_id": "00025456",
"ndc11": "61314020615",
"rxcui": "1000897",
"tier_level_value": 3
},
{
"formulary_id": "00025456",
"ndc11": "00527131301",
"rxcui": "1000913",
"tier_level_value": 4
},
{
"formulary_id": "00025456",
"ndc11": "00527140701",
"rxcui": "1001004",
"tier_level_value": 4
},
{
"formulary_id": "00025456",
"ndc11": "51672417005",
"rxcui": "1001689",
"tier_level_value": 3
},
{
"formulary_id": "00025456",
"ndc11": "00078059287",
"rxcui": "1002300",
"tier_level_value": 5
},
{
"formulary_id": "00025456",
"ndc11": "70700011685",
"rxcui": "1006065",
"tier_level_value": 4
},
{
"formulary_id": "00025456",
"ndc11": "24208067004",
"rxcui": "1006120",
"tier_level_value": 2
},
{
"formulary_id": "00025456",
"ndc11": "45802089626",
"rxcui": "1006688",
"tier_level_value": 4
},
{
"formulary_id": "00025456",
…What comes back
| Field | Holds |
|---|---|
status | complete · partial · refused · failed · queued |
value | rows or the figure, with its unit |
source · caveats · invariants_applied · response_hash | the public file and vintage; what to know before citing; the rules honoured; replayable |
refusal | code (15), message, recover_with |
Sources
| Kind | Name |
|---|---|
| source | drug_silver.fact_drug_formulary |
| source | drug_silver.dim_part_d_plan |
| table | drug_silver.fact_drug_formulary |
| table | drug_silver.dim_part_d_plan |
Known limits (4)
- 1spuf_quarterly_publication_lag
- 2ira_era_break_2026
- 3formulary_churn_truncated
- 4formulary_churn_no_match
This page as markdown: /docs-md/tools/formulary_churn.md · every page in one file: /llms-full.txt