HS Code Validation in Python — 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 Python — 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. Python code example
Using the isvalid-sdk Python SDK or the requests library. Install with pip install isvalid-sdk or pip install requests.
# hs_validator.py import os from isvalid_sdk import IsValidConfig, create_client iv = create_client(IsValidConfig(api_key=os.environ["ISVALID_API_KEY"])) # ── Validate a heading (4-digit HS code) ──────────────────────────────── result = iv.hs_code("8471") if not result["valid"]: print("Invalid HS code") else: print(f"Valid HS code — {result['level']}") print(f"Code: {result['code']}") print(f"Description: {result['description']}") print(f"Formatted: {result['formatted']}") print(f"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) ───────────────────────────── sub = iv.hs_code("847150") print(sub["description"]) # → Processing units other than those of subheading 8471.41 or 8471.49 # ── Validate a chapter (2-digit HS code) ──────────────────────────────── chapter = iv.hs_code("84") print(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 headings = iv.hs_code.list(chapter="84", level="heading") for h in headings: print(f"{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 a Flask application, you might use it to validate product classifications:
# app.py (Flask) import os import requests as http from flask import Flask, request, jsonify app = Flask(__name__) API_KEY = os.environ["ISVALID_API_KEY"] BASE_URL = "https://api.isvalid.dev" def validate_hs_code(code: str) -> dict: resp = http.get( f"{BASE_URL}/v0/hs-code", params={"value": code}, headers={"Authorization": f"Bearer {API_KEY}"}, ) resp.raise_for_status() return resp.json() @app.post("/products") def create_product(): data = request.get_json() # Validate the HS code before saving the product try: check = validate_hs_code(data["hs_code"]) except http.RequestException: return jsonify(error="HS code validation service unavailable"), 502 if not check["valid"]: return jsonify( error="Invalid HS code", hint="Provide a 2, 4, or 6-digit HS code (e.g. 8471 or 847150)", ), 400 # Use the validated data product = save_product( name=data["name"], hs_code=check["code"], hs_description=check["description"], hs_level=check["level"], hs_formatted=check["formatted"], chapter_code=check.get("chapter", {}).get("code"), chapter_description=check.get("chapter", {}).get("description"), origin=data.get("origin"), destination=data.get("destination"), ) return jsonify(success=True, product=product)
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) hts_code = "8471500150" # Extract the 6-digit international HS code hs_code = hts_code[:6] # "847150" result = iv.hs_code(hs_code) # Validates against the international HS nomenclature # Store both for customs purposes product = { "hs_code": result["code"], # "847150" — international "hts_code": hts_code, # "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 chapter = iv.hs_code("84") print(chapter["valid"]) # True # But not every 4-digit combination in chapter 84 is a valid heading invalid = iv.hs_code("8499") print(invalid["valid"]) # False # Always validate the exact code, not just the chapter valid = iv.hs_code("8471") print(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.