Medicare FFS vs Medicare Advantage vs Commercial Claims Differences
Systematic structural, incentive, and completeness differences among US administrative data by payer type (Medicare fee-for-service claims, Medicare Advantage encounter records, and commercial claims) that change exposure/outcome ascertainment, measured confounders, person-time observability, and the generalizability of real-world evidence.
On this page
Not all claims data are the same — three major US insurance sources (Medicare Fee-for-Service, Medicare Advantage, and commercial insurance) each produce records in a different way, for a different population, and with different gaps. Medicare Fee-for-Service generates a claim every time a provider bills for a covered service, so the record is nearly complete for what was paid.
Medicare Advantage plans instead submit encounter reports to the government for oversight and risk-adjustment purposes, not to get a fee per service, which means some services may never appear in the data. Commercial insurance claims cover working-age adults and depend heavily on which employers contribute data to a given database, so coverage of out-of-network care and specialty drugs can vary widely. Choosing the wrong source — or mixing sources without accounting for their differences — can make a disease look more common than it is, make a treatment look safer than it is, or limit who the study's findings actually apply to.
US administrative data are not one substrate. They are produced by three economically distinct machines — Medicare fee-for-service (FFS) payment claims, Medicare Advantage (MA) encounter records, and commercial payment claims — and the machine that generated a row determines what is captured, how completely, with what coding pressure, and for whom. Treating "claims data" as fungible across payer type is one of the most common and most consequential silent errors in claims-based RWE, because payer type sits upstream of phenotyping, exposure measurement, confounder ascertainment, and person-time observability simultaneously.
Core conceptual distinction
. The three sources differ along two axes that operate independently. (1) Data-generating purpose. FFS records exist because a provider billed for payment, so for covered services they are near-complete for what was reimbursed (adjudicated paid claims, MedPAR for inpatient stays, Part B carrier files, Part D for drugs).
MA records are encounter data — plans are paid a risk-adjusted capitation, so the record is a report of services rendered, submitted to CMS for risk adjustment and oversight rather than to trigger a fee; historically these were less complete and less standardized than FFS claims, with steady improvement under CMS submission requirements. Commercial claims are again payment-driven but assembled from contributing employers/payers (MarketScan, Optum, HealthCore), so completeness and benefit design vary by contributor and out-of-network capture is uneven.
(2) Coding incentive. MA capitation is risk-adjusted on diagnoses (HCC model), creating a strong, well-documented incentive for coding intensity — more complete and sometimes upcoded diagnoses via in-home health risk assessments and retrospective chart review — so MA beneficiaries systematically appear sicker than clinically identical FFS beneficiaries. FFS and commercial coding is driven by reimbursement and quality metrics, not HCC capitation.
The practical consequence: the same patient, same disease, can yield a different diagnosis profile, a different covariate vector, and a different phenotype classification depending solely on which payer's data observed them.
Pros, cons, and trade-offs
.
- Multi-payer (pooled FFS + MA + commercial) vs single-payer (e.g., FFS-only): Pooling buys sample size and a payer mix that mirrors the policy-relevant US population (MA is now roughly half of Medicare enrollment), and it lets you test transportability directly. Cost: you import differential misclassification and coding-intensity confounding; a naive pooled hazard ratio can be biased if one payer's data quality drives the estimate. Prefer multi-payer with explicit payer stratification or interaction, never silent pooling.
- FFS-only vs MA-only: FFS gives the cleanest, most research-validated capture and is the standard benchmark, but it is a shrinking and increasingly selected slice (healthier or differently-selected beneficiaries remain in FFS in some markets). MA-only maximizes contemporary relevance but demands extra completeness validation and coding-intensity handling. Prefer FFS-only when phenotype validity and complete utilization capture dominate; prefer including MA when current-population generalizability dominates and you can validate.
- Harmonized common algorithm vs payer-specific algorithms: A single phenotype (e.g., 1 inpatient or 2 outpatient diagnoses in a fixed window) is simpler and comparable across arms, but its PPV and sensitivity differ by payer because of coding intensity and completeness. Payer-specific tuning is more valid but harder to defend and reduces comparability. Prefer a harmonized algorithm with payer-specific sensitivity analyses and, where linked validation data exist, payer-specific operating characteristics reported transparently.
When NOT to use — and when it is actively misleading or dangerous
.
- Do not pool across payer types without stratification when the outcome or covariates are diagnosis-derived. Coding intensity makes MA comorbidity scores non-comparable to FFS; a pooled propensity score or a pooled comorbidity-adjusted model then conditions on a payer-distorted covariate, and "adjustment" can amplify rather than remove bias. This is the dangerous case.
- Do not assume "no claim = no event" inside MA-only person-time without validating completeness. If MA encounter capture is incomplete for the endpoint (historically true for some utilization measures), absence of a record is missingness, not a true negative, biasing incidence downward differentially by payer.
- Do not transport an FFS-calibrated phenotype, risk model, or cost model to an MA population unchecked. HCC-rich MA data and FFS-calibrated models do not interchange; risk scores are inflated relative to FFS, and shadow-priced MA "costs" are not the same quantity as FFS allowed amounts.
- Do not treat a commercial database as representative of the elderly or disabled, or assume one commercial extract generalizes to another — contributor composition, formularies, and out-of-network capture differ.
Data-source operational depth
.
- Medicare FFS claims (Parts A/B/D): The research-grade benchmark. Inpatient via MedPAR, physician/ outpatient via carrier and outpatient files, drugs via Part D. Failure modes: final-action claims lag several months (cohorts built on incomplete recent data undercount events); HCC capture exists but is not the dominant coding driver; FFS is increasingly selected as beneficiaries move to MA, so an FFS-only estimate may not transport. Workaround: allow claims run-out before locking the cohort; treat FFS as the calibration anchor for completeness checks of other payers.
- Medicare Advantage encounter data (Part C): Plan-submitted encounters, not paid claims. Failure modes: historical and residual incompleteness for some service types; coding intensity (in-home HRAs and chart reviews add diagnoses that never generate an encounter for the corresponding service), inflating comorbidity/HCC profiles and the apparent prevalence of mild disease; MA-only person-time lacks FFS A/B medical claims, so any logic that depends on those payment claims breaks. Do not confuse this medical-claims gap with Part D/PDE pharmacy observability: when Part D coverage and PDE files are available, outpatient drug transactions may be observable for both standalone PDP and MA-PD plans. Workarounds: validate inpatient completeness against MedPAR for the linkable subset; exclude or separately model MA-only person-time when the design relies on FFS medical observability; carry a coding-intensity proxy (HCC count, chart-review/HRA flags where available) and run results with and without it.
- Commercial claims (MarketScan, Optum, HealthCore, etc.): Payment-driven and structurally similar to FFS but for a younger, employed, differently-selected population. Failure modes: out-of-network and carve-out (behavioral health, specialty pharmacy) leakage; short and lumpy enrollment from job changes that truncates washout and follow-up; contributor turnover that creates artifactual cohort entry/exit. Workarounds: require continuous medical + pharmacy enrollment across washout and follow-up; check for contributor-driven enrollment discontinuities; never assume one extract's completeness applies to another.
- Linked / multi-database (Sentinel, PCORnet, claims–EHR–vital-records): Linkage can recover what a single payer misses (death index for censoring, EHR severity for confounding), but introduces a linkable-subset selection and date-reconciliation problems (order vs fill vs service dates), and linkage quality itself can differ by payer. Distributed/OMOP-CDM studies must carry an explicit payer flag and avoid harmonization that silently drops payer granularity.
Worked claims example
. Question: incident heart failure hospitalization comparing initiators of an SGLT2 inhibitor vs a DPP-4 inhibitor among adults ≥66 with type 2 diabetes, in a 100% Medicare sample that contains both FFS and MA beneficiaries. Run the same protocol three ways and compare. (1) Payer assignment at index: from the monthly enrollment/eligibility file, classify each person on the index month as `FFS` (Part A+B, not in a Part C plan) or `MA` (enrolled in a Part C contract).
(2) Cohort A — FFS-only: require continuous Part A/B/D for the 365-day washout (no SGLT2 or DPP-4 fill) through `index_date`; exposure from Part D `fill_date`/`days_supply`; baseline comorbidities from Part A/B diagnoses; first HF hospitalization from MedPAR. (3) Cohort B — FFS + MA pooled: add MA beneficiaries using encounter diagnoses and Part D fills, classifying HF from MA inpatient encounters.
(4) Cohort C — MA-only. Now inspect the three: the measured comorbidity burden (e.g., HCC count, Elixhauser via diagnoses) is higher in MA than FFS for clinically comparable patients — coding intensity, not true case-mix — so a pooled propensity score conditions on a payer-distorted covariate; inpatient HF capture in MA should be benchmarked against MedPAR for any linkable subset, and if MA IP capture is lower, pooled incidence is differentially attenuated; and because washout/"no prior fill" logic requires payment-claim observability, any MA-only person-time that lacks FFS-style claims is unobservable for the washout — excluding it changes N and follow-up versus the pooled cohort.
Deliverable: report the comparative estimate within FFS, within MA, and pooled with a payer × treatment interaction; report the MedPAR-vs-encounter inpatient completeness ratio; and show how excluding MA-only person-time and adding/removing the coding-intensity proxy move the estimate. Concordant estimates across payers strengthen the inference; divergence localizes the data-quality threat instead of burying it in a pooled number.
Decision diagram
flowchart TD
Pop[Source population] --> Payer{Payer type at index<br/>from enrollment file}
Payer -->|Medicare FFS| FFS[Payment claims A/B/D + MedPAR<br/>near-complete for covered services<br/>research benchmark; HCC not main coding driver]
Payer -->|Medicare Advantage| MA[Plan-submitted encounter data<br/>HCC coding intensity inflates diagnoses<br/>completeness varies; MA-only time lacks FFS claims]
Payer -->|Commercial| Comm[Contributor payment claims<br/>younger/employed; OON + carve-out leakage<br/>job-change enrollment churn]
FFS --> Harm[Common phenotype + covariate definitions]
MA --> Validate[Benchmark inpatient capture vs MedPAR<br/>carry coding-intensity proxy HCC/HRA flag]
Validate --> Harm
Comm --> Harm
Harm --> Analysis[Stratify by payer + payer x exposure interaction<br/>exclude/model MA-only person-time]
Analysis --> Report[Report within-FFS, within-MA, pooled<br/>+ completeness ratio + generalizability]flowchart LR
Enr[Enrollment / eligibility file<br/>monthly payer flag] --> Span{Person-time observability}
Span -->|FFS A/B/D| Obs[FFS A/B medical + Part D observed<br/>channel-specific washout valid after runout]
Span -->|MA-only| Unobs[FFS A/B medical claims absent<br/>medical-event washout unobservable without encounters]
Obs --> Cohort[Include in claims-dependent cohort]
Unobs --> Decide{Design relies on FFS-style claims?}
Decide -->|Yes| Drop[Exclude MA-only person-time<br/>re-derive N and follow-up]
Decide -->|No, use encounter endpoints| Bench[Validate MA capture vs MedPAR<br/>then include with caveats]Worked example
Scenario
You are studying whether a new diabetes medication reduces hospitalizations for heart failure. Your dataset is a large Medicare sample that contains both Fee-for-Service and Medicare Advantage enrollees. Before running any analysis, you need to understand how the three major claims sources differ in who they cover, how complete their records are, and what biases each introduces. The table below walks through four key dimensions — and then the steps explain what each difference means for your study.
Dataset
Comparison of Medicare FFS, Medicare Advantage, and Commercial claims across four study-design dimensions
| Dimension | Medicare Fee-for-Service (FFS) | Medicare Advantage (MA) | Commercial |
|---|---|---|---|
| Typical enrollee age and population | 65+ adults; increasingly those who stayed in traditional Medicare (some evidence of selection) | 65+ adults; now roughly half of all Medicare enrollees; skews toward certain regions and plans | Under-65 working-age adults and their families; employer-sponsored coverage |
| How complete are the claims? | Near-complete for services the government covers — every paid claim is recorded | Variable; encounter records are submitted by plans but historically under-capture some service types compared to FFS | Generally complete for in-network care; out-of-network visits and carved-out benefits (e.g., behavioral health) may be missing |
| Capitation or encounter gap risk | No capitation encounter gap — each covered billed service generates a paid/adjudicated claim after runout; absence means no covered billed service was observed, not proof no care occurred anywhere | Real gap — the plan is paid a flat monthly rate, so not every service triggers a new submission; a missing record could mean the service did not happen OR that it was not submitted | Low gap risk for in-network; out-of-network services paid by the enrollee may never appear in the database |
| Who does the study generalize to? | Traditional Medicare population — valid benchmark but shrinking as more people choose MA; results may not apply to MA enrollees | Contemporary Medicare population overall, but coded diagnoses appear more numerous than in FFS for clinically similar patients due to coding intensity | Working-age insured adults; findings do not generalize to elderly Medicare beneficiaries or uninsured populations |
Steps
Result
Source choice determines who you study, how much you trust the absence of a record, and whether your measure of patient health status is comparable across groups. FFS is the most research-validated source and the right choice when you need clean utilization records and reliable washout logic. Including MA expands the study to the contemporary Medicare population but requires you to validate how complete the MA encounter records are and to account for coding intensity so that inflated diagnosis counts do not distort your results. Commercial claims are the right source for working-age adults but should not be used to draw conclusions about elderly Medicare patients. Mixing sources silently — without acknowledging these differences — is one of the most common ways a claims study can produce misleading findings.
Trade-offs
Runnable example
Assign payer type at index and flag FFS-observable person-time from claims-style enrollment data, then quantify MA coding intensity and benchmark MA inpatient capture against an FFS (MedPAR-style) reference.
import pandas as pd
def assign_payer_at_index(enroll: pd.DataFrame, cohort: pd.DataFrame) -> pd.DataFrame:
"""payer_at_index = MA if in a Part C contract the index month, else FFS (needs A+B)."""
c = cohort.copy()
c["index_month"] = c["index_date"].dt.to_period("M")
e = enroll.merge(c[["person_id", "index_month"]],
left_on=["person_id", "month"],
right_on=["person_id", "index_month"], how="inner")
e["payer_at_index"] = e["part_c"].map({True: "MA", False: "FFS"})
# FFS classification additionally requires fee-for-service Part A and Part B at index.
e.loc[(e["payer_at_index"] == "FFS") & ~(e["part_a"] & e["part_b"]),
"payer_at_index"] = "UNCLASSIFIED"
return c.merge(e[["person_id", "payer_at_index"]], on="person_id", how="left")
def flag_ffs_observable(enroll: pd.DataFrame, cohort: pd.DataFrame,
washout_days: int = 365) -> pd.Series:
"""True only if EVERY month in [index-washout, index] is FFS with A+B+D (no MA-only gap).
This gates FFS A/B medical observability; outpatient drug observability should be checked against Part D/PDE."""
c = cohort.copy()
c["start_month"] = (c["index_date"] - pd.Timedelta(days=washout_days)).dt.to_period("M")
c["index_month"] = c["index_date"].dt.to_period("M")
e = enroll.merge(c[["person_id", "start_month", "index_month"]], on="person_id", how="inner")
in_window = (e["month"] >= e["start_month"]) & (e["month"] <= e["index_month"])
e = e[in_window].copy()
e["ffs_month"] = (~e["part_c"]) & e["part_a"] & e["part_b"] & e["part_d"]
# observable only if no month in the window is MA-only / lacks full FFS coverage
return e.groupby("person_id")["ffs_month"].all()
def coding_intensity_proxy(dx: pd.DataFrame, cohort: pd.DataFrame,
baseline_days: int = 365) -> pd.DataFrame:
"""Distinct HCC count in the baseline window = coding-intensity proxy; compare by payer.
Higher mean HCC count in MA vs FFS for comparable patients signals coding intensity, not case-mix."""
d = dx.merge(cohort[["person_id", "index_date", "payer_at_index"]], on="person_id")
d = d[(d["dx_date"] <= d["index_date"]) &
(d["dx_date"] >= d["index_date"] - pd.Timedelta(days=baseline_days))]
hcc = (d.groupby(["person_id", "payer_at_index"])["hcc"]
.nunique().rename("hcc_count").reset_index())
return hcc
def ma_inpatient_completeness(ip: pd.DataFrame) -> float:
"""MedPAR-vs-encounter completeness ratio for the linkable subset (target ~1.0).
Ratio < 1 means MA encounter under-captures inpatient stays -> endpoints biased low."""
ma = (ip["source"] == "MA_ENCOUNTER").sum()
ffs = (ip["source"] == "FFS_MEDPAR").sum()
return ma / ffs if ffs else float("nan")R/data.table mirror: assign payer at index, flag FFS-observable medical washout person-time, compute the HCC-count coding-intensity proxy by payer, and compute the MA-vs-MedPAR inpatient completeness ratio.
library(data.table)
assign_payer_at_index <- function(enroll, cohort) {
setDT(enroll); setDT(cohort)
cohort[, index_month := format(index_date, "%Y-%m")]
e <- merge(enroll, cohort[, .(person_id, index_month)],
by.x = c("person_id", "month"), by.y = c("person_id", "index_month"))
e[, payer_at_index := fifelse(part_c, "MA",
fifelse(part_a & part_b, "FFS", "UNCLASSIFIED"))]
merge(cohort, e[, .(person_id, payer_at_index)], by = "person_id", all.x = TRUE)
}
flag_ffs_observable <- function(enroll, cohort, washout_days = 365L) {
# TRUE only if every month in the washout window is FFS with A+B+D (no MA-only gap).
# This gates FFS A/B medical observability; Part D/PDE pharmacy observability is separate.
setDT(enroll); setDT(cohort)
cw <- cohort[, .(person_id,
start_m = format(index_date - washout_days, "%Y-%m"),
index_m = format(index_date, "%Y-%m"))]
e <- merge(enroll, cw, by = "person_id")
e <- e[month >= start_m & month <= index_m]
e[, ffs_month := (!part_c) & part_a & part_b & part_d]
e[, .(ffs_observable = all(ffs_month)), by = person_id]
}
coding_intensity_proxy <- function(dx, cohort, baseline_days = 365L) {
setDT(dx)
d <- merge(dx, cohort[, .(person_id, index_date, payer_at_index)], by = "person_id")
d <- d[dx_date <= index_date & dx_date >= index_date - baseline_days]
d[, .(hcc_count = uniqueN(hcc)), by = .(person_id, payer_at_index)]
}
ma_inpatient_completeness <- function(ip) {
setDT(ip)
ma <- ip[source == "MA_ENCOUNTER", .N]
ffs <- ip[source == "FFS_MEDPAR", .N]
if (ffs == 0L) NA_real_ else ma / ffs
}SAS/PROC SQL data-construction for payer-aware claims work (no estimation procedures here; this is cohort/data-quality preparation). Required input datasets (post data-management): work.enroll : person_id, month (YYYYMM num), part_c (0/1), part_a, part_b, part_d (0/1) work.cohort : person_id, index_date (SAS date)...
%let washout = 365;
/* Payer type at index: MA if enrolled in a Part C contract that month; else FFS needs A+B. */
proc sql;
create table payer as
select c.person_id, c.index_date,
case when e.part_c = 1 then 'MA'
when e.part_a = 1 and e.part_b = 1 then 'FFS'
else 'UNCLASSIFIED' end as payer_at_index length=12
from work.cohort c
left join work.enroll e
on c.person_id = e.person_id
and e.month = input(put(c.index_date, yymmn6.), 6.);
quit;
/* FFS-observable medical washout: every month in [index-washout, index] is FFS with A+B+D (no MA-only gap).
This gates FFS A/B medical observability; outpatient drug observability should be checked against Part D/PDE. */
proc sql;
create table observable as
select c.person_id,
( sum( case when (e.part_c=0 and e.part_a=1 and e.part_b=1 and e.part_d=1)
then 0 else 1 end ) = 0 ) as ffs_observable
from work.cohort c
join work.enroll e
on c.person_id = e.person_id
and e.month between input(put(c.index_date - &washout, yymmn6.), 6.)
and input(put(c.index_date, yymmn6.), 6.)
group by c.person_id;
quit;
/* Coding-intensity proxy: distinct HCC count in the baseline window, by payer.
Higher MA mean vs FFS for comparable patients signals coding intensity, not true case-mix. */
proc sql;
create table hcc_proxy as
select p.payer_at_index, d.person_id,
count(distinct d.hcc) as hcc_count
from payer p
join work.dx d
on p.person_id = d.person_id
and d.dx_date <= p.index_date
and d.dx_date >= p.index_date - &washout
group by p.payer_at_index, d.person_id;
create table hcc_by_payer as
select payer_at_index, mean(hcc_count) as mean_hcc, count(*) as n
from hcc_proxy group by payer_at_index;
quit;
/* MA-vs-MedPAR inpatient completeness ratio for the linkable subset (target ~1.0). */
proc sql;
create table ip_completeness as
select sum(source='MA_ENCOUNTER') as ma_ip,
sum(source='FFS_MEDPAR') as ffs_ip,
calculated ma_ip / calculated ffs_ip as completeness_ratio
from work.ip;
quit;Citations
- [1]Cotterill PG. An assessment of completeness and medical coding of Medicare Advantage hospitalizations in two national data sets. Health Services Research. 2023;58(6):1226-1235.
- [2]Kronick R, Welch WP. Measuring coding intensity in the Medicare Advantage program. Medicare & Medicaid Research Review. 2014;4(2):E1-E19.