ICD-10 Validation in Node.js — 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 Node.js 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 — infectious diseases, neoplasms, circulatory system, injuries, external causes, and so on. Each chapter is subdivided into blocks, categories, and highly specific sub-categories, forming a hierarchical tree of around 14,000 categories in the WHO base classification and over 70,000 billable codes in the US clinical modification.
ICD-10 codes appear throughout healthcare data: electronic health records (EHRs), HL7 messages, FHIR Condition resources, insurance claims (CMS-1500, UB-04), cancer registries, public health surveillance feeds, 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 always a letter (A–Z, excluding U which is reserved by WHO for provisional use). The next two characters are digits forming the category (e.g. J45 = Asthma). An optional decimal point followed by one to four additional characters specifies sub-categories and clinical detail.
The chapter letter maps to broad classes of disease:
| Letter | Range | Chapter |
|---|---|---|
| A–B | A00–B99 | Certain infectious and parasitic diseases |
| C–D | C00–D49 | Neoplasms |
| E | E00–E89 | Endocrine, nutritional and metabolic diseases |
| F | F01–F99 | Mental, behavioural and neurodevelopmental disorders |
| I | I00–I99 | Diseases of the circulatory system |
| J | J00–J99 | Diseases of the respiratory system |
| K | K00–K95 | Diseases of the digestive system |
| O | O00–O9A | Pregnancy, childbirth and the puerperium |
| S–T | S00–T88 | Injury, poisoning and other external causes |
| V–Y | V00–Y99 | External causes of morbidity |
| Z | Z00–Z99 | Factors influencing health status |
A few well-known codes:
| 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 unspecified part of neck of right femur, initial encounter (CM-only) |
| U07.1 | COVID-19, virus identified |
| Z00.00 | Encounter for general adult medical examination without abnormal findings |
3. WHO ICD-10 vs ICD-10-CM
A common source of confusion: there is not one ICD-10 dataset but several national clinical modifications. The two most commonly encountered are:
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 — digits after the decimal point.
ICD-10-CM (US)
The US Clinical Modification maintained by NCHS/CMS. A superset of WHO ICD-10 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 with a permissive regex — A00, A00.0, and S06.0X1A all pass format validation. Dictionary lookup is backed by the NIH/NLM ICD-10-CM dataset, which is 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, delaying reimbursement and adding administrative cost. 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 EHRs, HIEs, and regulatory systems requires every code to be syntactically valid and registered in the current version of the classification.
Clinical data quality
Clinical dashboards, cohort selection, and registry submissions rely on accurate diagnosis codes. A small rate of invalid codes can 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 and can hide emerging health trends.
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). It returns the diagnosis title, chapter, parent code, and leaf/billable flag.
Full parameter reference and response schema: ICD-10 Validation API docs →
6. Node.js code example
Validate a single ICD-10 code:
import { createClient } from '@isvalid-dev/sdk'; const iv = createClient({ apiKey: process.env.ISVALID_API_KEY }); // ── Validate an ICD-10 code ───────────────────────────────────────────────── const result = await iv.icd10('J45.0'); if (!result.found) { console.log('Code is not in the ICD-10-CM dictionary'); } else { console.log('Title:', result.title); // → 'Predominantly allergic asthma' console.log('Chapter:', result.chapter); // → 'J' console.log('Parent:', result.parent); // → 'J45' console.log('Leaf:', result.isLeaf); // → true (billable) }
Expand a parent code into its children:
// Get all sub-categories of asthma (J45) async function listChildren(parent) { const response = await fetch( `${BASE_URL}/v0/icd10/children?code=${encodeURIComponent(parent)}`, { headers: { Authorization: `Bearer ${API_KEY}` } }, ); return response.json(); } const children = await listChildren('J45'); for (const c of children) { console.log(` ${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:
// Validate every diagnosis on a claim before submission async function scrubClaim(claim) { const results = await Promise.all( claim.diagnoses.map(async (dx) => { const r = await validateIcd10(dx.code); return { dx, valid: r.valid, found: r.found, title: r.title }; }), ); const errors = results .filter((r) => !r.valid || !r.found) .map((r) => `${r.dx.code}: ${!r.valid ? 'invalid format' : 'not in dictionary'}`); return { accepted: errors.length === 0, errors, annotated: results }; }
j45.0 and J45.0, but always normalise to uppercase when storing to prevent duplicate keys in indexes.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 | boolean | Whether the code matches the ICD-10 / ICD-10-CM format regex |
| found | boolean | Whether the code exists in the ICD-10-CM dictionary |
| icd10 | string | Normalised input code |
| title | string | null | Diagnosis description |
| chapter | string | null | Chapter letter (A–Z) |
| parent | string | null | Parent code in the hierarchy |
| isLeaf | boolean | null | True if the code has no children (billable) |
| source | string | null | Dataset source (CMS) |
| version | string | null | Dataset release identifier |
9. Edge cases
Dotted vs flat code form
Some legacy systems emit ICD-10 codes without the decimal point (e.g. J450 instead of J45.0). The API accepts both forms and returns the canonical dotted form in the icd10 field.
Header vs billable codes
Non-leaf codes like J45 are parent/header codes — they describe a category but cannot be used for billing in the US. Use the isLeaf field to distinguish. Claim scrubbers should reject header codes on US claims even when found: true.
Deprecated codes
CMS retires and adds codes annually (effective October 1). A code valid in FY2024 may not appear in FY2026. When processing historical data, the dictionary version in the response (version field) tells you which release was used for validation.
ICD-10 vs ICD-10-CM
Codes beyond 3 digits after the decimal, or with alphabetic characters after the decimal (e.g. S06.0X1A), exist only in US ICD-10-CM. If your context is non-US WHO ICD-10, reject codes longer than X00.0 or containing letters after the decimal even when the API returns found: true.
Summary
Node.js integration notes
In a TypeScript project, model a validated ICD-10 code as a branded type — type Icd10 = string & { readonly __brand: 'Icd10' } — so the compiler enforces that only checked values flow through your claim pipeline. The IsValid SDK ships with full TypeScript definitions, giving you autocomplete on every response field without manual type declarations.
For Express.js or Fastify services that accept diagnosis codes as query or path parameters, add a middleware that validates the value before the route handler runs. On success, attach the full response to req.validatedData so handlers can use title, chapter, and isLeaf without a second API call.
For claim scrubbers processing thousands of codes per second, cache API responses in Redis keyed by the normalised code. A one-day TTL is safe; the dictionary changes at most annually. Use Promise.allSettled() to validate all diagnoses on a claim in parallel and collect all errors at once rather than failing on the first bad code.
See also
Validate ICD-10 codes instantly
Free tier includes 100 API calls per day. No credit card required. Validate against a locally-maintained copy of the NIH/NLM ICD-10-CM dictionary.