Guide · Python · Healthcare · REST API

ICD-11 Validation in Python — WHO MMS Diagnosis Codes

ICD-11 is the WHO's successor to ICD-10 — a digital-first classification in effect since 2022 that is gradually replacing ICD-10 for mortality and morbidity reporting worldwide. Here's how ICD-11 MMS codes are structured, what changed from ICD-10, and how to validate them in Python with the IsValid API.

1. What is ICD-11?

ICD-11 is the 11th revision of the International Classification of Diseases, adopted by the World Health Assembly in 2019 and in official use since 1 January 2022. ICD-11 is a digital-native classification — a web-based ontology (the Foundation Component) from which multiple linearizations are derived.

The primary linearization is MMS — Mortality and Morbidity Statistics — which serves the role that ICD-10 filled: a flat list of stem codes for reporting, billing, and statistical analysis. MMS has about 17,000 categories across 28 chapters, with new chapters for traditional medicine and extension codes.

Adoption is gradual — as of 2026, most member states are still in the transition period. Many jurisdictions have set target dates between 2025 and 2030 for full ICD-11 implementation; systems commonly produce both ICD-10 and ICD-11 codes in parallel during the migration.


2. MMS stem code structure

An ICD-11 MMS stem code looks like this:

1A00

Chapter

1

Infectious diseases

Block

A

Gastrointestinal

Category

00

Cholera

The first character is a digit 1–9 identifying the chapter (or V / X for supplementary and extension chapters). Subsequent characters are alphanumeric, with letters I and O excluded to prevent confusion with digits. An optional decimal + one or two characters specifies sub-categories.

CodeMeaning
1A00Cholera
2B5ABreast cancer
5A11Type 2 diabetes mellitus
6A00Disorders of intellectual development
BA00Essential hypertension
CA23Asthma
RA01COVID-19

3. ICD-10 vs ICD-11

AspectICD-10ICD-11
Year adopted19902019 (in effect 2022)
Code formatLetter + digits (+ decimal)Digit + alphanum (+ decimal)
Chapters2228
Categories~14,000 (WHO base)~17,000 (MMS)
StructureFlat tabularOntology-based, multi-linearization
Post-coordinationLimitedFirst-class (with extension codes)
API accessFile downloadsWHO ICD-API (OAuth)
ℹ️ICD-11 is not a drop-in replacement for ICD-10 — the code formats are different, and many diagnoses have been reorganised between chapters. WHO publishes a mapping table (TabMap) between the two classifications.

4. Why ICD-11 validation matters

Parallel ICD-10 / ICD-11 systems

During the transition, many systems produce or consume both ICD-10 and ICD-11 codes. Validation distinguishes the two (formats are non-overlapping) and routes codes to the correct downstream processor.

FHIR Condition resources

FHIR supports ICD-11 via the http://id.who.int/icd/release/11/mms code system. Invalid codes break cross-system interoperability and are rejected by conformant validators.

WHO reporting and surveillance

Countries report mortality and morbidity statistics to WHO using ICD-11 codes. Invalid codes cause rejections at the WHO submission portal.

Research datasets

Clinical research increasingly uses ICD-11 for international cohort studies. Syntactically-valid but unknown codes often indicate OCR or transcription errors that would otherwise silently corrupt analysis results.


5. The right solution

The IsValid ICD-11 API validates format with a strict regex and performs dictionary lookup against a locally-maintained mirror of the current WHO ICD-11 MMS linearization (refreshed monthly from the WHO ICD-API).

17,000+
Codes
MMS categories
WHO API
Source
monthly refresh
100/day
Free tier
no credit card

Full parameter reference: ICD-11 Validation API docs →


6. Python code example

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

# icd11_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-11 MMS code ─────────────────────────────────────────────

result = iv.icd11("1A00")

if not result["found"]:
    print("Code is not in the WHO ICD-11 MMS dictionary")
else:
    print(f"Title: {result['title']}")                   # → 'Cholera'
    print(f"Chapter: {result['chapter']}")               # → '01'
    print(f"Foundation URI: {result['foundationUri']}")   # → WHO foundation link
    print(f"Version: {result['version']}")

Routing a mixed ICD-10 / ICD-11 stream based on the first character:

# ICD-10 starts with a LETTER, ICD-11 starts with a DIGIT.
# Delegate validation to the right endpoint.

def validate_diagnosis(code: str) -> dict:
    first = code.strip()[:1]
    if first.isdigit():
        return {"revision": "ICD-11", **validate_icd11(code)}
    return {"revision": "ICD-10", **validate_icd10(code)}


print(validate_diagnosis("J45.0"))  # → ICD-10
print(validate_diagnosis("CA23"))   # → ICD-11
ICD-11 codes are case-insensitive on input, but letters I and O are never valid — WHO excluded them to prevent confusion with digits 1 and 0.

7. cURL example

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.isvalid.dev/v0/icd11?value=1A00"

Validate a circulatory code:

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.isvalid.dev/v0/icd11?value=BA00"

8. Understanding the response

Known code:

{
  "valid": true,
  "found": true,
  "icd11": "1A00",
  "title": "Cholera",
  "chapter": "01",
  "parent": null,
  "isLeaf": false,
  "foundationUri": "http://id.who.int/icd/entity/257068234",
  "blockId": null,
  "version": "ICD-11 for Mortality and Morbidity Statistics"
}

Valid format but unknown code:

{
  "valid": true,
  "found": false,
  "icd11": "9ZZ9",
  "title": null,
  "chapter": null,
  "parent": null,
  "isLeaf": null,
  "foundationUri": null,
  "blockId": null,
  "version": null
}

Invalid format:

{
  "valid": false
}
FieldTypeDescription
validboolWhether the code matches the ICD-11 MMS stem regex
foundboolWhether the code exists in the WHO ICD-11 MMS dictionary
icd11strNormalised input code
titlestr | NoneEntity title
chapterstr | NoneChapter number
isLeafbool | NoneTrue if the code has no child entities
foundationUristr | NoneWHO foundation URI for the entity
versionstr | NoneMMS release version

9. Edge cases

Letters I and O are reserved

WHO excludes I and O from stem codes. Any code containing either letter fails the regex.

Post-coordination

ICD-11 supports combining codes via the & separator (e.g. CA23.3&XB25). Split on & before validating each stem code individually:

expression = "CA23.3&XB25"
parts = expression.split("&")

results = [validate_icd11(p) for p in parts]
all_valid = all(r["valid"] and r["found"] for r in results)

Chapter V and Chapter X

Extension chapters V (functioning) and X (extension codes) use letter prefixes. Verify chapter context in the response chapter field.

Version drift

WHO publishes annual MMS releases. The version field in the response records which release was used for dictionary lookup; log it with each validation for auditability.


Summary

Distinguish ICD-10 from ICD-11 by the first character — letter means ICD-10, digit means ICD-11
Reject any code containing letters I or O — WHO reserves these
Split post-coordinated expressions on & before validating each stem code
Record the MMS version with each validation for auditability
Do not treat ICD-10 and ICD-11 as interchangeable — map via WHO TabMap when migrating historical data
Do not rely on format alone — dictionary lookup catches OCR and transcription errors

Python integration notes

In typed Python projects, model ICD-10 and ICD-11 as distinct NewType aliases so type checkers prevent accidental cross-revision comparison. A Pydantic Union[Icd10, Icd11] discriminated by the first character is useful when storing mixed data during migration periods.

Because the first character unambiguously distinguishes the two revisions, a cheap local dispatch check can route codes to the right endpoint without an extra network call: code[0].isdigit() ICD-11, otherwise ICD-10.

Use httpx.AsyncClient for concurrent validation of batches and cache responses in Redis keyed by normalised code. A one-day TTL is appropriate — WHO publishes annual releases, so the dictionary is effectively static within a year.

  • Read os.environ["ISVALID_API_KEY"] at startup and fail fast if missing
  • Use respx to mock the IsValid client in unit tests — never call the real API in CI
  • For FastAPI services, expose ICD validation as a dependency so every endpoint automatically benefits from the pre-validated metadata
  • Record version in your audit log so you can prove which MMS release validated each historical record

See also

Validate ICD-11 codes instantly

Free tier includes 100 API calls per day. Validates against the current WHO ICD-11 MMS linearization.