Developers
Developer API
Everything the calculator does is a REST API underneath — group ICD-10 codes to an MS-DRG (any manual version back to FY2022), price the claim with real FY2027 hospital-specific factors (FY2026 for earlier discharges), verify the result against the official Medicare pricer, and export the worksheet as PDF or Excel. JSON in, JSON out.
Base URL. Issued with your API key. The examples below call it $BASE.
On this page
Authentication & rate limits
Every call from your own server needs an issued key, sent as an X-API-Key header. Without one, or with a key we didn't issue, the endpoints on this page answer 401 (403 when the client is a script such as curl). The reply is the same for a missing and a wrong key, so it never confirms whether a guessed key exists. Usage is metered per key, not per IP, so your whole team or server fleet shares one predictable quota.
| Tier | Covers | Limit | Metered |
|---|---|---|---|
| API key | every endpoint here, any MS-DRG, no daily cap | 240/min · 10,000/hour across all endpoints | per key |
| No key (this site, in the browser) | grouping and lookups; pricing for 40 common MS-DRGs (others 403), 40 priced claims a day (then 429) | 60/min · 500/hour per endpoint (20/min · 150/hour on the lookups below and verify), 120/min · 1,500/hour across all endpoints | per client IP |
Keys are issued per organization — request one here with a line about what you're building. Exceeding a limit returns 429: back off and retry after a minute. Only the across-all-endpoints limit sends a Retry-After header.
Server-side use. Browser CORS is restricted to our own origins, so call the API from your backend. Please keep /drg/verify traffic modest — each call prices the claim through the official Medicare pricer live.
Endpoints
POST/drg/group
Group a claim to an MS-DRG from ICD-10-CM diagnoses (with POA flags) and ICD-10-PCS procedures. Optional age, sex, and dischargeStatus (UB-04 code) drive the disposition-split DRG families — AMI discharged-alive vs. expired, left-AMA, the newborn MDC, and the discharged-alive-only MCC rule. Add ccn + coveredCharges + los to price the resulting DRG in the same call.
curl -X POST "$BASE/drg/group" -H "Content-Type: application/json" \
-H "X-API-Key: $KEY" -d '{
"diagnoses": [
{"code": "I213", "poa": "Y"}, // principal first (STEMI)
{"code": "J9601", "poa": "Y"}, // acute resp failure — MCC
{"code": "I5021", "poa": "Y"}
],
"procedures": [{"code": "0271356"}, {"code": "02C03Z6"}],
"age": 68, "sex": "M", "dischargeStatus": "01",
"grouperVersion": "v44" // 11 manuals, v39 (FY2022) to v44 (FY2027); omitted or unknown: the one in force today
}'// response (trimmed) { "grouper": { "drg": "359", "mdc": "05", "path": "surgical", "severity": "MCC", "severityDriver": "J9601", "grouperVersion": "v44", "grouperFiscalYear": "FY2027", "secondaryDetail": [{"dx": "J9601", "level": "MCC", "poa": "Y"}, …], "caveats": […] }, "drgTitle": "Percutaneous Coronary Atherectomy w/ Intraluminal Device w/ MCC" }
Also accepts FHIR R4, vendor, and Epic Tapestry claim shapes — send the payload as-is and the API detects the format. Ungroupable claims return 422 with the reason. GET /drg/grouper-versions lists the loaded manual versions.
POST/drg/calculate
Price a DRG the way Medicare does — operating + capital + IME/DSH + uncompensated care + outlier, with the hospital's real factors.
curl -X POST "$BASE/drg/calculate" -H "Content-Type: application/json" \
-H "X-API-Key: $KEY" -d '{"drgCode": "871", "ccn": "330101", "coveredCharges": 120000, "los": 5}'| Field | Meaning |
|---|---|
| drgCode | MS-DRG, e.g. 871 (required) |
| ccn · wageIndex | a hospital CCN or a manual wage index (one required) |
| coveredCharges · los | drive outlier and transfer math |
| transferStatus | none · transfer · postacute |
| hmoPaidClaim | Medicare Advantage claim toggle |
| includeIme · includeDsh · includeUcp | strip components for contract modeling (default true) |
The response is the full dollar build the UI renders — component subtotals, outlier decision, grand total. Formula coefficients stay server-side.
POST/drg/verify
Price the same claim through our engine and the official Medicare pricer, live, side by side — the same check we publish on the site (typically matching to the cent).
curl -X POST "$BASE/drg/verify" -H "Content-Type: application/json" -H "X-API-Key: $KEY" \
-d '{"drgCode": "208", "ccn": "450358", "coveredCharges": 95000, "los": 4, "dischargeDate": "2026-03-15"}'All five fields above are required; add "hmoPaidClaim": true for MA claims. Returns both totals and the delta.
POST/drg/worksheet
The same inputs as /drg/calculate, returned as a client-ready pricing worksheet with the full line-by-line formula detail. Add "fmt": "pdf" (default) or "fmt": "xlsx". Same key, DRG access and limits as /drg/calculate. An optional "providers": ["billing:NPI", "attending:NPI"] adds a provider screening section (see POST /screen).
curl -o worksheet.pdf -X POST "$BASE/drg/worksheet" -H "Content-Type: application/json" \
-H "X-API-Key: $KEY" -d '{"drgCode": "871", "ccn": "330101", "coveredCharges": 120000, "los": 5, "fmt": "pdf"}'POST/drg/jobs
Price a large claim file in the background: the same rows and options as POST /drg/batch, up to 20,000 rows a job. The call returns 202 with a jobId; poll its status, then fetch the results as JSON or CSV. Jobs need an API key or an approved account sign-in.
curl -X POST "$BASE/drg/jobs" -H "Content-Type: application/json" -H "X-API-Key: $KEY" \
-d '{"rows": [{"drgCode": "871", "ccn": "330101", "coveredCharges": 120000, "los": 5}, …]}'
# 202 {"jobId": "…", "state": "queued", "rows": 18000, "done": 0, "queuePosition": 0, …}
curl "$BASE/drg/jobs/$JOB" -H "X-API-Key: $KEY" # state: queued, running, done, cancelled or failed
curl "$BASE/drg/jobs/$JOB/results" -H "X-API-Key: $KEY" # /drg/batch's shape: results, receipt, summary
curl "$BASE/drg/jobs/$JOB/csv" -H "X-API-Key: $KEY" -o priced.csv
curl -X POST "$BASE/drg/jobs/$JOB/cancel" -H "X-API-Key: $KEY"| Route | Returns |
|---|---|
| POST /drg/jobs | 202 and the status below. Body: rows plus includeUcp, includeIme, includeDsh, includePassthrough (default true) |
| GET /drg/jobs/{jobId} | jobId, state, rows, done, priced, errors, chunkRows, queuePosition, waitingOnRateLimit, createdAt, startedAt, finishedAt, error |
| GET /drg/jobs/{jobId}/results | the finished job: results (row = its place in your file), receipt, summary; a cancelled or failed job returns the rows it priced |
| GET /drg/jobs/{jobId}/csv | the same, one CSV line per submitted row |
| POST /drg/jobs/{jobId}/cancel | stops the job; rows already priced stay in its results (rows are charged when the job is submitted) |
| Tier | Rows per /drg/batch call | Background jobs |
|---|---|---|
| No key (in the browser) | 10 | no |
| Approved work-email account | 25 | yes, up to 20,000 rows |
| API key | 200 | yes, up to 20,000 rows |
| API key, top tier | 1,000 | yes, up to 20,000 rows |
One job runs at a time per key or account; a finished job is kept for 24 hours (the last three per caller). A job is paced in chunks at your tier’s batch rate, so a large file takes minutes, not seconds. Submitting is limited like /drg/batch (10 a minute, 50 an hour without a higher tier); status, results, CSV and cancel allow 60 a minute and 1,500 an hour.
| Status | When |
|---|---|
| 403 | no API key or approved sign-in |
| 404 | no such job for this key or sign-in (finished jobs are kept 24 hours) |
| 409 | you already have a job running, the queue is full, or the results are asked for before the job is done |
| 422 | no rows, or more than 20,000 |
POST/drg/remit
Reconcile an X12 835 remittance against the claims it pays. Each remitted claim is matched to its 837 claim (CLP01 = CLM01), regrouped under the manual for its discharge date, priced at the hospital (outpatient claims under OPPS) and its payment explained: paid against expected, the variance, the CARC and RARC codes and the likely cause, sorted by recoverable dollars. Patient identifiers are removed from every file before it is read.
curl -X POST "$BASE/drg/remit" -H "Content-Type: application/json" -H "X-API-Key: $KEY" \
-d '{"remit": "ISA*00*…~", "claims": [{"content": "ISA*00*…~", "filename": "claims.837"}], "perspective": "payer"}'| Field | Meaning |
|---|---|
| remit | the 835 text (required, up to 6 MB) |
| claims | up to 50 claim files, each {content, filename} (837I text, up to 6 MB each) |
| ccn | price every claim at this hospital instead of the CCN on each claim |
| perspective | payer (recoverable = overpayments, the default) or provider (underpayments) |
Every matched claim that is priced counts as one priced claim of your day, charged up front, and a call prices at most as many claims as your tier’s batch row limit above. It is rate-limited like /drg/batch. 422: no 835 text, a file that cannot be read, or more priceable claims than your row limit; 429: the day’s priced claims are spent.
POST/screen
Provider screening: compare a claim’s provider NPIs with the OIG List of Excluded Individuals/Entities (LEIE), the file OIG publishes each month. A hit is reported as matches an LEIE entry, with the list’s date and the entry as published, never as a finding that a provider is excluded: OIG says a match must be confirmed in its online search (exclusions.oig.hhs.gov) with the person’s SSN or the entity’s EIN. Screening changes no price, receipt or export amount, and it is not a payment determination.
curl -X POST "$BASE/screen" -H "Content-Type: application/json" -H "X-API-Key: $KEY" \
-d '{"claims": [{"claimRef": "TEST-1", "serviceFrom": "2026-07-10", "serviceThrough": "2026-07-16", "providerState": "TX",
"providers": [{"role": "rendering", "npi": "1234567893", "last": "ZZTESTPERSON", "first": "ALPHA"}]}]}'
# 200 {"results": [{"claimRef": "TEST-1", "screening": {"status": "noMatch", "lists": [...], "screened": [...], "flags": [], ...}}],
# "lists": [{"list": "LEIE", "asOf": "2026-10-09", "fileLabel": "09-2026", ...}]}| Field | Meaning |
|---|---|
| claims | the claims to screen: exactly 1 without an API key or approved sign-in; with one, up to your tier’s batch row limit |
| claimRef | your own reference, up to 40 characters, returned as given |
| serviceFrom, serviceThrough | the claim’s service dates (ISO); an entry is compared with them, or with today’s date when there are none |
| providerState | the provider’s 2-letter state; name checks need one |
| providers | up to 24, each {role, npi, last, first, middle, org}. npi (10 digits) is required on every provider; role is one of billing, attending, operating, otherOperating, rendering, referring, primaryCare, ordering, supervising, purchasedService, serviceFacility, other |
Limits. Without a key a call screens one claim and spends one of the day’s free lookups; more than one claim without a key or approved sign-in is a 401. Names are compared only when a call holds one claim, and only on providers that carry an NPI: a bulk call ignores names and says so in notes. There is no name search. A provider without an NPI, an unknown role or a malformed date is a 422. Screening never spends a priced claim. Inputs and results are not stored or logged.
The screening block. status is flags, noMatch, unavailable (the list could not be loaded: never read it as a no match) or notRun. lists names the LEIE file screened against (asOf, fileLabel, daysOld, stale past 45 days). screened lists each provider with npiResult (match, noMatch, notScreened with a reason) and nameResult. Each of flags carries code (LEIE_NPI, LEIE_REIN, LEIE_AFTER, LEIE_NAME, LEIE_NAME_REIN), severity (check, possible, info), basis, the roles and lines, the entry as published (name, state, general and specialty, exclusion authority and date, waiver), the service dates compared, and an evidence line for a case note. It never holds a birth date or address. notChecked says what was not screened: SAM.gov exclusions, state Medicaid exclusion lists, Medicare enrollment, and ordering and referring eligibility.
Elsewhere. POST /drg/import returns the same block as screening (a keyless call with andGroup: false gets status: notRun). POST /drg/batch/x12 returns rowScreening, one block per row in rows, and screeningLists. POST /drg/worksheet takes an optional providers list of "role:npi" strings (up to 6) and providerState, and adds a Provider screening section to the worksheet; without them the worksheet is unchanged. GET /meta reports the list on hand as screening.leie (asOf, fileLabel, rows, status, daysOld).
Arranging or contracting with an excluded person can bring civil money penalties (42 CFR 1003.200(b)(4)). OIG recommends screening monthly.
Claim fields go in the body
Every pricing endpoint takes its fields as one JSON object in a POST body: /drg/calculate, /drg/verify, /drg/worksheet, and the outpatient, post-acute and state Medicaid pricing routes. The field names and limits are the route’s own parameters, listed on this page for each route, and a field may hold a list where a route repeats a parameter. A query string is written to every access log between you and us, and charges and dates of service tied to a hospital are claim content.
Deprecated: the same routes still answer GET with the fields as query parameters, with the same results, rate limits and daily caps, and mark the reply with a Deprecation: true header. Move to POST: the GET form will be retired. Lookups (/drg/search, /drg/codes, hospital search) stay GET.
Outpatient, post-acute and Medicaid
These routes take their fields as one JSON object in a POST body (the field names below), and answer the deprecated GET with the same fields as query parameters.
| Route | Fields |
|---|---|
| /outpatient/compare | hcpcs; ccn or wageIndex (0.1 to 3.0); serviceDate (YYYY-MM-DD). Hospital outpatient (APC) and ASC rates side by side |
| /outpatient/apg | Medicaid EAPG line pricing |
| POST /outpatient/oce | an outpatient claim’s HCPCS lines through the OCE and NCCI edits (packaging, MUE units, PTP pairs) |
| /ltch/calculate | drgCode; wageIndex (default 1.0); lengthOfStay (0 to 730) |
| /irf/calculate | cmg; tier; wageIndex; rural; or ccn with dischargeDate to price on the facility’s record; charges |
| /ipf/calculate · /hospice/calculate · /snf/calculate · /hh/calculate · /esrd/calculate | the inpatient psychiatric, hospice, SNF, home health and dialysis pricers |
| POST /snf/claim | a SNF claim by its bill type: 21x as the PDPM per diem from each 0022 line’s HIPPS code and covered days |
| /nymedicaid/price | aprDrg; severity (1 to 4); opcert (NY operating certificate, TX TPI, CO CCN or VA NPI); state (NY, TX, CO, VA or WA); alcDays; drugs |
| /mdmedicaid/price | hospital; lines as CENTER:units pairs, for example MSG:4,OR:120; payer (ffs or mco) |
| POST /mdmedicaid/batch | up to 1,000 Maryland claims a call (25 without a key or sign-in), each {claimId, hospital, lines: [{center, units}]} |
| /vamedicaid/eapg/price · fee/rate · nf/price · hospice/price · homehealth/price · transplant/price | Virginia Medicaid outpatient EAPG, fee schedules, nursing facility, hospice, home health and transplant rates |
curl -X POST "$BASE/outpatient/compare" -H "Content-Type: application/json" -H "X-API-Key: $KEY" \
-d '{"hcpcs": "45385", "ccn": "330101"}'
curl -X POST "$BASE/nymedicaid/price" -H "Content-Type: application/json" -H "X-API-Key: $KEY" \
-d '{"aprDrg": "720", "severity": 3, "opcert": "…", "state": "NY"}'Without a key, priced calls on these routes count against a daily allowance of their own, separate from the inpatient one, and lookups against another; an API key or an approved account lifts both. They are rate-limited per endpoint like the routes above. 400 or 422: a field outside its range or a code the tables do not hold, with the reason in detail; 429: a limit or the day’s allowance is spent; 503: that pricer’s tables are not loaded.
Lookups
| Endpoint | What it returns |
|---|---|
| GET /drg/search?q=sepsis | MS-DRGs by code or title (code, title, MDC, type, weight) |
| GET /drg/codes?q=J96&kind=dx | ICD-10-CM / PCS codes with descriptions and CC/MCC severity (kind=dx|proc) |
| GET /drg/hospitals/search?q=houston | IPPS hospitals by name, or filter by state/city/zip |
| GET /drg/hospitals/{ccn} | one hospital's profile (name, location, wage index, CMI, teaching) |
| GET /drg/grouper-versions | loaded MS-DRG manual versions; isDefault marks the one in force today |
Search endpoints require ≥2 characters and cap at 25 rows per response.
Errors
| Status | Meaning |
|---|---|
| 400 / 404 | bad filter, or unknown DRG / CCN — {"detail": "…"} says which |
| 422 | claim couldn't be grouped, or invalid body — the detail carries the grouper's reason |
| 401 / 403 | no valid X-API-Key (403 for a script client such as curl) |
| 429 | rate limit hit; back off and retry after a minute, or after the Retry-After seconds when sent |
| 502 / 503 | an upstream dependency (official pricer, registry) is unavailable — retry later |
Estimates, not adjudication. Results are payment estimates from published factors — final payment is determined by the payer at claim adjudication. Grouping notes its approximations in the caveats field of every response.
© 2026 PayerParity · info@payerparity.com · Request an API key