Guide · Python · Healthcare · REST API

ICD-10 Validation in Python — Medical Diagnosis Codes

Every diagnosis on a medical claim, discharge summary, or death certificate in most of the world is encoded as an ICD-10 code. Here's how the classification works, the difference between WHO ICD-10 and the US ICD-10-CM superset, and how to validate codes in a Python application with the IsValid API.

1. What is ICD-10?

ICD-10 is the 10th revision of the International Classification of Diseases, a standardised taxonomy of diseases, injuries, and causes of death maintained by the World Health Organization. First published in 1992, ICD-10 is used in more than 100 countries for mortality reporting, morbidity surveillance, clinical documentation, and healthcare reimbursement.

Each ICD-10 code represents a single diagnosis, condition, or cause of death. Codes are organised into 22 chapters covering broad body systems and disease categories. The WHO base classification has around 14,000 categories; the US clinical modification extends this to over 70,000 billable codes.

ICD-10 codes appear throughout healthcare data: electronic health records (EHRs), HL7 messages, FHIR Condition resources, insurance claims, cancer registries, and international cause-of-death reports. Validating them — both syntactically and against the current official dictionary — is essential for claim acceptance, interoperability, and data quality.


2. Code structure

An ICD-10 code has a well-defined prefix structure:

J45.0

Chapter letter

J

Respiratory system

Category

45

Asthma

Sub-category

0

Predominantly allergic

The first character is a letter (A–Z, excluding U which is reserved by WHO). The next two characters are digits forming the category. An optional decimal point followed by one to four additional characters specifies sub-categories and clinical detail.

CodeMeaning
A00.0Cholera due to Vibrio cholerae 01, biovar cholerae
E11.9Type 2 diabetes mellitus without complications
I10Essential (primary) hypertension
I21.9Acute myocardial infarction, unspecified
J45.0Predominantly allergic asthma
S72.001AFracture of right femur, initial encounter (CM-only)
U07.1COVID-19, virus identified
Z00.00General adult medical examination without abnormal findings

3. WHO ICD-10 vs ICD-10-CM

There is not one ICD-10 dataset but several national clinical modifications:

WHO ICD-10

The base international classification, ~14,000 categories. Used for mortality reporting, epidemiology, and healthcare outside the United States. Codes have the form A00, A00.0.

ICD-10-CM (US)

The US Clinical Modification maintained by NCHS/CMS. Superset with over 70,000 highly granular codes for clinical and billing use. Adds alphanumeric sub-codes and extension characters (e.g. S06.0X1A).

ℹ️The IsValid /v0/icd10 endpoint accepts both forms. Dictionary lookup is backed by the NIH/NLM ICD-10-CM dataset, a superset covering both standards.

4. Why ICD-10 validation matters

Claim acceptance and reimbursement

Medical claims with invalid or outdated ICD-10 codes are rejected by payers. Claim scrubbers run ICD-10 validation before submission to catch typos, decommissioned codes, and incorrect code-to-diagnosis combinations.

HL7 and FHIR interoperability

FHIR Condition resources reference ICD-10 codes via the http://hl7.org/fhir/sid/icd-10 code system. Exchanging patient data between systems requires every code to be syntactically valid and registered.

Clinical data quality

Clinical dashboards, cohort selection, and registry submissions rely on accurate diagnosis codes. Invalid codes distort quality metrics, miss patients from outcome studies, or trigger false-positive safety alerts.

Public health reporting

Cancer registries, notifiable disease surveillance, and cause-of-death reporting use ICD-10 codes for automated case detection. Invalid codes break downstream aggregation.


5. The right solution

The IsValid ICD-10 API validates both format and existence against a locally-maintained copy of the NIH/NLM ICD-10-CM dataset (refreshed monthly).

70,000+
Codes
ICD-10-CM dictionary
<20ms
Response time
indexed lookup
100/day
Free tier
no credit card

Full parameter reference: ICD-10 Validation API docs →


6. Python code example

Install with pip install isvalid-sdk or pip install requests.

Validate a single ICD-10 code:

# icd10_validator.py
import os
from isvalid_sdk import IsValidConfig, create_client

iv = create_client(IsValidConfig(api_key=os.environ["ISVALID_API_KEY"]))

# ── Validate an ICD-10 code ─────────────────────────────────────────────────

result = iv.icd10("J45.0")

if not result["found"]:
    print("Code is not in the ICD-10-CM dictionary")
else:
    print(f"Title: {result['title']}")     # → 'Predominantly allergic asthma'
    print(f"Chapter: {result['chapter']}") # → 'J'
    print(f"Parent: {result['parent']}")   # → 'J45'
    print(f"Leaf: {result['isLeaf']}")     # → True (billable)

Expand a parent code into its children:

# List all sub-categories of asthma (J45)
def list_children(parent: str) -> list[dict]:
    response = requests.get(
        f"{BASE_URL}/v0/icd10/children",
        params={"code": parent},
        headers={"Authorization": f"Bearer {API_KEY}"},
    )
    response.raise_for_status()
    return response.json()


for c in list_children("J45"):
    print(f"  {c['icd10']} — {c['title']}")
# → J45.0 Predominantly allergic asthma
# → J45.1 Nonallergic asthma
# → J45.2 Mild intermittent asthma
# ...

Typical use — validating a batch of diagnosis codes from an incoming claim:

import asyncio
import httpx

async def scrub_claim(claim: dict, client: httpx.AsyncClient) -> dict:
    """Validate every diagnosis on a claim concurrently."""
    async def check(dx):
        r = await client.get(
            f"{BASE_URL}/v0/icd10",
            params={"value": dx["code"]},
            headers={"Authorization": f"Bearer {API_KEY}"},
        )
        body = r.json()
        return {"dx": dx, "valid": body.get("valid"), "found": body.get("found"), "title": body.get("title")}

    results = await asyncio.gather(*(check(dx) for dx in claim["diagnoses"]))

    errors = [
        f"{r['dx']['code']}: {'invalid format' if not r['valid'] else 'not in dictionary'}"
        for r in results if not r["valid"] or not r["found"]
    ]

    return {"accepted": not errors, "errors": errors, "annotated": results}
ICD-10 codes are case-insensitive on input. Normalise to uppercase with code.upper() before storing so keys are stable across ingestions.

7. cURL example

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.isvalid.dev/v0/icd10?value=J45.0"

Validate an ICD-10-CM code with extension characters:

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.isvalid.dev/v0/icd10?value=S06.0X1A"

List child codes of a category:

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.isvalid.dev/v0/icd10/children?code=J45"

8. Understanding the response

Known code:

{
  "valid": true,
  "found": true,
  "icd10": "J45.0",
  "title": "Predominantly allergic asthma",
  "chapter": "J",
  "parent": "J45",
  "isLeaf": true,
  "source": "CMS",
  "version": "NLM-2026"
}

Valid format but unknown code:

{
  "valid": true,
  "found": false,
  "icd10": "J99.9",
  "title": null,
  "chapter": "J",
  "parent": null,
  "isLeaf": null,
  "source": null,
  "version": null
}

Invalid format:

{
  "valid": false
}
FieldTypeDescription
validboolWhether the code matches the ICD-10 / ICD-10-CM format regex
foundboolWhether the code exists in the ICD-10-CM dictionary
icd10strNormalised input code
titlestr | NoneDiagnosis description
chapterstr | NoneChapter letter (A–Z)
parentstr | NoneParent code in the hierarchy
isLeafbool | NoneTrue if the code has no children (billable)
sourcestr | NoneDataset source (CMS)
versionstr | NoneDataset release identifier

9. Edge cases

Dotted vs flat code form

Legacy systems often emit codes without the decimal point (J450 instead of J45.0). The API accepts both and returns the canonical dotted form.

Header vs billable codes

Non-leaf codes like J45 are parent/header categories — not billable in the US. Check isLeaf in claim scrubbers.

Deprecated codes

CMS retires and adds codes annually (effective October 1). A code valid in FY2024 may not appear in FY2026. The response version field identifies the dictionary release used for validation.

ICD-10 vs ICD-10-CM

Codes with letters after the decimal (e.g. S06.0X1A) exist only in US ICD-10-CM. For strict WHO ICD-10 processing, apply a stricter downstream filter.

⚠️The regex is permissive by design — it accepts the union of WHO ICD-10 and ICD-10-CM. Apply a stricter downstream filter if you need WHO-only codes.

Summary

Validate both format and existence — a syntactically correct code may not be in the current dictionary
Use isLeaf to distinguish billable codes from parent/header categories
Normalise input to uppercase and preserve the decimal when storing
Record the dictionary version alongside the validation result for auditable claim history
Do not rely on regex-only validation — dictionary lookup catches decommissioned codes
Do not assume WHO ICD-10 and ICD-10-CM are interchangeable — CM is a US-only superset

Python integration notes

In typed Python projects, model a validated ICD-10 code as a NewType Icd10 = NewType("Icd10", str) — so type checkers enforce that only validated values flow through your claim pipeline. Pydantic models can use a custom validator that calls the IsValid API for the diagnosis_code field.

For Django or FastAPI services accepting diagnosis codes as request parameters, add a dependency/middleware that validates before the view runs. FastAPI's Depends() is a clean fit — a single dependency returns the full parsed API response, and every route that needs the metadata can declare it.

For claim scrubbers processing many codes, use httpx.AsyncClient with asyncio.gather() to validate all diagnoses on a claim concurrently. Cache responses in Redis keyed by the normalised code — a one-day TTL is safe; the dictionary changes annually.

  • Read os.environ["ISVALID_API_KEY"] at startup and fail fast if missing
  • Mock the client in unit tests with responses or respx — never call the real API in CI
  • Log responses at DEBUG level for data-quality post-mortems
  • For extreme throughput, pre-filter obviously malformed codes with a local regex before making API calls

See also

Validate ICD-10 codes instantly

Free tier includes 100 API calls per day. No credit card required.