HTS Classification API for Logistics Software Integration (2026)
htsapi.dev gives logistics software a single REST endpoint that returns HTS codes (to 10 digits when the facts support it), Census Bureau figures for duty actually collected on past imports, and CBP ruling citations. No SDK, no enterprise contract, no six-month integration project.
Last updated: April 2026
Why Logistics Platforms Need Embedded HTS Classification
Every logistics platform that touches US imports needs HTS codes. Transportation management systems need them for landed-cost calculations. Warehouse management systems need them for inventory compliance. Customs filing software needs them on every entry. Freight forwarders need them to quote duties before goods ship.
Manual HTS lookup does not scale. A 3PL handling 50 clients with 500 SKUs each is looking at 25,000 products that need classification. Each product requires searching the USITC tariff schedule, cross-referencing CBP rulings, and calculating effective duty rates that include Section 301/232 surcharges. That is weeks of work per catalog refresh.
The alternative is embedding classification directly into your software. When a new product enters your system, classify it programmatically. When tariff rates change, reclassify affected SKUs. When a customer asks what duties to expect, answer from data instead of saying "we'll get back to you."
How htsapi.dev Integrates with Your Software
The integration is a single HTTP POST. Your application sends a product description and country of origin. The API returns the HTS code, duty figures, rationale, and supporting evidence. No SDK to install, no WSDL to parse, no client library to maintain.
↓
POST /v1/classifyhtsapi.dev
↓ JSON response
HTS code + duty rate + ruling citation
The request takes four fields:
# One call - that's the entire integration curl -X POST https://htsapi.dev/v1/classify \ -H "Content-Type: application/json" \ -H "X-API-Key: YOUR_API_KEY" \ -d '{ "description": "lithium-ion battery pack 48V for electric forklift", "country_of_origin": "China", "material": "lithium-ion cells, steel casing", "category": "industrial battery" }'
Any language that can make an HTTP request works: Python, Node.js, Java, Go, C#, PHP, Ruby. The response is JSON. Parse it, store the HTS code and duty figures, and route anything below high confidence to a reviewer.
API Response: What You Get
Every classification response includes:
{
"confident_to": "8507.60",
"candidates": [
{
"hts_code": "8507.60.00.20",
"description": "Lithium-ion storage batteries",
"general_duty": "3.4%",
"confidence": "high",
"rationale": "Classified under heading 8507 covering electric accumulators. Subheading 8507.60 specifically covers lithium-ion batteries. GRI 1 applies: the terms of the heading are definitive for this product.",
"rulings": [
{
"ruling_number": "N331847",
"subject": "The tariff classification of lithium-ion battery packs from China",
"source_url": "https://rulings.cbp.gov/ruling/N331847"
}
],
"census_duties": {
"country": "CHINA",
"period": "2025-06..2026-05",
"total_effective_rate_pct": 28.4,
"total_import_value_usd": 89200000,
"total_duty_collected_usd": 25332800,
"breakdown": [
{ "rp_code": "61", "rp_label": "Dutiable at the General (MFN) rate", "effective_rate_pct": 3.4 },
{ "rp_code": "69", "rp_label": "Dutiable under HTS chapter 99 (e.g. Section 301, 232), duty reported", "effective_rate_pct": 25.0 }
],
"aggregate_import_value_usd": 312500000
},
"legal_notes": "--- SECTION XVI ---\n Notes\n1 This section does not cover..."
}
],
"clarification": null
}
Key fields for logistics integration:
answer: the code at the depth the facts support; short of 10 digits, the options and the question that decides themhts_code: each candidate's 10-digit code, for review before customs filingconfidence: high, medium, or low. Route low-confidence results to a human reviewer.rationale: explains the classification logic. Store this for audit defense.rulings: CBP ruling citations that support the classification. Link directly to rulings.cbp.gov.census_duties: duty actually collected on past imports, from Census Bureau import data. A historical average, not a specific shipment's duty.clarification: if the API needs more detail, it asks a follow-up question. Use this for interactive classification flows.
Batch Classification for Product Catalogs
Most logistics software needs to classify entire product catalogs, not one item at a time. Loop through your catalog, call the API for each product (there is no batch endpoint), and store the results. The rate limit is 10 requests per minute per API key.
import requests import time import json API_KEY = "YOUR_API_KEY" ENDPOINT = "https://htsapi.dev/v1/classify" # Your product catalog - from database, CSV, ERP export, etc. products = [ {"sku": "FLT-4800", "desc": "lithium-ion battery 48V forklift", "origin": "China"}, {"sku": "PPE-VEST-HV", "desc": "high-visibility safety vest polyester", "origin": "Vietnam"}, {"sku": "CONV-BELT-10", "desc": "rubber conveyor belt 10m industrial", "origin": "Germany"}, # ... thousands more ] results = [] for i, product in enumerate(products): try: resp = requests.post(ENDPOINT, headers={"X-API-Key": API_KEY}, json={ "description": product["desc"], "country_of_origin": product["origin"] }, timeout=90 ) if resp.status_code == 429: # Rate limited - wait and retry time.sleep(10) resp = requests.post(ENDPOINT, headers={"X-API-Key": API_KEY}, json={"description": product["desc"], "country_of_origin": product["origin"]}, timeout=90 ) data = resp.json() top = data["candidates"][0] results.append({ "sku": product["sku"], "hts_code": top["hts_code"], "confidence": top["confidence"], "duty_rate": (top.get("census_duties") or {}).get("total_effective_rate_pct"), "rationale": top["rationale"] }) # Progress print(f"[{i+1}/{len(products)}] {product['sku']}: {top['hts_code']} ({top['confidence']})") # Respect rate limit: 10/min = 1 every 6 seconds time.sleep(6) except Exception as e: print(f"ERROR {product['sku']}: {e}") results.append({"sku": product["sku"], "error": str(e)}) # Save results with open("classified_catalog.json", "w") as f: json.dump(results, f, indent=2) print(f"\nDone. {len([r for r in results if 'hts_code' in r])}/{len(products)} classified.")
Duty Figures from Census Data
Getting the HTS code is only half the problem. Logistics software also needs to answer: what duty should the importer expect?
The statutory duty rate in the HTS schedule is just the starting point. Real-world duty depends on the country of origin and which additional tariff provisions apply. For lithium-ion batteries (8507.60.00) from China, on 2026-10-03:
- MFN base rate: 3.4% (the published "general" rate)
- Section 301: +25.0% (9903.91.06, on USITC's China Tariffs table)
- Schedule rate + Section 301 list (USITC): 28.4% (other Chapter 99 lines, such as Section 232, not included; the API does not compute these)
- Paid by importers, July 2026: 35.8% on entries that paid Chapter 99 duties (Census, 6-digit 8507.60)
The census_duties field on each candidate provides this breakdown when Census has data for the subheading. It comes from US Census Bureau import data: duties actually collected divided by dutiable value, per country: what importers paid in the latest published month (the 10-digit line when there is enough trade, else the 6-digit subheading), last 3 months in recent_months, and the 12-month 6-digit average. Past imports, not the duty a particular shipment will owe.
The rate provision breakdown shows each component:
| RP Code | Label | What It Covers |
|---|---|---|
61 | Dutiable at the General (MFN) rate | Standard Most Favored Nation duty |
69 | Dutiable under HTS chapter 99 | Section 301, Section 232, reciprocal and other additional duties |
64 | Dutiable at a Special (preferential) rate | Entries at a reduced FTA or program rate |
10-19 | Free | Entries free under the general or special column, other legislation, or Chapter 99 |
00 | Bonded warehouse or FTZ | Duty not applicable |
This gives logistics software a duty estimate grounded in what CBP actually collected, not a rate table that ignores Section 301. It is still an estimate from historical averages; it does not calculate the duty for a particular shipment.
Compared to Enterprise Solutions
Enterprise trade management platforms like E2open, Descartes, and Thomson Reuters ONESOURCE offer HTS classification as part of broader suites. If you need a full global trade management platform with denied-party screening, license management, and multi-country compliance, those platforms make sense. If you need HTS classification embedded in your existing software, the comparison looks different.
| Criteria | htsapi.dev | E2open / Descartes / Thomson Reuters |
|---|---|---|
| Setup time | Minutes. Get API key, make first call. | Weeks to months. Sales process, SOW, implementation project. |
| Pricing | Pay per call. $10 for 100 credits ($0.10/call) or $50 for 1,000 credits ($0.05/call). | Enterprise license. $50K-$500K+/year typical. |
| Integration | 1 REST endpoint. JSON in, JSON out. | SDK, SOAP/XML, or custom connector. Often requires middleware. |
| Data freshness | HTS schedule and CBP rulings weekly; Census figures cover the latest 3 published months and a 12-month average (about two months behind). | Varies. Some update quarterly or on request. |
| US tariff depth | Deep. 10-digit HTS, 220K+ rulings, Census duty-collected figures including Section 301/232. | Broad. Global coverage but US-specific detail varies. |
| Coverage | US HTS only (HS6 maps internationally via WCO system). | Multi-country tariff schedules. |
| Minimum commitment | None. Start with 100 credits ($10), try it. | Annual contract, often multi-year. |
| Support | Email, developer docs, API reference. | Dedicated account team, professional services. |
Integration Patterns
Logistics software integrates HTS classification in four common patterns. Choose based on your workflow.
1. Classify on new product (your webhook handler)
When a new SKU enters your system (from ERP sync, catalog upload, or manual entry), have your own webhook or event handler call /v1/classify and stores the result. The HTS code is ready before anyone needs it.
# Webhook handler - triggered when new product is created def on_new_product(product): resp = requests.post("https://htsapi.dev/v1/classify", headers={"X-API-Key": API_KEY}, json={ "description": product.description, "country_of_origin": product.origin_country }) data = resp.json() top = data["candidates"][0] product.hts_code = top["hts_code"] product.duty_rate = (top.get("census_duties") or {}).get("total_effective_rate_pct") product.classification_confidence = top["confidence"] product.save()
2. Scheduled batch reclassification
Run a nightly or weekly job that reclassifies products whose HTS codes are older than 90 days, or when tariff rates change. This catches products affected by new trade actions.
3. On-demand at customs filing
When preparing a customs entry, classify (or verify an existing classification). Show the agent the HTS code, confidence level, and ruling citation. If confidence is low, flag for manual review before filing.
4. E-commerce integration
For Shopify, WooCommerce, or custom storefronts that ship internationally: classifying takes 15-35 seconds, so do it ahead of checkout. Use the new-product pattern to pre-classify on product creation, then look up stored codes and Census figures at checkout for a duty estimate.
Getting Started
Three steps to embed HTS classification in your logistics software.
1. Get an API key
Buy a credit pack. $10 for 100 credits or $50 for 1,000 credits. Each /v1/classify call costs 1 credit. All other endpoints (HTS search, rulings, status) are free with a key.
2. Test with curl
curl -X POST https://htsapi.dev/v1/classify \ -H "Content-Type: application/json" \ -H "X-API-Key: YOUR_API_KEY" \ -d '{"description": "corrugated cardboard shipping boxes", "country_of_origin": "Canada"}'
3. Integrate into your application
import requests def classify_product(description, country): """Classify a product and return HTS code + duty rate.""" resp = requests.post( "https://htsapi.dev/v1/classify", headers={"X-API-Key": "YOUR_API_KEY"}, json={"description": description, "country_of_origin": country} ) resp.raise_for_status() data = resp.json() top = data["candidates"][0] return { "hts_code": top["hts_code"], "duty_rate_pct": (top.get("census_duties") or {}).get("total_effective_rate_pct"), "confidence": top["confidence"], "ruling": top["rulings"][0]["ruling_number"] if top["rulings"] else None } # Use in your TMS/WMS result = classify_product("stainless steel pallet racking 3-tier", "Mexico") print(result) # {'hts_code': '7326.90.86.88', 'duty_rate_pct': 0.0, 'confidence': 'high', 'ruling': 'N298431'}
For full integration details, error handling, and all available endpoints, see the Developer Integration Guide.
Frequently Asked Questions
How fast is the API?
A classification typically takes 15-35 seconds, sometimes up to about 60. An AI agent searches the current USITC schedule and over 220,000 CBP rulings, reads rulings and legal notes, and applies GRI analysis; products that need more of that take longer. The API processes one product per call, so for catalogs, factor in the 10 requests/minute rate limit.
Can I classify products in bulk?
Yes, one call per product: there is no batch endpoint. Loop through your product catalog and call /v1/classify for each item. Each call takes 15-35 seconds, so one at a time a 10,000-SKU catalog takes about 60-115 hours; the rate limit of 10 requests per minute allows a few calls in parallel. The Python example above includes rate-limit handling and progress tracking.
Do you support non-US tariff schedules?
Currently the API covers the US Harmonized Tariff Schedule only. Classifications go down to 10-digit US HTS codes when the facts support it. The first 6 digits follow the WCO Harmonized System used by 200+ countries, so they map internationally. Country-specific subdivisions beyond HS6 are US-only.
How often is the tariff data updated?
The USITC HTS schedule and our copy of the CBP rulings are refreshed weekly. Census Bureau duty figures are the latest 3 months Census has published, about two months behind, with a 12-month average. Check GET /v1/status for HTS and rulings freshness timestamps.
What about uptime and SLA?
There is no SLA or uptime guarantee. The GET /v1/status endpoint reports data freshness for the HTS schedule and CBP rulings. Questions: contact us.