Classification
Customs classification endpoint - create a job, poll for status and retrieve the HS code, confidence score and GRI justification.
The classification endpoint provides the HS (Harmonized System) code for a product based on its text description and/or images. Processing is asynchronous: you create a job, then poll its status until results are available.
Create a classification job
POST
/v1/classify/jobsBody parameters (JSON)
| Parameter | Type | Required | Description |
|---|---|---|---|
content | string | Yes | Product description to classify |
image_urls | string[] | No | Product image URLs (max 5) |
locale | "fr" | "en" | "es" | No | Response language (default: "fr") |
Example request
curl -X POST https://api.thetradehub.eu/v1/classify/jobs \
-H "X-API-Key: th_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"content": "Wireless Bluetooth headphones with active noise cancellation, leather headband, USB-C charging",
"image_urls": ["https://example.com/product/headphones.jpg"],
"locale": "en"
}'import httpx
client = httpx.Client(
base_url="https://api.thetradehub.eu",
headers={"X-API-Key": "th_live_your_api_key"},
)
response = client.post("/v1/classify/jobs", json={
"content": "Wireless Bluetooth headphones with active noise cancellation, leather headband, USB-C charging",
"image_urls": ["https://example.com/product/headphones.jpg"],
"locale": "en",
})
job = response.json()
print(f"Job created: {job['id']} - Status: {job['status']}")const response = await fetch("https://api.thetradehub.eu/v1/classify/jobs", {
method: "POST",
headers: {
"X-API-Key": "th_live_your_api_key",
"Content-Type": "application/json",
},
body: JSON.stringify({
content: "Wireless Bluetooth headphones with active noise cancellation, leather headband, USB-C charging",
image_urls: ["https://example.com/product/headphones.jpg"],
locale: "en",
}),
});
const job = await response.json();
console.log(`Job created: ${job.id} - Status: ${job.status}`);Response (202 Accepted)
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "pending",
"created_at": "2026-02-24T10:30:00Z",
"updated_at": "2026-02-24T10:30:00Z"
}Get job status and results
GET
/v1/classify/jobs/{job_id}Path parameters
| Parameter | Type | Description |
|---|---|---|
job_id | string (UUID) | Job identifier returned at creation |
Possible statuses
| Status | Description |
|---|---|
pending | The job is queued |
processing | Processing is underway |
completed | Classification is complete, results are available |
failed | An error occurred during processing |
Example request
curl https://api.thetradehub.eu/v1/classify/jobs/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
-H "X-API-Key: th_live_your_api_key"import httpx
import time
client = httpx.Client(
base_url="https://api.thetradehub.eu",
headers={"X-API-Key": "th_live_your_api_key"},
)
job_id = "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
delay = 1
while True:
response = client.get(f"/v1/classify/jobs/{job_id}")
job = response.json()
if job["status"] == "completed":
print("Classification complete!")
print(job["result"])
break
elif job["status"] == "failed":
print(f"Error: {job.get('error')}")
break
time.sleep(delay)
delay = min(delay * 2, 5) # Progressive backoff, max 5sconst API_KEY = "th_live_your_api_key";
const jobId = "a1b2c3d4-e5f6-7890-abcd-ef1234567890";
async function pollJob(jobId) {
let delay = 1000;
while (true) {
const response = await fetch(
`https://api.thetradehub.eu/v1/classify/jobs/${jobId}`,
{ headers: { "X-API-Key": API_KEY } }
);
const job = await response.json();
if (job.status === "completed") {
console.log("Classification complete!", job.result);
return job;
}
if (job.status === "failed") {
throw new Error(job.error);
}
await new Promise((r) => setTimeout(r, delay));
delay = Math.min(delay * 2, 5000); // Progressive backoff, max 5s
}
}
const result = await pollJob(jobId);Full response (status: completed)
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "completed",
"result": {
"classification": {
"rankings": [
{
"rank": 1,
"hs_code": "8518.30.00",
"description": "Headphones and earphones",
"confidence": 0.94,
"gri_justification": "GIR 1 - The product is specifically covered by heading 8518...",
"validated": true
},
{
"rank": 2,
"hs_code": "8517.62.00",
"description": "Apparatus for reception, conversion and transmission of data",
"confidence": 0.15,
"gri_justification": "GIR 1 - Heading 8517 covers telecommunication apparatus. However, the headphones are more specifically covered by 8518. Rejected.",
"validated": true
}
]
},
"text": "Based on EU BTI rulings and TARIC nomenclature...",
"session_id": "sess_abc123"
},
"created_at": "2026-02-24T10:30:00Z",
"updated_at": "2026-02-24T10:30:05Z"
}Result structure
result.classification.rankings[]
Up to 3 classification proposals sorted by descending confidence.
| Field | Type | Description |
|---|---|---|
rank | number | Position in ranking (1 = best match) |
hs_code | string | HS code at 6, 8 or 10 digits |
description | string | Tariff heading description |
confidence | number | Confidence score between 0 and 1 |
gri_justification | string | Justification based on General Interpretive Rules |
validated | boolean | true if the code has been verified against the TARIC nomenclature |
result.text
Free-text explanation detailing the classification reasoning.
result.session_id
Session identifier to find this classification in The Trade Hub web interface.
Polling best practices
| Recommendation | Detail |
|---|---|
| Initial interval | 1 second |
| Backoff | Increase progressively (1s, 2s, 4s, 5s max) |
| Maximum timeout | 30 seconds - beyond that, consider the job failed |
| Status check | Always check status before accessing result |
Last updated