Compare payers' prior authorization metrics
Approval, denial, appeal and turnaround rates for prior authorization, as each health plan posts them every year, side by side and compared like with like.
| Plan | Builder and up (403 plan-upgrade-required on Free) |
| Dataset | Payer prior authorization metrics (CMS-0057-F postings) |
| Calls per answer | 1 to find payers, then 1 per payer |
1. Find the payers
Filter by the level a payer reports at (type: ma_contract for Medicare Advantage contracts),
by state (payers with a reporting unit there), or by name or unit id (q):
curl "https://api.healthcare.dev/v1/payers?type=ma_contract" \
-H "Authorization: Bearer $HDEV_API_KEY" | jq '[.data[] | {id, name}]'
# One payer's reporting units and the years each posted
curl https://api.healthcare.dev/v1/payers/humana -H "Authorization: Bearer $HDEV_API_KEY"2. Get each payer's metrics
curl "https://api.healthcare.dev/v1/payers/humana/prior-auth-metrics?unit=H1036" \
-H "Authorization: Bearer $HDEV_API_KEY" | jq '.data.reports[0] | {unit: .unit.key, year, report_status, approved: .standard.approved, mean_time: .standard.mean_time, derivation, evidence}'{
"unit": "H1036",
"year": 2025,
"report_status": "available",
"approved": {
"value": 92.98,
"unit": "percent",
"count": 1212346,
"denominator": 1303837,
"denominator_basis": "total_requests",
"as_reported": null
},
"mean_time": { "value": 24, "unit": "hours", "as_reported": "1 day(s)", "...": "..." },
"derivation": "source_stated",
"evidence": [{ "url": "https://assets.humana.com/...", "anchor": "page=2" }]
}Without year you get the latest year the payer posted (years_available lists the others);
without unit, every unit we hold for that payer and year.
Compare them
Payers don't all divide by the same thing, so compare a percentage only with another of the same
denominator_basis, and skip reports that are still pending_review. A payer's metrics cover
every unit it reports (a Medicare Advantage organization may also post Medicaid or exchange
units), so keep only the unit type you compare.
TypeScript
import { HealthcareDev } from "@healthcare.dev/sdk";
const hdev = new HealthcareDev();
const payers = await hdev.payers.list({ type: "ma_contract" });
const metrics = await Promise.all(
payers.data.slice(0, 5).map((p) => hdev.payers.priorAuthMetrics(p.id)),
);
const rows = metrics.flatMap(({ data }) =>
data.reports
.filter((r) => r.unit.type === "ma_contract" && r.report_status === "available")
.map((r) => {
const approved = r.standard.approved;
return {
payer: r.payer_name,
unit: r.unit.key,
year: r.year,
approved_pct: approved.value,
basis: approved.value === null ? null : approved.denominator_basis,
mean_hours: r.standard.mean_time.value,
posting: r.evidence[0]?.url,
};
}),
);
console.table(rows.filter((r) => r.basis === "total_requests"));Python
from healthcare_dev import HealthcareDev
hdev = HealthcareDev()
rows = []
for payer in hdev.payers.list(type="ma_contract")["data"][:5]:
for r in hdev.payers.prior_auth_metrics(payer["id"])["data"]["reports"]:
if r["unit"]["type"] != "ma_contract" or r["report_status"] != "available":
continue
approved = r["standard"]["approved"]
rows.append({
"payer": r["payer_name"],
"unit": r["unit"]["key"],
"year": r["year"],
"approved_pct": approved["value"],
"basis": approved.get("denominator_basis"),
"mean_hours": r["standard"]["mean_time"]["value"],
"posting": r["evidence"][0]["url"] if r["evidence"] else None,
})
for row in rows:
if row["basis"] == "total_requests":
print(row)Ask an agent
Compare prior authorization approval rates and average decision times across Medicare Advantage contracts. Only compare percentages with the same denominator basis, note any metric a payer didn't report, and link each payer's posting.
The agent uses find_payers and get_payer_prior_auth_metrics.
Read the answer
- Every metric is a value or a gap, never an estimate. A gap is
{ value: null, not_reported_reason }:not_reported(the posting doesn't give it),not_applicable(the payer says it doesn't apply) orunparseable_pending_review. denominator_basissays what a percentage is out of:total_requests,total_requests_all_priorities,total_appeals,extended_requestsornot_stated.derivation:source_statedmeans fixed rules read the numbers straight from the posting and they passed our checks (approved plus denied at most 100%, counts no larger than their totals, units stated, the right year and unit);reviewedmeans a person checked them against the posting.confidenceis1for both and0while a report ispending_review, when every metric isnull.evidencelinks the payer's posting and where in it the unit's numbers are. Show it with any comparison.
What it is not
Values are as each payer reported them, not audited: payers count, round and label differently.
This is not the list of services that need prior authorization (the payer's link is in
items_requiring_pa_url); for that, see
Check a prior auth requirement.
Drug safety status
For an NDC: the RxNorm concept, whether FDA lists a current shortage, any recall FDA hasn't terminated, and the label's boxed warning, each with its source and release.
Check a prior auth requirement (preview)
Does a payer's public prior authorization list require prior authorization for a procedure code, per line of business, with dates, conditions and the list to check. Preview: not available yet.