Trade Classification Engine API

v1.0

Assign HS tariff codes to products with the AI-powered Trade Classification Engine.

Overview

The Trade Classification Engine API is a decision engine, not a simple lookup table. Send a product description and receive ranked HS code candidates with confidence scores, reasoning, and follow-up questions when the input is ambiguous.

🎯 Progressive

Narrows down codes across multiple calls with follow-up questions

🌍 Multi-Region

Covers GLOBAL (HS6), UK, US, and EU tariff schedules

📊 Explainable

Every result includes confidence scores and reasoning

💡 Perfect For

  • • Customs brokers and freight forwarders
  • • Cross-border e-commerce platforms
  • • ERP and trade compliance systems
  • • Supply chain and logistics software

Quick Start

Base URL

https://api.cparse.com/trade/v1

Authentication

http
X-API-Key: YOUR_API_KEY

Get your API key from the dashboard. New accounts include free credit, no credit card required.

Quick Example

Classify a product description:

cURL

bash
curl --request POST \
  --url https://api.cparse.com/trade/v1/classify \
  --header 'Content-Type: application/json' \
  --header 'X-API-Key: YOUR_API_KEY' \
  --data '{
    "description": "blue cotton t-shirt",
    "region": "GLOBAL"
  }'

Refining with Context

If the initial response includes follow-up questions, re-send with answers:

bash
curl --request POST \
  --url https://api.cparse.com/trade/v1/classify \
  --header 'Content-Type: application/json' \
  --header 'X-API-Key: YOUR_API_KEY' \
  --data '{
    "description": "men'\''s knitted cotton t-shirt 100%",
    "region": "GLOBAL",
    "context": {
      "construction": "knit",
      "gender": "men",
      "material": "100% cotton"
    }
  }'

Endpoints

POST/classify

Classify a product description into HS tariff codes.

Request Body (JSON)

FieldTypeRequiredDescription
descriptionstringYesProduct description (3–2000 chars)
regionstringNoTarget tariff schedule. One of GLOBAL, UK, US, EU. Defaults to GLOBAL.
contextobjectNoAnswers to previous follow-up questions. Each key is the attribute from a follow_up_questions item; each value is the chosen answer. Omit on the first call.
heading_hintstringNo4-digit HS heading to lock the search to (e.g. 6109). Pass the top_heading value from the previous response. This prevents the classifier from drifting into unrelated product categories across rounds.

The context field

When the response contains follow_up_questions, collect answers from the user and send them back in the next request. The key is the attribute name from the question; the value is the user's answer (one of options, or free text when options is null).

json
// Response contains:
"top_heading": "6109",
"follow_up_questions": [
  {
    "attribute": "gender_age",
    "question": "Who is this garment intended for?",
    "options": ["men", "women", "boys", "girls", "unisex"]
  },
  {
    "attribute": "material",
    "question": "What is the primary material or composition?",
    "options": ["cotton", "man-made fibres", "wool", "silk"]
  }
]

// Next request — attribute becomes context key, top_heading becomes heading_hint:
{
  "description": "blue cotton t-shirt",
  "region": "GLOBAL",
  "heading_hint": "6109",
  "context": {
    "gender_age": "men",
    "material": "cotton"
  }
}

Accumulate answers across rounds — include all previous answers plus any new ones in each subsequent request.

GET/regions

List supported classification regions. No authentication required.

Response Fields

FieldTypeDescription
statusstringfinal — confident result, no further input needed. needs_more_info — ambiguous, check follow_up_questions.
candidatesarrayRanked list of HS code candidates, best match first
candidates[].codestringHS or tariff code
candidates[].descriptionstringOfficial tariff description
candidates[].confidencenumberScore from 0.0 to 1.0. Above 0.85 is considered confident.
candidates[].duty_ratestring | nullApplicable duty rate (available for UK, US, EU regions)
candidates[].reasoningstring | nullWhy this code was selected
follow_up_questionsarrayEmpty when status is final. Otherwise contains questions whose answers would narrow the classification.
follow_up_questions[].attributestringThe attribute name. Use this as the key in context in the next request.
follow_up_questions[].questionstringHuman-readable question to present to the user
follow_up_questions[].optionsstring[] | nullSuggested answers. Null means free text is expected.
regionstringRegion used for this classification
top_headingstring | nullThe dominant 4-digit HS heading among the top candidates. Pass this back as heading_hint in subsequent requests to lock the search space and prevent category drift.
explanationstring | nullOverall reasoning summary. Populated when status is final.

Multi-turn flow

When you receive needs_more_info, present the questions to your user, then send a new request with their answers in context. Keep the same description and region across rounds.

json
// Round 1 — initial request
POST /classify
{
  "description": "blue cotton t-shirt",
  "region": "US"
}

// Round 1 — response (ambiguous)
{
  "status": "needs_more_info",
  "top_heading": "6109",              // <-- save this
  "candidates": [ ... ],
  "follow_up_questions": [
    {
      "attribute": "gender_age",
      "question": "Who is this garment intended for?",
      "options": ["men", "women", "boys", "girls", "unisex"]
    },
    {
      "attribute": "material",
      "question": "What is the primary material or composition?",
      "options": ["cotton", "man-made fibres", "wool", "silk"]
    }
  ]
}

// Round 2 — pass top_heading back as heading_hint to lock the search space
POST /classify
{
  "description": "blue cotton t-shirt",
  "region": "US",
  "heading_hint": "6109",            // <-- locks search to heading 6109
  "context": {
    "gender_age": "men",
    "material": "cotton"
  }
}

// Round 2 — response (final, stays within heading 6109)
{
  "status": "final",
  "top_heading": "6109",
  "candidates": [
    {
      "code": "6109100012",
      "description": "Men's T-shirts, of cotton",
      "confidence": 0.94,
      "heading": "6109",
      "duty_rate": "16.5%",
      "reasoning": "Matches men's standard cotton T-shirt under heading 6109"
    }
  ],
  "follow_up_questions": [],
  "explanation": "Classified as men's cotton T-Shirt under HTS 6109.10"
}

Example Response (Ambiguous)

json
{
  "status": "needs_more_info",
  "candidates": [
    {
      "code": "610910",
      "description": "T-shirts, knitted, of cotton",
      "confidence": 0.78,
      "heading": "6109",
      "duty_rate": "12%",
      "reasoning": "Strong match for cotton t-shirt under chapter 61"
    },
    {
      "code": "610990",
      "description": "T-shirts, other materials",
      "confidence": 0.42,
      "reasoning": "Possible if material is not 100% cotton"
    }
  ],
  "follow_up_questions": [
    {
      "attribute": "construction",
      "question": "Is the garment knitted or woven?",
      "options": ["knitted", "woven"]
    },
    {
      "attribute": "gender",
      "question": "Is it for men, women, or children?",
      "options": ["men", "women", "boys", "girls", "unisex"]
    }
  ],
  "region": "GLOBAL",
  "explanation": null
}

Example Response (Final)

json
{
  "status": "final",
  "candidates": [
    {
      "code": "610910",
      "description": "T-shirts, knitted, of cotton",
      "confidence": 0.94,
      "heading": "6109",
      "duty_rate": "12%",
      "reasoning": "Matches knitted cotton garment under chapter 61"
    }
  ],
  "follow_up_questions": [],
  "region": "GLOBAL",
  "explanation": "Classified as knitted cotton t-shirt under HS heading 6109"
}

Plans

FeatureBasicProUltraMega
Max description length200 chars500 chars1,000 chars2,000 chars
RegionsGLOBALAll 4All 4All 4
Max candidates351010

API key users always get Mega plan limits. The balance is the only limiting factor.

Error Handling

StatusMeaning
400Invalid region or malformed request body
401Missing or invalid API key
402Insufficient balance
413Description too long or region not allowed for your plan
422Validation error (missing required fields, etc.)
503Tariff data not loaded (temporary)

Code Examples

Python

python
import requests

url = "https://api.cparse.com/trade/v1/classify"
headers = {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
}

# Round 1 — initial classification
response = requests.post(url, headers=headers, json={
    "description": "blue cotton t-shirt",
    "region": "US",
})
data = response.json()

if data["status"] == "final":
    print(data["candidates"][0]["code"])
else:
    # Save top_heading to lock the search space in the next round
    heading_hint = data.get("top_heading")

    # Collect answers to follow-up questions
    # attribute field = context key, user answer = context value
    answers = {}
    for q in data["follow_up_questions"]:
        answers[q["attribute"]] = q["options"][0] if q["options"] else "unknown"

    response = requests.post(url, headers=headers, json={
        "description": "blue cotton t-shirt",
        "region": "US",
        "heading_hint": heading_hint,   # prevents category drift
        "context": answers,
    })
    refined = response.json()
    print(refined["status"])
    print(refined["candidates"][0]["code"])

JavaScript / Node.js

javascript
const response = await fetch("https://api.cparse.com/trade/v1/classify", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    description: "blue cotton t-shirt",
    region: "GLOBAL",
  }),
});

const data = await response.json();
console.log(data.status);
console.log(data.candidates);

if (data.follow_up_questions.length > 0) {
  console.log("Follow-up questions:", data.follow_up_questions);
}

Tips

Be specific

Include material, construction method, intended use, and gender when possible. "Men's 100% cotton knitted t-shirt" will give better results than "t-shirt".

Use the follow-up flow

When the API returns needs_more_info, use the follow-up questions to collect answers and send them back in the context field for a more confident result.

Choose the right region

Use GLOBAL for generic 6-digit HS codes. Use country-specific regions (UK, US, EU) when you need full tariff codes with duty rates.