Batch Classification
Batch classification endpoint - classify up to 1,000 products in a single request with real-time progress tracking.
The batch classification endpoint allows you to classify up to 1,000 products in a single request. Like single classification, processing is asynchronous with real-time progress tracking.
Create a batch classification
POST
/v1/classify/batchBody parameters (JSON)
| Parameter | Type | Required | Description |
|---|---|---|---|
items | object[] | Yes | List of products to classify (1 to 1,000) |
items[].id | string | No | Custom identifier to correlate results |
items[].content | string | Yes | Product description |
items[].image_urls | string[] | No | Product image URLs |
locale | "fr" | "en" | "es" | No | Response language (default: "fr") |
Example request
curl -X POST https://api.thetradehub.eu/v1/classify/batch \
-H "X-API-Key: th_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"items": [
{
"id": "SKU-001",
"content": "Wireless Bluetooth headphones with active noise cancellation"
},
{
"id": "SKU-002",
"content": "Men organic cotton t-shirt, size L"
},
{
"id": "SKU-003",
"content": "Extra virgin olive oil, 750ml glass bottle, origin Spain"
}
],
"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/batch", json={
"items": [
{"id": "SKU-001", "content": "Wireless Bluetooth headphones with active noise cancellation"},
{"id": "SKU-002", "content": "Men organic cotton t-shirt, size L"},
{"id": "SKU-003", "content": "Extra virgin olive oil, 750ml glass bottle, origin Spain"},
],
"locale": "en",
})
batch = response.json()
print(f"Batch created: {batch['id']} - {batch['total']} items")const response = await fetch("https://api.thetradehub.eu/v1/classify/batch", {
method: "POST",
headers: {
"X-API-Key": "th_live_your_api_key",
"Content-Type": "application/json",
},
body: JSON.stringify({
items: [
{ id: "SKU-001", content: "Wireless Bluetooth headphones with active noise cancellation" },
{ id: "SKU-002", content: "Men organic cotton t-shirt, size L" },
{ id: "SKU-003", content: "Extra virgin olive oil, 750ml glass bottle, origin Spain" },
],
locale: "en",
}),
});
const batch = await response.json();
console.log(`Batch created: ${batch.id} - ${batch.total} items`);Response (202 Accepted)
{
"id": "batch_f1e2d3c4-b5a6-7890-abcd-ef1234567890",
"status": "processing",
"total": 3,
"completed": 0,
"failed": 0,
"progress": 0,
"created_at": "2026-02-24T10:30:00Z",
"updated_at": "2026-02-24T10:30:00Z"
}Get batch status
GET
/v1/classify/batch/{batch_id}Path parameters
| Parameter | Type | Description |
|---|---|---|
batch_id | string (UUID) | Batch identifier returned at creation |
Possible statuses
| Status | Description |
|---|---|
processing | The batch is being processed |
completed | All items have been processed |
partial | The batch finished but some items failed |
failed | The entire batch failed |
Polling example
curl https://api.thetradehub.eu/v1/classify/batch/batch_f1e2d3c4-b5a6-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"},
)
batch_id = "batch_f1e2d3c4-b5a6-7890-abcd-ef1234567890"
delay = 2
while True:
response = client.get(f"/v1/classify/batch/{batch_id}")
batch = response.json()
print(f"Progress: {batch['progress'] * 100:.0f}% ({batch['completed']}/{batch['total']})")
if batch["status"] in ("completed", "partial"):
for item in batch["items"]:
if item["status"] == "completed":
hs = item["result"]["classification"]["rankings"][0]["hs_code"]
print(f" {item['id']} → {hs}")
else:
print(f" {item['id']} → Error: {item.get('error')}")
break
elif batch["status"] == "failed":
print(f"Batch error: {batch.get('error')}")
break
time.sleep(delay)
delay = min(delay * 1.5, 10)const API_KEY = "th_live_your_api_key";
const batchId = "batch_f1e2d3c4-b5a6-7890-abcd-ef1234567890";
async function pollBatch(batchId) {
let delay = 2000;
while (true) {
const response = await fetch(
`https://api.thetradehub.eu/v1/classify/batch/${batchId}`,
{ headers: { "X-API-Key": API_KEY } }
);
const batch = await response.json();
console.log(`Progress: ${Math.round(batch.progress * 100)}% (${batch.completed}/${batch.total})`);
if (batch.status === "completed" || batch.status === "partial") {
for (const item of batch.items) {
if (item.status === "completed") {
const hs = item.result.classification.rankings[0].hs_code;
console.log(` ${item.id} → ${hs}`);
} else {
console.log(` ${item.id} → Error: ${item.error}`);
}
}
return batch;
}
if (batch.status === "failed") {
throw new Error(batch.error);
}
await new Promise((r) => setTimeout(r, delay));
delay = Math.min(delay * 1.5, 10000);
}
}
const result = await pollBatch(batchId);Full response (status: completed)
Each item contains the same result structure as single classification.
{
"id": "batch_f1e2d3c4-b5a6-7890-abcd-ef1234567890",
"status": "completed",
"total": 3,
"completed": 3,
"failed": 0,
"progress": 1.0,
"items": [
{
"id": "SKU-001",
"status": "completed",
"result": {
"classification": {
"rankings": [
{
"rank": 1,
"hs_code": "8518.30.00",
"description": "Headphones and earphones",
"confidence": 0.94,
"gri_justification": "GIR 1 - Heading 8518 specifically covers headphones and earphones.",
"validated": true
}
]
},
"text": "Based on EU BTI rulings and the TARIC nomenclature...",
"session_id": "sess_abc123"
}
},
{
"id": "SKU-002",
"status": "completed",
"result": {
"classification": {
"rankings": [
{
"rank": 1,
"hs_code": "6109.10.00",
"description": "T-shirts and singlets, of cotton",
"confidence": 0.97,
"gri_justification": "GIR 1 - The product is a cotton t-shirt, specifically covered by heading 6109.",
"validated": true
}
]
},
"text": "Classification under heading 6109 for knitted articles...",
"session_id": "sess_def456"
}
},
{
"id": "SKU-003",
"status": "completed",
"result": {
"classification": {
"rankings": [
{
"rank": 1,
"hs_code": "1509.10.90",
"description": "Virgin olive oil",
"confidence": 0.96,
"gri_justification": "GIR 1 - Heading 1509 covers olive oil. Subheading 1509.10: virgin oil.",
"validated": true
}
]
},
"text": "Classification under heading 1509 for olive oils...",
"session_id": "sess_ghi789"
}
}
],
"created_at": "2026-02-24T10:30:00Z",
"updated_at": "2026-02-24T10:30:45Z"
}Response structure
| Field | Type | Description |
|---|---|---|
id | string | Unique batch identifier |
status | string | Overall batch status |
total | number | Total number of items |
completed | number | Number of successfully processed items |
failed | number | Number of failed items |
progress | number | Completion ratio (0.0 to 1.0) |
items | object[] | Per-item results - same structure as single classification |
Persistent results
Results are permanently accessible via the same endpoint:
GET /v1/classify/batch/{batch_id}- During processing / first 24h — served from cache, includes live progress
- After 24h — transparent fallback to the database, full results always available
No need to store results client-side. Retrieve any past batch at any time using the original batch_id.
Best practices
| Recommendation | Detail |
|---|---|
| Batch size | Maximum 1,000 items per request |
| Polling | Start at 2s interval, increase progressively |
| Identifiers | Use items[].id to correlate with your data |
| Partial error handling | Check status of each item in items |
| Timeout | Allow 30s + 5s per item for large batches |
| Result storage | Not required — results are permanently accessible via batch_id |
Last updated