HTS Classification API: Developer Integration Guide

One POST request. Describe a product in plain English, get 10-digit HTS codes with duty rates, Section 301/232 tariffs, CBP ruling evidence, and FTA rates. JSON in, JSON out.

Quick start

1. Get an API key

Buy a credit pack. Each /classify call costs 1 credit. All other endpoints are free.

2. Make your first call

# Classify a product
curl -X POST https://htsapi.dev/v1/classify \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_API_KEY" \
  -d '{
    "description": "wireless bluetooth headphones with noise cancellation",
    "country_of_origin": "China"
  }'

3. Parse the response

{
  "confident_to": "8518.30",
  "candidates": [
    {
      "hts_code": "8518.30.20.00",
      "description": "Headphones and earphones...",
      "general_duty": "Free",
      "census_duties": {
        "country": "CHINA",
        "period": "2026-01",
        "total_effective_rate_pct": 16.9,
        "total_import_value_usd": 61910940,
        "total_duty_collected_usd": 10463040,
        "breakdown": [
          { "rp_code": "00", "rp_label": "MFN base rate", "effective_rate_pct": 0.0 },
          { "rp_code": "69", "rp_label": "Additional tariffs (Section 301/232/reciprocal)", "effective_rate_pct": 25.9 }
        ],
        "aggregate_import_value_usd": 443182895
      },
      "confidence": "high",
      "rationale": "This code covers headphones...",
      "rulings": [
        {
          "ruling_number": "N302512",
          "subject": "The tariff classification of audio headphones from China",
          "source_url": "https://rulings.cbp.gov/ruling/N302512"
        }
      ],
      "legal_notes": "--- SECTION XVI ---\n  Notes\n1 This section does not cover..."
    }
  ],
  "clarification": null,
  "model_used": "gemini-2.5-flash-lite"
}

Code examples

Python

import requests

resp = requests.post(
    "https://htsapi.dev/v1/classify",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "description": "stainless steel water bottle 500ml insulated",
        "country_of_origin": "China"
    }
)
data = resp.json()

# Top candidate
top = data["candidates"][0]
print(f"HTS: {top['hts_code']}")           # 7323.93.00.50
print(f"MFN duty: {top['general_duty']}")   # 2%
print(f"Confident to: {data['confident_to']}")

# Census effective duty rate (ground truth from CBP)
census = top["census_duties"]
if census:
    print(f"Effective rate: {census['total_effective_rate_pct']}% from {census['country']}")
    print(f"Import volume: ${census['total_import_value_usd']:,}")
    for b in census["breakdown"]:
        print(f"  {b['rp_label']}: {b['effective_rate_pct']}%")

Node.js

const resp = await fetch("https://htsapi.dev/v1/classify", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-API-Key": "YOUR_API_KEY"
  },
  body: JSON.stringify({
    description: "ceramic coffee mug handmade",
    country_of_origin: "Japan"
  })
});

const data = await resp.json();
const top = data.candidates[0];

console.log(top.hts_code);          // 6912.00.44.00
console.log(top.general_duty);      // 10%
console.log(top.census_duties);     // {total_effective_rate_pct: 3.5, ...}
console.log(data.confident_to);     // 6912.00

Key concepts

confident_to — how deep to trust

The deepest HTS prefix that all high/medium-confidence candidates agree on. This tells you where classification certainty stops.

ValueMeaningWhat you can do
6109.10.00.12Full 10-digit agreementFile with high confidence
6109.10.008-digit agreementDuty rate is known, statistical suffix varies
6109.106-digit agreementProduct type is clear, specifics needed
61094-digit (heading)Broad category only
nullNo agreementDescription is too vague, add detail

census_duties — ground truth duty rates

Effective duty rates from the US Census Bureau — what CBP actually collected at the port. Includes a breakdown by rate provision: MFN base rate, FTA/special programs, additional tariffs (Section 301/232/reciprocal), and exclusions. This is the real-world duty burden, not a theoretical calculation. Requires country_of_origin for country-specific rates; without it, returns aggregate across all countries.

legal_notes — official classification guidance

Section, Chapter, and Heading notes from the official tariff schedule — the legal reasoning behind why a product belongs in a specific classification. These are the General Rules of Interpretation (GRI) notes that customs brokers use for classification decisions.

clarification — when the API needs more info

When candidates diverge, the API returns a question to narrow it down. If the top candidate is high confidence, clarification is null. Use this in conversational flows — ask the user, re-classify with more detail.

All endpoints

MethodEndpointCostDescription
POST/v1/classify1 creditAI classification with full enrichment
GET/v1/hts/search?q=FreeSearch HTS codes by keyword or prefix
GET/v1/hts/{code}FreeSingle code detail with hierarchy
GET/v1/rulings/search?q=FreeSearch 134K CBP rulings
GET/v1/rulings/{id}FreeFull ruling text
GET/v1/billing/balanceFreeCheck remaining credits
GET/v1/statusFreeSystem health + data freshness

Only /classify costs credits. Everything else is free and unlimited (within rate limits).

Request fields

FieldTypeRequiredNotes
descriptionstringYes3-2000 chars. More detail = better results.
materialstringNoPrimary material (e.g., "cotton", "stainless steel")
categorystringNoProduct category hint
country_of_originstringNoRequired for Section 301/232 duties and FTA filtering
Tip: Always provide country_of_origin. Without it, additional duties won't appear and confidence tends to be lower. Country names work (e.g., "China", "Vietnam") — no need for ISO codes.

Tips for better results

Error handling

StatusCodeMeaning
401MISSING_API_KEYNo X-API-Key header
401INVALID_API_KEYKey not found or revoked
402NO_CREDITSCredits exhausted — buy more
403ACCOUNT_INACTIVEAccount disabled
422Validation errorDescription too short/long
503CLASSIFY_UNAVAILABLEModel or DB not ready

Rate limits

Default: 60 requests per minute per API key. If you need higher limits, contact us.

Data freshness

Check GET /v1/status for per-source freshness. HTS data updates on USITC revision (~monthly). CBP rulings update daily. FTA rates update weekly.

Ready to integrate? Buy credits and start classifying. Questions? [email protected]

Related guides