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.
In this guide
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:
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.
| Code | Meaning |
|---|---|
| A00.0 | Cholera due to Vibrio cholerae 01, biovar cholerae |
| E11.9 | Type 2 diabetes mellitus without complications |
| I10 | Essential (primary) hypertension |
| I21.9 | Acute myocardial infarction, unspecified |
| J45.0 | Predominantly allergic asthma |
| S72.001A | Fracture of right femur, initial encounter (CM-only) |
| U07.1 | COVID-19, virus identified |
| Z00.00 | General 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).
/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).
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}
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 }
| Field | Type | Description |
|---|---|---|
| valid | bool | Whether the code matches the ICD-10 / ICD-10-CM format regex |
| found | bool | Whether the code exists in the ICD-10-CM dictionary |
| icd10 | str | Normalised input code |
| title | str | None | Diagnosis description |
| chapter | str | None | Chapter letter (A–Z) |
| parent | str | None | Parent code in the hierarchy |
| isLeaf | bool | None | True if the code has no children (billable) |
| source | str | None | Dataset source (CMS) |
| version | str | None | Dataset 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.
Summary
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
responsesorrespx— never call the real API in CI - Log responses at
DEBUGlevel 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.