Medicare Advantage, Part D, Medicaid

PBP otc (cleaned view)

medicare_semantic.vw_pbp_otc·live·semantic_view·annual
Rows
N/A
Columns
103
History
2010 to 2026
Card
yesgrain, traps, filters
Typed tools
1

What it is

  1. 1A long-history (2010-2026) Medicare Advantage plan-benefit-package (PBP) view describing each plan's Over-The-Counter (OTC) supplemental benefit, sourced from the public CMS PBP PUF (section B13).
  2. 2For every MA contract/plan/segment it records, per OTC component, whether the benefit is offered, its dollar allowance + period (when the plan files one), cost-share, quantity limits, and a rich set of CMS indicator flags (cap present, carry-forward, delivery mode, NRT/naloxone, covered-list).
  3. 3It is the OTC slice of a normalized PBP semantic layer for analyzing MA supplemental-benefit generosity and cost.

Grain

KeyGrain
contract_idplan_idsegment_idreport_yearquarterotc_sub_benefit
  1. 1One row per MA plan-segment per PBP report year per intra-year release snapshot (quarter) per OTC sub-benefit component.
  2. 2Verified: COUNT(*) = 503,753 exactly equals COUNT(DISTINCT contract_id, plan_id, segment_id, report_year, quarter, otc_sub_benefit), and a GROUP BY on those 6 columns yields max 1 row per group across all 503,753 groups.
  3. 3A plan-snapshot fans out into up to 4 component rows (otc_list_coverage, otc_items_allowance, otc_nicotine_replacement_therapy, otc_naloxone). version is constant within each grain group (not a key).
  4. 4Only otc_items_allowance carries a dollar amount; the other three components are indicator-only.

How to read it

  1. 1Get current-year OTC allowance generosity per MA plan: SELECT contract_id, plan_id, segment_id, benefit_amount, benefit_amount_period_label, benefit_amount_annualized, benefit_amount_pmpm FROM vw_pbp_otc WHERE report_year=2026 AND quarter=(SELECT MAX(quarter) FROM vw_pbp_otc WHERE report_year=2026) AND otc_sub_benefit='otc_items_allowance' AND benefit_amount IS NOT NULL ORDER BY benefit_amount_annualized DESC — answers 'which 2026 Medicare Advantage plans offer the richest OTC dollar allowance, normalized to an annual figure.' Filtering to MAX(quarter) avoids snapshot double-counting; restricting to otc_items_allowance is required because only that component holds dollars.

Traps

  1. 1Grain is 6 columns including quarter and otc_sub_benefit. (contract_id, plan_id, segment_id) is NOT unique (only 92,166 plan-year keys vs 503,753 rows). Each plan-snapshot fans into up to 4 sub-benefit rows; do not treat one plan as one row.
  2. 2There is NO is_current/snapshot flag. 'quarter' is an intra-year CMS PUF republication, and snapshot years (2018, 2019, 2023 = quarters 1-4; 2024 = 1,3,4; 2020 = 1,2,4) repeat the SAME plan data each quarter (amounts are stable across quarters, max distinct amount per plan-year = 1). Counting all quarters 2-4x inflates plan counts. To get one current row per plan-year, filter to MAX(quarter) within each report_year.
  3. 3Dollars live on ONE sub-benefit only: benefit_amount / _annualized / _pmpm are non-null exclusively on otc_sub_benefit='otc_items_allowance' (and even there only ~47%). The other three components (otc_list_coverage, otc_nicotine_replacement_therapy, otc_naloxone) are indicator-only and carry $0/null amounts by design — never SUM benefit_amount across sub-benefits or read OTC spend off the list/NRT/naloxone rows.
  4. 4A null benefit_amount usually does NOT mean missing data — read null_amount_reason: 'in_kind_or_vendor_administered' (250,819 rows; OTC delivered via vendor card so no filed $), 'unlimited', 'not_offered', or 'unknown_requires_crosswalk'. offered_status='offered_unknown' (160k rows) also means the offered flag is genuinely indeterminate, not absent.
  5. 5Sub-benefit availability is year-gated: otc_nicotine_replacement_therapy rows exist only report_year>=2019, otc_naloxone only 2025-2026. Their indicator columns (otc_nrt_indicator, otc_naloxone_indicator) are null for ~85-98% of the table because those rows simply don't exist in earlier years — not because the value is missing.
  6. 6Several columns are degenerate/empty and carry no signal in this view: benefit_category (always 'otc'), network_type (always 'in_network'), confidence_tier (always 'source_faithful'), consumer_document_reconciliation_status (always 'not_checked'), and lmi_authorities (always ''). Don't filter on them expecting variation.
  7. 7Use report_year as the time axis, NOT quarter — the profiler mislabels 'quarter' as the time column. Period labels like 'Every three months' refer to the allowance refill period, not a calendar quarter; annualize via benefit_amount_annualized to compare plans.

Columns (103)

ColumnTypeMeaning
contract_idstringCMS MA contract id, e.g. H0104, H3347, E5088 (E-prefix = employer/EGWP). 1,680 distinct. Part of grain.
plan_idstringPlan id within the contract, e.g. 001, 007, 027 (zero-padded string). 656 distinct values; only unique together with contract_id+segment_id. Part of grain.
segment_idstringPlan benefit segment, mostly '0'; 26 distinct. Part of grain — identifies the PBP segment when a plan is segmented by geography.
versiondoublePBP submission/bid version number (double, 1-26). Constant within a grain group (one version per plan-snapshot); NOT a grain key. Reflects which bid revision the row came from.
report_yeardoubleContract/benefit year the PBP applies to (2010-2026). THE real history axis of this table (17 years). Use this, not 'quarter', for time-series.
quarterdoubleIntra-year CMS PUF release snapshot (1-4), NOT a separate benefit period. Older years have only quarter=1; snapshot years (2018/2019/2023 have 1-4, 2020/2024 have 3) republish the SAME plan-year data each quarter. A trap: counting all quarters 2-4x-inflates plans. Dedup by MAX(quarter) per plan-year to get one current snapshot.
benefit_categorystringAlways 'otc' — this view is the OTC slice only. Degenerate single-value column.
cost_share_stringstringRaw cost-share text as filed. Null in nearly all rows (OTC is usually $0 cost-share / allowance-based).
copay_amountdoubleGeneric per-visit copay from the PBP cost-share block. Largely null for OTC; the OTC-specific copay lives in otc_copay_amount_min/max.
coinsurance_pctdoubleGeneric coinsurance percent. Largely null for OTC.
limit_textstringRaw free-text limit description from the bid. Largely null.
auth_requiredbooleanBoolean: prior authorization required. Largely null for OTC.
referral_requiredbooleanBoolean: referral required. Largely null for OTC.
network_typevarchar(10)Always 'in_network' — degenerate single-value column for this view (OTC is in-network only).
max_oop_appliesbooleanBoolean: does this benefit count toward the plan MOOP. Largely null.
limit_quantitydoubleNumeric quantity limit on the benefit (with limit_unit/limit_period). Sparsely populated.
limit_unitstringUnit for limit_quantity (e.g. items/dollars). Sparsely populated.
limit_period_codestringCMS code for the limit period (e.g. per month/quarter/year). Raw code paired with limit_period_label.
limit_period_labelstringHuman label for limit_period_code, e.g. 'Every three months'.
limit_quantity_per_yeardoublelimit_quantity normalized to an annual figure. Sparsely populated.
benefit_amountdoubleThe OTC dollar allowance AS FILED for its period (e.g. 950.0 = $950 'Every three months'). Populated ONLY on otc_items_allowance rows, and there only ~47% (83,457 of 179,479) — null elsewhere. Range $0-$3,900; median ~$57. THE core cost figure.
benefit_amount_kindvarchar(14)Classifies the amount: 'otc_allowance' (179,479 rows = the otc_items_allowance component, may still have null amount) vs 'indicator_only' (324,274 rows = the 3 non-dollar components). Tells you whether a dollar value is even possible for the row.
benefit_amount_period_codestringCMS code for the allowance period. Raw code paired with the label below.
benefit_amount_period_labelstringPeriod the benefit_amount covers: 'Every three months' (most common), 'Every month', 'Every year', 'Every six months', 'Every three years'. Drives annualization.
benefit_amount_annualizeddoublebenefit_amount normalized to a yearly dollar figure (e.g. $950/quarter -> $3,800/yr). Range $0-$5,136. Use THIS to compare allowances across plans on equal footing.
benefit_amount_pmpmdoubleAllowance expressed per-member-per-month (annualized/12, e.g. $3,800/yr -> $316.67). Convenience normalization for cost modeling.
normalization_statusvarchar(36)How the amount was derived: 'normalized' (83,457, has a clean dollar value), 'component_indicator_only' (324,274, no dollar by design), 'not_populated' (90,807, allowance component but bid left it blank), 'offered_no_numeric_allowance' (5,215, offered but unlimited/non-numeric).
normalization_basisvarchar(24)What field the value/indicator was built from, e.g. 'cms_indicator_field'. Provenance of the normalization.
aggregation_policyvarchar(60)How this row should be aggregated, e.g. 'indicator_component_not_financial_cap' — warns that indicator rows must not be summed as if they were dollar caps.
offered_statusvarchar(15)Tri-state offered flag for the component: 'offered' (188,207), 'not_offered' (155,354), 'offered_unknown' (160,192, offered flag indeterminate in the bid). Key column for 'does this plan have OTC'.
numeric_amount_observabilityvarchar(31)Why a dollar amount is/ isn't present: 'all_offered_numeric' (83,457, clean dollars), 'offered_without_numeric_amounts' (264,942, offered but vendor/in-kind so no $), 'not_offered' (155,354).
null_amount_reasonvarchar(30)Explains a null benefit_amount: 'in_kind_or_vendor_administered' (250,819 — OTC run through a vendor card, no filed $), 'not_offered' (155,354), 'not_null_numeric_amount' (83,457 — i.e. amount IS present), 'unlimited' (5,215), 'unknown_requires_crosswalk' (8,908). A null amount is NOT missing data — read this column.
annualization_policyvarchar(17)Rule applied to annualize, e.g. 'not_numeric' for indicator rows. Governs benefit_amount_annualized.
combined_limit_policyvarchar(29)Whether this benefit shares a limit with another, e.g. 'none_detected'. Provenance/aggregation guard.
consumer_document_reconciliation_statusvarchar(11)Always 'not_checked' — placeholder; these PBP values have not been cross-checked against consumer SB/EOC documents. Degenerate single-value column.
confidence_tiervarchar(23)Always 'source_faithful' — every row is a faithful copy of the CMS bid (no inference). Degenerate single-value column.
cost_share_populated_in_bidbooleanBoolean: did the bid populate a cost-share block for this benefit. Mostly false for OTC (allowance-based, $0 cost-share).
otc_amount_sourcestring
otc_base_offeredbooleanPlan-level boolean: does the plan offer a base OTC benefit at all (distinct from the per-component otc_offered). Yes 261,661 / No 217,423 / null 24,669.
otc_base_offered_codestringRaw CMS code behind otc_base_offered ('1'=Yes,'2'=No).
otc_base_offered_labelvarchar(3)Human label for otc_base_offered ('Yes'/'No').
otc_benefit_familyvarchar(16)Which family this row's component belongs to: 'otc_list' (list_coverage), 'otc_items' (items_allowance), 'otc_drug_subitem' (NRT + naloxone). Mirrors otc_sub_benefit at a coarser level.
otc_benefit_type_codestringCMS code for the OTC benefit type. Populated where the component is offered (~88,673 per applicable component).
otc_benefit_type_labelvarchar(10)Human label for otc_benefit_type_code.
otc_cap_carry_forwardbooleanBoolean: does an unused OTC allowance carry forward to the next period. Populated mainly on items_allowance rows.
otc_cap_carry_forward_codestringRaw CMS code for carry-forward.
otc_cap_carry_forward_labelstringHuman label for carry-forward (Yes/No).
otc_cap_presentbooleanBoolean: is there a spending cap on the OTC allowance. Populated on otc_items_allowance rows (88,673); null for the indicator-only components.
otc_cap_present_codestringRaw CMS code for cap presence.
otc_cap_present_labelstringHuman label for cap presence (Yes/No).
otc_cap_sourcestringWhere the cap value/flag was sourced from. Sparsely populated.
otc_coinsurance_pct_maxdouble
otc_component_detail_statusvarchar(23)Describes the detail level captured for the component, e.g. 'list_coverage_indicator'. Metadata about completeness of the component row.
otc_copay_amount_maxdoubleUpper bound of the OTC item copay range. When populated, almost always equals the min and is $0 (36,994 rows at 0/0); range exists for the rare plan with varying item copays.
otc_copay_amount_mindoubleLower bound of the OTC item copay range (mostly $0). Pair with _max; equal min=max means a single flat copay.
otc_delivery_mode_codestringCMS code for OTC delivery mode (e.g. mail/retail/catalog). Very sparsely populated (~4,244 rows).
otc_delivery_mode_labelstringHuman label for delivery mode.
otc_in_package_aggregate_maxboolean
otc_list_coveredbooleanBoolean specific to the otc_list_coverage component: is the OTC item list covered. Populated only on otc_list_coverage rows (88,673); null (415,080) on all other components by design.
otc_list_covered_codestringRaw CMS code for list-covered.
otc_list_covered_labelstringHuman label for list-covered (Yes/No); null on non-list rows.
otc_list_sourcestringSource of the covered OTC item list. Sparsely populated.
otc_naloxone_indicatorbooleanBoolean specific to the otc_naloxone component: is OTC naloxone offered. Populated only on otc_naloxone rows (8,711), and that sub-benefit exists ONLY in report_year 2025-2026. Null (495,042) elsewhere.
otc_naloxone_indicator_codestringRaw CMS code for naloxone indicator.
otc_naloxone_indicator_labelstringHuman label for naloxone indicator (Yes/No).
otc_nrt_attestation_presentbooleanBoolean: is an NRT (nicotine replacement therapy) attestation present in the bid. Tied to the NRT component.
otc_nrt_attestation_rawstringRaw NRT attestation text from the bid. Sparsely populated.
otc_nrt_indicatorbooleanBoolean specific to the otc_nicotine_replacement_therapy component: is OTC NRT offered. Populated only on NRT rows (75,606); the NRT sub-benefit exists only report_year 2019+. Null (428,147) elsewhere.
otc_nrt_indicator_codestringRaw CMS code for NRT indicator.
otc_nrt_indicator_labelstringHuman label for NRT indicator (Yes/No).
otc_offeredbooleanRow/component-level boolean: is THIS specific OTC component offered (distinct from plan-level otc_base_offered). Fill varies by component (e.g. 170,571 on items_allowance, 75,606 on NRT). Pairs with otc_offered_code/label and offered_status.
otc_offered_codestringRaw CMS code for otc_offered.
otc_offered_labelvarchar(3)Human label for otc_offered ('Yes' 188,207 / 'No' 155,354 / null 160,192).
otc_package_aggregate_amountdouble
otc_package_aggregate_annualizeddouble
otc_package_aggregate_category_codesstring
otc_package_aggregate_period_codestring
otc_package_aggregate_period_labelstring
otc_package_idstring
otc_package_match_basisstring
otc_package_namestring
otc_package_type_codestring
otc_package_type_labelstring
otc_pooled_group_amountdouble
otc_pooled_group_category_codesstring
otc_pooled_group_countbigint
otc_pooled_group_indexint
otc_pooled_group_namestring
otc_pooled_group_period_codestring
otc_pooled_group_period_labelstring
otc_pooled_withstring
otc_section_d_category_codesstring
otc_section_d_group_indexint
otc_section_d_group_namestring
otc_sub_benefitstringTHE component dimension and part of grain. 4 values: 'otc_list_coverage' & 'otc_items_allowance' (all years), 'otc_nicotine_replacement_therapy' (2019+), 'otc_naloxone' (2025-2026 only). Each value drives which indicator columns are populated; only otc_items_allowance carries a dollar amount.
otc_targetingvarchar(36)
source_amount_columnstringName of the upstream silver/PBP column the benefit_amount was read from. Lineage; null on indicator rows.
source_period_columnstringName of the upstream column the period was read from. Lineage.
source_pbp_sectionstringWhich PBP section the row came from: 'B13B' (487,753 = modern format) or 'B13C' (16,000 = older 2010-era OTC layout). A format/version marker, not a benefit difference.
source_silver_tablevarchar(59)Always 'medicare_silver.pbp_section_b13_other_services' — the single upstream silver table feeding this view.
lmi_authoritiesstringIntended to hold LMI (low-income / authorities) tagging, but EMPTY ('') for every row in this view. Carries no information here.
source_urlstringCMS download URL of the PBP PUF zip the row came from (31 distinct = 31 release files, one per report_year/quarter snapshot). Provenance.
sha256_content_hashstringContent hash of the source file (31 distinct, 1:1 with source_url). Provenance/integrity, not a row key.

Query it

describe_data_source(source_fqn="medicare_semantic.vw_pbp_otc")     # the card, live
query_substrate(sql="SELECT * FROM medicare_semantic.vw_pbp_otc WHERE contract_id = '<value>' AND plan_id = '<value>' AND segment_id = '<value>' AND report_year = '<value>' AND quarter = '<value>' AND otc_sub_benefit = '<value>' LIMIT 100")
ItemValue
Typed tools that read itget_pbp_benefit
Identity keycontract_id,plan_id,segment_id
Joinhow_to_join(source_fqn="medicare_semantic.vw_pbp_otc")
Raw doorAthena (read-only via query_substrate): medicare_semantic.vw_pbp_otc
NoteCleaned PBP reasoning surface (categories/otc/contract.yaml); raw is pbp_section_*.

This page as markdown: /docs-md/data/medicare_semantic_vw_pbp_otc.md · every page in one file: /llms-full.txt