HS Code Validation in Node.js — International Trade Classification
Every product crossing an international border needs a Harmonized System (HS) code. Get the code wrong and you face the wrong tariff, overpaid duties, or customs penalties. Here's how to validate HS codes properly in Node.js — including chapter, heading, and subheading lookups.
In this guide
1. What is an HS code?
The Harmonized System (HS) is a standardised numerical classification for traded products, maintained by the World Customs Organization (WCO). Introduced in 1988, it is used by more than 200 countries and economies as the foundation for their customs tariffs and trade statistics.
The system organises roughly 5,000 commodity groups into a logical hierarchy. Every item that crosses a border — from coffee beans to semiconductors — is assigned an HS code that determines the applicable tariff rate, trade regulations, and statistical reporting category.
HS codes are mandatory for international trade. They appear on commercial invoices, customs declarations, bills of lading, and certificates of origin. Without a valid HS code, goods cannot clear customs in any country that is party to the HS Convention.
The WCO revises the HS nomenclature every five years (most recently in 2022) to reflect changes in technology, trade patterns, and environmental concerns. This means HS codes can become obsolete or be reclassified, making ongoing validation essential.
2. HS code structure
HS codes are organised into three hierarchical levels. The international standard covers the first six digits. Individual countries then add their own national digits (typically bringing the total to 8–10 digits) for finer classification and tariff determination.
| Level | Digits | Count | Example | Description |
|---|---|---|---|---|
| Chapter | 2 | 97 chapters | 84 | Nuclear reactors, boilers, machinery... |
| Heading | 4 | ~1,200 headings | 8471 | Automatic data-processing machines |
| Subheading | 6 | ~5,000 subheadings | 847150 | Processing units (other than combined) |
3. Why HS code validation matters
Correct tariff classification
The HS code directly determines the tariff rate applied to imported goods. A wrong code means paying the wrong duty — either overpaying (reducing margins) or underpaying (risking back-duties, interest, and penalties when audited by customs authorities).
Customs clearance speed
Invalid or unrecognised HS codes trigger manual review at the border. This can delay clearance by days, incurring demurrage charges, warehouse fees, and disrupting just-in-time supply chains. Validating codes before shipment prevents these delays.
Trade compliance
Certain HS codes are subject to export controls, sanctions, anti-dumping duties, or preferential trade agreements. Misclassification can lead to inadvertent violations of trade regulations — a serious legal and reputational risk for any business involved in international trade.
Data quality in trade platforms
ERP systems, customs brokers, and logistics platforms process thousands of product classifications daily. Validating HS codes at the point of entry — in product catalogs, purchase orders, or customs forms — catches errors before they propagate through the supply chain.
4. The right solution
The IsValid HS Code API provides two endpoints: a validate endpoint that checks whether a 2, 4, or 6-digit HS code is valid and returns its description, and a list endpoint that returns all codes at a given level or within a specific chapter.
Get your free API key at isvalid.dev. The free tier includes 100 calls per day — enough for most development and low-volume production use.
Full parameter reference and response schema: HS Code Validation API docs →
5. Node.js code example
Using the isvalid-sdk Node.js SDK or the built-in fetch API. Install with npm install isvalid-sdk.
// hs-validator.mjs import { IsValid } from "isvalid-sdk"; const iv = new IsValid({ apiKey: process.env.ISVALID_API_KEY }); // ── Validate a heading (4-digit HS code) ──────────────────────────────── const result = await iv.hsCode("8471"); if (!result.valid) { console.log("Invalid HS code"); } else { console.log(`Valid HS code — ${result.level}`); console.log(`Code: ${result.code}`); console.log(`Description: ${result.description}`); console.log(`Formatted: ${result.formatted}`); console.log(`Chapter: ${result.chapter.code} — ${result.chapter.description}`); // → Valid HS code — heading // → Code: 8471 // → Description: Automatic data-processing machines and units thereof // → Formatted: 84.71 // → Chapter: 84 — Nuclear reactors, boilers, machinery and mechanical appliances; parts thereof } // ── Validate a subheading (6-digit HS code) ───────────────────────────── const sub = await iv.hsCode("847150"); console.log(sub.description); // → Processing units other than those of subheading 8471.41 or 8471.49 // ── Validate a chapter (2-digit HS code) ──────────────────────────────── const chapter = await iv.hsCode("84"); console.log(chapter.description); // → Nuclear reactors, boilers, machinery and mechanical appliances; parts thereof
You can also list all HS codes at a given level or within a chapter:
// List all headings in chapter 84 const headings = await iv.hsCode.list({ chapter: "84", level: "heading" }); for (const h of headings) { console.log(`${h.code} — ${h.description}`); } // → 8401 — Nuclear reactors; fuel elements... // → 8402 — Steam or other vapour generating boilers... // → ... // → 8471 — Automatic data-processing machines and units thereof // → ...
In an Express application, you might use it to validate product classifications:
// app.mjs (Express) import express from "express"; import { IsValid } from "isvalid-sdk"; const app = express(); app.use(express.json()); const iv = new IsValid({ apiKey: process.env.ISVALID_API_KEY }); app.post("/products", async (req, res) => { const { name, hsCode, origin, destination } = req.body; // Validate the HS code before saving the product const check = await iv.hsCode(hsCode); if (!check.valid) { return res.status(400).json({ error: "Invalid HS code", hint: "Provide a 2, 4, or 6-digit HS code (e.g. 8471 or 847150)", }); } // Use the validated data const product = await saveProduct({ name, hsCode: check.code, hsDescription: check.description, hsLevel: check.level, hsFormatted: check.formatted, chapterCode: check.chapter.code, chapterDescription: check.chapter.description, origin, destination, }); res.json({ success: true, product }); }); app.listen(3000);
list endpoint to build HS code picker dropdowns in your UI. Fetch chapters first, then load headings for the selected chapter, then subheadings — giving users a guided classification experience.6. cURL example
Validate a 4-digit heading:
curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://api.isvalid.dev/v0/hs-code?value=8471"
Validate a 6-digit subheading:
curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://api.isvalid.dev/v0/hs-code?value=847150"
Validate a 2-digit chapter:
curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://api.isvalid.dev/v0/hs-code?value=84"
List all headings in chapter 84:
curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://api.isvalid.dev/v0/hs-code/list?chapter=84&level=heading"
7. Understanding the response
Response for a chapter (2-digit code):
{ "valid": true, "code": "84", "level": "chapter", "description": "Nuclear reactors, boilers, machinery and mechanical appliances; parts thereof", "formatted": "84" }
Response for a heading (4-digit code):
{ "valid": true, "code": "8471", "level": "heading", "description": "Automatic data-processing machines and units thereof", "formatted": "84.71", "chapter": { "code": "84", "description": "Nuclear reactors, boilers, machinery and mechanical appliances; parts thereof" } }
Response for a subheading (6-digit code):
{ "valid": true, "code": "847150", "level": "subheading", "description": "Processing units other than those of subheading 8471.41 or 8471.49", "formatted": "8471.50", "chapter": { "code": "84", "description": "Nuclear reactors, boilers, machinery and mechanical appliances; parts thereof" } }
Invalid HS code:
{ "valid": false }
| Field | Type | Description |
|---|---|---|
| valid | boolean | Whether the HS code exists in the WCO nomenclature |
| code | string | The normalised HS code (2, 4, or 6 digits) |
| level | string | Classification level: "chapter", "heading", or "subheading" |
| description | string | Official WCO description of the commodity group |
| formatted | string | Dot-separated display format (e.g. "84.71" or "8471.50") |
| chapter.code | string | 2-digit chapter code (present for headings and subheadings) |
| chapter.description | string | Description of the parent chapter |
chapter object is only present for heading and subheading codes. When validating a 2-digit chapter code, the response does not include a nested chapter object since the code itself is already at the chapter level.8. Edge cases to handle
National extensions (HTS, CN, TARIC)
Countries extend the 6-digit HS code with national digits. The United States uses 10-digit HTS codes, the EU uses 8-digit CN codes (and 10-digit TARIC codes for trade measures). If your system stores these longer codes, extract the first 6 digits for HS validation and store the national extension separately.
// US HTS code: 8471.50.0150 (10 digits) const htsCode = "8471500150"; // Extract the 6-digit international HS code const hsCode = htsCode.slice(0, 6); // "847150" const result = await iv.hsCode(hsCode); // Validates against the international HS nomenclature // Store both for customs purposes const product = { hsCode: result.code, // "847150" — international htsCode: htsCode, // "8471500150" — US national description: result.description, };
Chapter vs. heading validation
A valid 2-digit chapter code does not guarantee that a 4-digit heading within it exists. For example, chapter 84 is valid, but 8499 may not be a valid heading. Always validate at the specific level you need.
// Chapter 84 is valid const chapter = await iv.hsCode("84"); console.log(chapter.valid); // true // But not every 4-digit combination in chapter 84 is a valid heading const invalid = await iv.hsCode("8499"); console.log(invalid.valid); // false // Always validate the exact code, not just the chapter const valid = await iv.hsCode("8471"); console.log(valid.valid); // true
Obsolete and reclassified codes
The WCO revises the HS nomenclature every five years. Codes that were valid in the 2017 edition may have been split, merged, or deleted in the 2022 edition. If your system stores historical HS codes, be aware that older codes may no longer validate against the current nomenclature. The API validates against the most recent HS edition. Consider storing the HS revision year alongside the code for audit purposes.
Input formatting
Users may enter HS codes with dots, spaces, or dashes (e.g. 84.71 or 8471.50). The API normalises input automatically — strip separators and pass the raw digits. The formatted field in the response gives you the canonical dot-separated display form.
9. Summary
See also
Validate HS codes instantly
Free tier includes 100 API calls per day. No credit card required. Validate any HS code and get descriptions, chapter info, and formatted display codes.