Classement douanier UE et Chine
Connectez votre système, lancez un classement UE ou Chine et récupérez le code, les sources et la version tarifaire appliquée.
Cette API classe un produit dans la nomenclature de la juridiction demandée :
| Juridiction | Résultat | Route |
|---|---|---|
| Union européenne | NC à 8 chiffres et TARIC à 10 chiffres | /v1/classify |
| Chine | Code marchandise GACC à 10 chiffres et suffixe CIQ à 3 chiffres | /v1/classify-china |
Le traitement est asynchrone : créez une tâche de classement, appelée job dans l’API, interrogez son statut, puis lisez result lorsqu’elle est terminée.
Le contrat OpenAPI public permet de
générer un client typé. L’URL de production est https://api.thetradehub.eu.
Référentiels officiels 2026
| Juridiction | Référence officielle | Application |
|---|---|---|
| Union européenne | Règlement d’exécution (UE) 2025/1926, NC 2026 | Depuis le 1er janvier 2026 |
| Chine | Plan d’ajustement tarifaire 2026, annonce 2025 n° 11 | Depuis le 1er janvier 2026 |
| Chine | Consultation tarifaire de l’Administration générale des douanes | Vérification opérationnelle des codes et taux |
La réponse reste la référence technique pour un dossier donné : lisez toujours provenance.applied_tariff_date, provenance.tariff_date_basis et provenance.corpus_versions. La valeur latest_available_snapshot indique que le dernier instantané disponible a été appliqué pendant une période de bascule tarifaire.
Démarrage en 3 appels
1. Configurez la clé
Ouvrez votre organisation depuis l’espace Organisations, créez une clé dans la rubrique Développeur, puis attribuez-lui le périmètre d’autorisation requis :
| Route | Périmètre requis |
|---|---|
/v1/classify/... | classify |
/v1/classify-china/... | classify_china |
Conservez la clé sur votre serveur. Ne l’exposez jamais dans un navigateur ou une application mobile.
export TTH_API_KEY="th_live_votre_cle_api"2. Créez une tâche
curl -sS -X POST "https://api.thetradehub.eu/v1/classify/jobs" \
-H "X-API-Key: $TTH_API_KEY" \
-H "Idempotency-Key: produit-4821-eu-20260910" \
-H "Content-Type: application/json" \
-d '{
"content": "Casque audio Bluetooth sans fil avec réduction de bruit active et recharge USB-C",
"analysis_mode": "operational",
"locale": "fr",
"classification_date": "2026-09-10"
}'Une nouvelle demande retourne 202 Accepted. Le rejeu de la même demande avec le même Idempotency-Key retourne la tâche existante avec 200 OK.
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "pending",
"analysis_mode": "operational",
"created_at": "2026-09-10T10:30:00Z",
"updated_at": "2026-09-10T10:30:00Z"
}3. Récupérez le résultat
Utilisez la même famille de route que lors de la création.
curl -sS "https://api.thetradehub.eu/v1/classify/jobs/a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
-H "X-API-Key: $TTH_API_KEY"Interrogez la tâche après 1, 2, 4 secondes, puis toutes les 5 secondes jusqu’à completed ou failed.
Exemple complet prêt à utiliser
Ce client Python fonctionne pour les deux juridictions et vérifie les erreurs HTTP.
import os
import time
import uuid
import httpx
BASE_URL = "https://api.thetradehub.eu"
API_KEY = os.environ["TTH_API_KEY"]
ROUTES = {"eu": "classify", "china": "classify-china"}
def classify_product(
content: str,
jurisdiction: str = "eu",
analysis_mode: str = "operational",
classification_date: str | None = None,
) -> dict:
route = ROUTES[jurisdiction]
timeout_seconds = 12 * 60 if analysis_mode == "operational" else 18 * 60
headers = {
"X-API-Key": API_KEY,
"Idempotency-Key": str(uuid.uuid4()),
}
payload = {
"content": content,
"analysis_mode": analysis_mode,
"locale": "fr",
}
if classification_date:
payload["classification_date"] = classification_date
with httpx.Client(base_url=BASE_URL, headers=headers, timeout=30) as client:
response = client.post(f"/v1/{route}/jobs", json=payload)
response.raise_for_status()
job_id = response.json()["id"]
deadline = time.monotonic() + timeout_seconds
delay = 1
while time.monotonic() < deadline:
response = client.get(f"/v1/{route}/jobs/{job_id}")
response.raise_for_status()
job = response.json()
if job["status"] == "completed":
return job["result"]
if job["status"] == "failed":
raise RuntimeError(job.get("error", "Classification failed"))
time.sleep(delay)
delay = min(delay * 2, 5)
raise TimeoutError(f"Tâche {job_id} toujours en cours")
result = classify_product(
"Pompe centrifuge en acier inoxydable pour liquides alimentaires",
jurisdiction="china",
classification_date="2026-09-10",
)
decision = result["classification"]
china = result.get("china") or decision.get("china")
print(china.get("code13") or china["code10"])
print(decision["provenance"]["applied_tariff_date"])Où se trouvent les données
| Donnée | UE | Chine |
|---|---|---|
| Code recommandé | result.classification.recommended_code | result.china.code13 ou result.china.code10 |
| Code SH commun | 6 premiers chiffres du code | result.china.hs6 |
| Confiance | result.classification.candidates[0].confidence | result.china.confidence |
| Justification | result.classification.candidates[0].legal_position | result.china.justification |
| Statut juridique | result.classification.status | result.classification.status |
| Date tarifaire appliquée | result.classification.provenance.applied_tariff_date | même chemin |
| Version des corpus | result.classification.provenance.corpus_versions | même chemin |
| Identifiant du dossier | result.session_id | result.session_id |
Si result.classification.status vaut review_required, le moteur a livré un classement exploitable mais demande une validation humaine avant utilisation déclarative.
Exemple UE condensé
{
"status": "completed",
"result": {
"session_id": "run_abc123",
"classification": {
"jurisdiction": "eu",
"status": "completed",
"recommended_code": "8518300090",
"candidates": [
{
"rank": 1,
"code": "8518300090",
"description": "Écouteurs et casques",
"confidence": 0.94,
"legal_position": "Classement selon les RGI et les notes applicables"
}
],
"provenance": {
"methodology_version": "sh-workflow-v1",
"applied_tariff_date": "2026-09-10T00:00:00Z",
"tariff_date_basis": "request",
"corpus_versions": {}
}
}
}
}Exemple Chine condensé
{
"status": "completed",
"result": {
"session_id": "run_def456",
"classification": {
"jurisdiction": "china",
"status": "completed",
"recommended_code": "8413709990999",
"provenance": {
"applied_tariff_date": "2026-09-10T00:00:00Z",
"tariff_date_basis": "request",
"corpus_versions": {}
}
},
"china": {
"code10": "8413709990",
"code13": "8413709990999",
"hs6": "841370",
"description_zh": "离心泵",
"confidence": 0.91,
"justification": "Classement fondé sur le tarif chinois et les notes nationales",
"ciq_candidates": [],
"selected_ciq": "999",
"declaration_elements": [],
"supervision_codes": [],
"quarantine_categories": [],
"units": "",
"mfn_rate": "",
"vat_rate": "",
"validated": true,
"verification_status": "operational",
"warnings": []
}
}
}Les valeurs ci-dessus illustrent la structure. Elles ne constituent pas un avis de classement pour les produits décrits.
Paramètres acceptés
| Champ | Type | Requis | Utilisation |
|---|---|---|---|
content | string | Conditionnel | Description détaillée, fiche produit ou données structurées |
image_urls | string[] | Conditionnel | Jusqu’à 5 images accessibles par HTTPS |
image_base64 | string | Conditionnel | Image encodée en base64 |
image_media_type | string | Avec image_base64 | Type MIME de l’image |
analysis_mode | operational ou legal_deep | Non | operational par défaut |
locale | fr, en, es ou de | Non | Langue de la réponse, fr par défaut |
classification_date | date ISO YYYY-MM-DD | Non | Date tarifaire à appliquer |
product_identity | object | Non | Fabricant, nom, modèle et référence |
technical_facts | object[] | Non | Caractéristiques techniques sourcées |
transaction_context | object | Non | Destination, utilisateur final et usage connu |
taric_code | string | Chine seulement | Indice comparatif facultatif, jamais une conversion automatique |
Au moins content, image_urls ou image_base64 doit être fourni.
Statuts et délais
| Niveau | Valeurs |
|---|---|
| Tâche | pending, processing, completed, failed |
| Décision | completed, review_required |
| Élément d’un lot | completed, failed, timeout |
La passerelle API autorise jusqu’à 11 minutes pour operational et 17 minutes pour legal_deep. Utilisez un délai client légèrement supérieur, par exemple 12 et 18 minutes. Une réponse 202 signifie que la tâche est acceptée, pas qu’elle est terminée.
Classement par lot
Utilisez /v1/classify/batch pour l’UE ou /v1/classify-china/batch pour la Chine.
Une demande contient de 1 à 1 000 lignes et retourne un seul identifiant de lot parent. Le traitement est réparti en sous-lots durables de 100, avec au maximum 32 classements actifs. Un fichier de 500 lignes produit donc 5 sous-lots internes, et un fichier de 1 000 lignes en produit 10. Ce découpage est géré par le service, pas par le client.
curl -sS -X POST "https://api.thetradehub.eu/v1/classify-china/batch" \
-H "X-API-Key: $TTH_API_KEY" \
-H "Idempotency-Key: import-septembre-2026" \
-H "Content-Type: application/json" \
-d '{
"items": [
{"id": "SKU-001", "content": "Roulement à billes radial 25 mm"},
{"id": "SKU-002", "content": "Pompe centrifuge inox"}
],
"locale": "fr",
"analysis_mode": "operational"
}'Récupérez ensuite le lot avec GET /v1/classify-china/batch/{batch_id}. Le champ items contient les résultats lorsque le lot est terminé. Conservez un id stable pour chaque ligne. Après un échec partiel, créez un nouveau lot avec les seules lignes en erreur et un nouvel Idempotency-Key de lot.
Historique, export et sources
Remplacez {route} par classify ou classify-china.
| Besoin | Requête |
|---|---|
| Lister l’historique | GET /v1/{route}/history?page=1&per_page=20 |
| Lire un classement | GET /v1/{route}/history/{log_id} |
| Exporter en CSV | GET /v1/{route}/history/export?from=2026-09-01&to=2026-09-30 |
| Lister les sources figées | GET /v1/{route}/runs/{run_id}/sources |
| Lire une source | GET /v1/{route}/runs/{run_id}/source?source_id={source_id} |
Erreurs et nouvelles tentatives
Les erreurs JSON utilisent toujours cette forme :
{
"error": "description explicite de l'erreur"
}| HTTP | Action recommandée |
|---|---|
400 | Corrigez la requête, ne réessayez pas à l’identique |
401 | Vérifiez X-API-Key |
403 | Ajoutez le périmètre d’autorisation ou activez le module requis |
404 | Vérifiez l’identifiant et l’organisation propriétaire |
429 | Respectez Retry-After, puis réessayez avec le même Idempotency-Key |
502, 503, 504 | Réessayez avec une temporisation progressive et le même Idempotency-Key |
Ne journalisez jamais la clé API, les images en base64 ni les données commerciales sensibles.
Last updated on