# MA pbp section b9 outpat hosp

`medicare_silver.pbp_section_b9_outpat_hosp` · Medicare Advantage, Part D, Medicaid · status partial

Section B9: Outpatient hospital cost-share. Coverage: All years.

**Grain.** One row per Medicare Advantage plan segment per CMS quarterly PBP snapshot — i.e. one row per (contract_id, plan_id, segment_id, report_year, quarter). Verified: that 5-column key yields 196,319 distinct combinations = the exact row count. It carries that plan's member cost-sharing (copays, coinsurance, deductibles, OOP caps, prior-auth/referral flags) for outpatient hospital services (PBP Section B9, sub-sections a/b/c/d) as filed in that quarter's release.

**How to use it.** Look up a Medicare Advantage plan's outpatient-hospital member cost-sharing for a given year, using the latest snapshot of that year. Example: SELECT contract_id, plan_id, segment_id, pbp_b9a_copay_ohs_amt_min, pbp_b9a_copay_ohs_amt_max, pbp_b9a_coins_pct_mc FROM medicare_silver.pbp_section_b9_outpat_hosp WHERE report_year=2026 AND quarter=1 AND contract_id='H0028' AND plan_id='004' — answers 'what does this MA plan charge members (copay range / coinsurance %) for outpatient hospital services in 2026?'. To compare across all plans, add the quarter filter to avoid double-counting the same plan-year's multiple quarterly releases.

**Traps.**
- GRAIN TRAP: the profiler reports a 12-column 'verified unique' superset that includes pbp_b9_maxplan_yesno_pos, pbp_b9b/c/d_maxplan_yesno_pos. Those four columns (and many *_pos / *_yesno / base pbp_b9_* columns) are 100% NULL, so they add zero distinctness. The TRUE minimal grain is just contract_id × plan_id × segment_id × report_year × quarter = 196,319 distinct = the full row count. (effective_period_start can substitute for report_year, and bid_id for contract||plan||segment, but neither adds a dimension.)
- NO is_current / no dedup flag. The table is an append of EVERY CMS quarterly PBP release across 21 plan years. The same plan-year appears in up to 4 quarterly snapshots and the benefit VALUES can differ between them (carriers re-file: version increments, copays change). To get 'the' benefits for a plan year you MUST pick one snapshot — filter to a single (report_year, quarter), normally the latest quarter present for that year (check the ry/quarter inventory: e.g. 2021/2024 have no Q2, 2020 has no Q3, 2025/2026 are Q1-only).
- quarter is a SNAPSHOT/vintage axis, not an effective-date axis. All quarters of a report_year share effective_period 2026-01-01..12-31 (etc.). Do not read quarter as 'benefits effective in Q2'; read it as 'the Q2 re-release of that plan year's filing'. cms_vintage_sequence is a messy proxy for it and is NOT 1:1 with quarter — trust (report_year, quarter).
- Mostly-NULL columns carry meaning, three ways: (1) ~9,400 rows have ALL B9 sub-section yn NULL = the plan did not file B9 cost-sharing that snapshot (342 of those have pbp_a_ben_cov NULL = no benefit record at all). (2) Within a filed row, a NULL copay/coins amount means that cost-share type doesn't apply (read the matching _yn = '2'/No first). (3) _yn codes are CMS 1=Yes / 2=No (NOT 0/1) — a NULL _yn is 'not applicable', distinct from '2'=explicitly No.
- Sub-sections a/b/c/d are NOT independent benefits you pick one of — virtually every plan files all four (B9a outpatient-hospital with obs/ohs split, B9b flat-copay tier, B9c tiered group/individual sessions via mcgs/mcis intervals, B9d plan-described benefit). Within B9a, _obs (Observation Services) and _ohs (Outpatient Hospital Services) are SEPARATE cost-shares (different copays) — don't collapse them. This is plan COST-SHARING (what the member pays), not a provider payment/price benchmark; for actual Medicare facility prices use medicare_silver.medicare_opps_rates.
- version is the CMS bid version (0-27), not a vintage/dedup key — never use it to dedup. Likewise asserted_at is Vlada's load date (2026-05-16/20 here), not the CMS publish date; cms_vintage_sequence/parse_revision/parser_artifact_hash/sha256_content_hash/source_url are all ingestion provenance, not benefit content.

| column | type | meaning |
|---|---|---|
| contract_id | string | CMS Medicare Advantage contract ID (e.g. 'H0022'). Prefix encodes plan org: H = MA/MA-PD local (1,820 contracts, 98% of rows), R = regional PPO (37), E = employer/EGWP (2). Part of the grain. 1,859 distinct overall. |
| plan_id | string | Plan number within the contract (e.g. '001', '004'), zero-padded 3-char string. NOT unique on its own (656 distinct values, reused across contracts). Part of the grain. |
| segment_id | string | Service-area segment within a plan (mostly '0'; 27 distinct). Splits a plan into geographic segments that can carry different cost-sharing. Part of the grain — same contract+plan can have multiple segments. |
| version | double | CMS PBP bid-submission version number for that plan (double, 0-27; most rows 1-6). Increments as the carrier re-files the bid; can change between quarterly snapshots of the SAME plan year (e.g. a 2023 plan shows up to 3 versions across its 4 quarterly releases). It is provenance/refile-tracking, NOT a row-dedup or grain field. |
| report_year | double | Plan/contract year the benefits apply to (double, 2006-2026). Equals year(effective_period_start) exactly. Part of the minimal grain (the snapshot year dimension). |
| quarter | double | CMS quarterly release within report_year (double: 1,2,3,4). Part of the minimal grain. Early years (2006-2017) have ONLY quarter 1 (annual release); 2018+ have multiple quarters as CMS began re-releasing PBP data quarterly. This is a snapshot/vintage axis, not an effective-date axis — all quarters of a year share the same effective_period. |
| bid_id | string | Derived composite = contract_id // '_' // plan_id // '_' // segment_id (verified: 0 rows deviate). 23,191 distinct. Redundant convenience key, NOT independent — do not treat as an extra grain dimension. |
| orgtype | string | CMS organization type code (string): '01' = local CCP/HMO-PPO (88% of rows), '08','04','11','06','05','02','14','03'. Indicates the MA organization structure. |
| pbp_a_ben_cov | string | Section-level benefit-coverage flag carried from PBP Section A: '1' = plan offers/records this benefit area (191,967 rows), '2' = does not (4,010), NULL = no Part A/B record at all (342). Caveat: not a clean gate — some '2' rows still have B9a cost-share populated. |
| pbp_a_plan_type | string | CMS plan-type code (string, ~23 values): '01' (HMO, 50%), '04' (local PPO, 28%), '02' (HMO-POS), '20' (MSA/PFFS-ish), '09' (regional PPO), '31','18','48' etc. Describes the MA product type. |
| pbp_b9a_auth | string |  |
| pbp_b9a_auth_obs_yn | string |  |
| pbp_b9a_auth_ohs_yn | string |  |
| pbp_b9a_auth_yn | string |  |
| pbp_b9a_coins_cov_mc | string |  |
| pbp_b9a_coins_ehc | string |  |
| pbp_b9a_coins_obs_pct_max | double |  |
| pbp_b9a_coins_obs_pct_min | double |  |
| pbp_b9a_coins_ohs_pct_max | double |  |
| pbp_b9a_coins_ohs_pct_min | double |  |
| pbp_b9a_coins_pct_mc | double |  |
| pbp_b9a_coins_pct_mcmax | double |  |
| pbp_b9a_coins_yn | string |  |
| pbp_b9a_copay_ehc | string |  |
| pbp_b9a_copay_mc_amt | double |  |
| pbp_b9a_copay_mc_amt_max | double |  |
| pbp_b9a_copay_obs_amt_max | double |  |
| pbp_b9a_copay_obs_amt_min | double |  |
| pbp_b9a_copay_obs_per | string |  |
| pbp_b9a_copay_obs_per_d | string |  |
| pbp_b9a_copay_ohs_amt_max | double |  |
| pbp_b9a_copay_ohs_amt_min | double |  |
| pbp_b9a_copay_yn | string |  |
| pbp_b9a_ded_amt | double |  |
| pbp_b9a_ded_ehc | string |  |
| pbp_b9a_ded_obs_amt | double |  |
| pbp_b9a_ded_ohs_amt | double |  |
| pbp_b9a_ded_yn | string |  |
| pbp_b9a_maxenr_amt | double |  |
| pbp_b9a_maxenr_ehc | string |  |
| pbp_b9a_maxenr_obs_amt | double |  |
| pbp_b9a_maxenr_obs_per | string |  |
| pbp_b9a_maxenr_obs_per_d | string |  |
| pbp_b9a_maxenr_ohs_amt | double |  |
| pbp_b9a_maxenr_ohs_per | string |  |
| pbp_b9a_maxenr_ohs_per_d | string |  |
| pbp_b9a_maxenr_per | string |  |
| pbp_b9a_maxenr_yn | string |  |
| pbp_b9a_refer_obs_yn | string |  |
| pbp_b9a_refer_ohs_yn | string |  |
| pbp_b9a_refer_yn | string |  |
| pbp_b9a_sep_cost_yn | string |  |
| pbp_b9b_auth | string |  |
| pbp_b9b_auth_yn | string |  |
| pbp_b9b_coins_cov_mc | string |  |
| pbp_b9b_coins_pct_mc | double |  |
| pbp_b9b_coins_pct_mcmax | double |  |
| pbp_b9b_coins_yn | string |  |
| pbp_b9b_copay_mc_amt | double |  |
| pbp_b9b_copay_mc_amt_max | double |  |
| pbp_b9b_copay_yn | string |  |
| pbp_b9b_ded_amt | double |  |
| pbp_b9b_ded_yn | string |  |
| pbp_b9b_maxenr_amt | double |  |
| pbp_b9b_maxenr_per | string |  |
| pbp_b9b_maxenr_per_d | string |  |
| pbp_b9b_maxenr_type | string |  |
| pbp_b9b_maxenr_yn | string |  |
| pbp_b9b_refer_yn | string |  |
| pbp_b9b_sep_cost_yn | string |  |
| pbp_b9c_auth | string |  |
| pbp_b9c_auth_yn | string |  |
| pbp_b9c_coins_ehc | string |  |
| pbp_b9c_coins_mcgs_bgns_ntrvl1 | double |  |
| pbp_b9c_coins_mcgs_bgns_ntrvl2 | double |  |
| pbp_b9c_coins_mcgs_bgns_ntrvl3 | double |  |
| pbp_b9c_coins_mcgs_cov | string |  |
| pbp_b9c_coins_mcgs_ends_ntrvl1 | double |  |
| pbp_b9c_coins_mcgs_ends_ntrvl2 | double |  |
| pbp_b9c_coins_mcgs_ends_ntrvl3 | double |  |
| pbp_b9c_coins_mcgs_intrvl_num | string |  |
| pbp_b9c_coins_mcgs_maxpct | double |  |
| pbp_b9c_coins_mcgs_minpct | double |  |
| pbp_b9c_coins_mcgs_pct_intrvl1 | double |  |
| pbp_b9c_coins_mcgs_pct_intrvl2 | double |  |
| pbp_b9c_coins_mcgs_pct_intrvl3 | double |  |
| pbp_b9c_coins_mcis_bgns_ntrvl1 | double |  |
| pbp_b9c_coins_mcis_bgns_ntrvl2 | double |  |
| pbp_b9c_coins_mcis_bgns_ntrvl3 | double |  |
| pbp_b9c_coins_mcis_cov | string |  |
| pbp_b9c_coins_mcis_ends_ntrvl1 | double |  |
| pbp_b9c_coins_mcis_ends_ntrvl2 | double |  |
| pbp_b9c_coins_mcis_ends_ntrvl3 | double |  |
| pbp_b9c_coins_mcis_intrvl_num | string |  |
| pbp_b9c_coins_mcis_maxpct | double |  |
| pbp_b9c_coins_mcis_minpct | double |  |
| pbp_b9c_coins_mcis_pct_intrvl1 | double |  |
| pbp_b9c_coins_mcis_pct_intrvl2 | double |  |
| pbp_b9c_coins_mcis_pct_intrvl3 | double |  |
| pbp_b9c_coins_yn | string |  |
| pbp_b9c_copay_ehc | string |  |
| pbp_b9c_copay_mcgs_amt_intrvl1 | double |  |
| pbp_b9c_copay_mcgs_amt_intrvl2 | double |  |
| pbp_b9c_copay_mcgs_amt_intrvl3 | double |  |
| pbp_b9c_copay_mcgs_bgns_ntrvl1 | double |  |
| pbp_b9c_copay_mcgs_bgns_ntrvl2 | double |  |
| pbp_b9c_copay_mcgs_bgns_ntrvl3 | double |  |
| pbp_b9c_copay_mcgs_ends_ntrvl1 | double |  |
| pbp_b9c_copay_mcgs_ends_ntrvl2 | double |  |
| pbp_b9c_copay_mcgs_ends_ntrvl3 | double |  |
| pbp_b9c_copay_mcgs_intrvl_num | string |  |
| pbp_b9c_copay_mcgs_maxamt | double |  |
| pbp_b9c_copay_mcgs_minamt | double |  |
| pbp_b9c_copay_mcis_amt_intrvl1 | double |  |
| pbp_b9c_copay_mcis_amt_intrvl2 | double |  |
| pbp_b9c_copay_mcis_amt_intrvl3 | double |  |
| pbp_b9c_copay_mcis_bgns_ntrvl1 | double |  |
| pbp_b9c_copay_mcis_bgns_ntrvl2 | double |  |
| pbp_b9c_copay_mcis_bgns_ntrvl3 | double |  |
| pbp_b9c_copay_mcis_ends_ntrvl1 | double |  |
| pbp_b9c_copay_mcis_ends_ntrvl2 | double |  |
| pbp_b9c_copay_mcis_ends_ntrvl3 | double |  |
| pbp_b9c_copay_mcis_intrvl_num | string |  |
| pbp_b9c_copay_mcis_maxamt | double |  |
| pbp_b9c_copay_mcis_minamt | double |  |
| pbp_b9c_copay_yn | string |  |
| pbp_b9c_ded_amt | double |  |
| pbp_b9c_ded_yn | string |  |
| pbp_b9c_maxenr_amt | double |  |
| pbp_b9c_maxenr_per | string |  |
| pbp_b9c_maxenr_per_d | string |  |
| pbp_b9c_maxenr_type | string |  |
| pbp_b9c_maxenr_yn | string |  |
| pbp_b9c_refer_yn | string |  |
| pbp_b9c_sep_cost_yn | string |  |
| pbp_b9d_auth | string |  |
| pbp_b9d_auth_yn | string |  |
| pbp_b9d_bendec | string |  |
| pbp_b9d_bendesc_amo | string |  |
| pbp_b9d_bendesc_yn | string |  |
| pbp_b9d_coins_cov_mc | string |  |
| pbp_b9d_coins_pct_mc | double |  |
| pbp_b9d_coins_pct_mc_max | double |  |
| pbp_b9d_coins_pct_mc_min | double |  |
| pbp_b9d_coins_pct_mcmax | double |  |
| pbp_b9d_coins_yn | string |  |
| pbp_b9d_copay_mc_amt | double |  |
| pbp_b9d_copay_mc_amt_max | double |  |
| pbp_b9d_copay_mc_amt_min | double |  |
| pbp_b9d_copay_yn | string |  |
| pbp_b9d_ded_amt | double |  |
| pbp_b9d_ded_waive_3pt_yn | string |  |
| pbp_b9d_ded_yn | string |  |
| pbp_b9d_maxenr_amt | double |  |
| pbp_b9d_maxenr_per | string |  |
| pbp_b9d_maxenr_per_d | string |  |
| pbp_b9d_maxenr_type | string |  |
| pbp_b9d_maxenr_yn | string |  |
| pbp_b9d_refer_yn | string |  |
| pbp_b9d_sep_cost_yn | string |  |
| observed_period | date | Date = Jan 1 of report_year (the plan year being observed). 1:1 with report_year/effective_period_start. Vlada temporal-spine column. |
| asserted_at | date | Date the row was loaded/asserted into the silver mart (Vlada ingestion timestamp, e.g. 2026-05-16 / 2026-05-20). A load-batch marker, NOT a CMS publication date. Roughly clusters by cms_vintage_sequence. |
| effective_period_start | date |  |
| effective_period_end | date |  |
| cms_vintage_sequence | int | Integer vintage rank of the CMS source release within its year (1-4). Loosely tracks quarter but NOT a clean function of it (e.g. 2021 Q1 = vintage 4, 2022 Q1/Q2 = vintage 2) because CMS didn't release every quarter every year. Use (report_year, quarter) as the authoritative snapshot key, not this. |
| parse_revision | int | Vlada parser revision integer (1 for vintage-1 loads, 2 for vintages 2-4). Ingestion provenance, not a CMS field. |
| parser_artifact_hash | string | SHA-256 of the parser code/config artifact that produced the row. Provenance for reproducibility. |
| source_url | string | CMS download URL of the source PBP zip (e.g. https://www.cms.gov/files/zip/pbp-benefits-2020-quarter-4.zip). 35 distinct = exactly one per (report_year, quarter) snapshot. |
| sha256_content_hash | string | SHA-256 of the source content the row was parsed from. One hash per snapshot file (no snapshot has >1 hash). Provenance/integrity. |
| pbp_b9_auth | string |  |
| pbp_b9_auth_pos | string |  |
| pbp_b9_coins_cov_mc | string |  |
| pbp_b9_coins_cov_pos | string |  |
| pbp_b9_coins_yesno | string |  |
| pbp_b9_coins_yesno_pos | string |  |
| pbp_b9_copay_yesno | string |  |
| pbp_b9_copay_yesno_pos | string |  |
| pbp_b9_ded_yesno | string |  |
| pbp_b9_ded_yesno_pos | string |  |
| pbp_b9_maxenr_per | string |  |
| pbp_b9_maxenr_yesno | string |  |
| pbp_b9_maxenr_yn_pos | string |  |
| pbp_b9_maxplan_yesno_pos | string |  |
| pbp_b9_maxpln_cov_pos | string |  |
| pbp_b9_maxpln_per_pos | string |  |
| pbp_b9b_auth_pos | string |  |
| pbp_b9b_coins_cov_pos | string |  |
| pbp_b9b_coins_yesno | string |  |
| pbp_b9b_coins_yesno_pos | string |  |
| pbp_b9b_copay_yesno | string |  |
| pbp_b9b_copay_yesno_pos | string |  |
| pbp_b9b_ded_yesno | string |  |
| pbp_b9b_ded_yesno_pos | string |  |
| pbp_b9b_maxenr_type_pos | string |  |
| pbp_b9b_maxenr_yesno | string |  |
| pbp_b9b_maxenr_yn_pos | string |  |
| pbp_b9b_maxplan_yesno_pos | string |  |
| pbp_b9b_maxpln_cov_pos | string |  |
| pbp_b9b_maxpln_per_pos | string |  |
| pbp_b9c_auth_pos | string |  |
| pbp_b9c_coins_cov_mcgrp | string |  |
| pbp_b9c_coins_cov_mcind | string |  |
| pbp_b9c_coins_cov_pos | string |  |
| pbp_b9c_coins_covgrp_pos | string |  |
| pbp_b9c_coins_yesno | string |  |
| pbp_b9c_coins_yesno_pos | string |  |
| pbp_b9c_copay_yesno | string |  |
| pbp_b9c_copay_yesno_pos | string |  |
| pbp_b9c_ded_yesno | string |  |
| pbp_b9c_ded_yesno_pos | string |  |
| pbp_b9c_maxenr_type_pos | string |  |
| pbp_b9c_maxenr_yesno | string |  |
| pbp_b9c_maxenr_yn_pos | string |  |
| pbp_b9c_maxplan_yesno_pos | string |  |
| pbp_b9c_maxpln_cov_pos | string |  |
| pbp_b9c_maxpln_per_pos | string |  |
| pbp_b9d_auth_pos | string |  |
| pbp_b9d_coins_cov_pos | string |  |
| pbp_b9d_coins_yesno | string |  |
| pbp_b9d_coins_yesno_pos | string |  |
| pbp_b9d_copay_yesno | string |  |
| pbp_b9d_copay_yesno_pos | string |  |
| pbp_b9d_ded_yesno | string |  |
| pbp_b9d_ded_yesno_pos | string |  |
| pbp_b9d_maxenr_type_pos | string |  |
| pbp_b9d_maxenr_yesno | string |  |
| pbp_b9d_maxenr_yn_pos | string |  |
| pbp_b9d_maxplan_yesno_pos | string |  |
| pbp_b9d_maxpln_cov_pos | string |  |
| pbp_b9d_maxpln_per_pos | string |  |
| pbp_b9a_* |  | B9a = Outpatient Hospital Services cost-sharing (the primary sub-section, filed by ~93% of rows). Has copay/coins/deductible/max-enrollee-OOP/authorization/referral fields, SPLIT into two service flavors via suffixes: _obs = Observation Services (copay $0-$900, median ~$236) and _ohs = Outpatient Hospital Services / surgery (copay $0-$1,400). _yn fields = 1/Yes 2/No; _amt = dollar copay/deductible; _pct_mc = coinsurance % of Medicare-covered (0-30%, median 20%); _amt_min/_amt_max = copay range across services; _mc = Medicare-covered amount. |
| pbp_b9b_* |  | B9b sub-section cost-sharing (filed alongside B9a for essentially all rows). Flat per-service structure: copay_mc_amt $0-$1,000 (median ~$92), coins_pct_mc, ded_amt, maxenr (per-period cap), auth/refer yn. A simpler flat-copay outpatient benefit tier (no obs/ohs split, no intervals). |
| pbp_b9c_* |  | B9c sub-section — TIERED/interval cost-sharing for session-based services (mental-health / partial-hospitalization style). Two streams: mcgs = Medicare-Covered Group Sessions, mcis = Medicare-Covered Individual Sessions; each with up-to-3 visit-count intervals (intrvl1/2/3 with bgns_/ends_ bounds and per-interval copay amt or coins pct). intrvl_num = how many intervals defined (mostly '1'). Plus ded/maxenr/auth/refer. |
| pbp_b9d_* |  | B9d sub-section — a plan-DESCRIBED ('bendesc') outpatient benefit. bendec='1' (yes, has a described benefit, 54% of rows) /'2'; bendesc_yn / bendesc_amo = free-text-driven amount flags; then standard copay_mc_amt (with _min/_max), coins_pct_mc, ded (incl ded_waive_3pt_yn), maxenr, auth/refer. Captures outpatient cost-sharing that doesn't fit the a/b/c templates. |
| pbp_b9_* (base, no sub-letter) and all *_pos / *_yesno columns |  | 100% NULL in this table (verified: pbp_b9_coins_yesno, pbp_b9_*_pos, pbp_b9b/c/d_*_pos, *_yesno all count = 0). These are older/alternate CMS PBP layout fields retained in the unioned silver schema but never populated for B9. They are the reason the profiler's grain looked like a 12-column superset — being uniformly null they add no distinctness. IGNORE for analysis. |
| effective_period_start / effective_period_end |  | Plan-year coverage window: Jan 1 - Dec 31 of report_year (dates). ALL four quarterly snapshots of a given report_year carry the SAME window — quarter is a release vintage, not a different effective period. report_year = year(effective_period_start) exactly. |

Tools that read it: get_pbp_benefit

Query it: `query_substrate` with `SELECT ... FROM medicare_silver.pbp_section_b9_outpat_hosp WHERE ... LIMIT 100`; read `describe_data_source("medicare_silver.pbp_section_b9_outpat_hosp")` first.
