TrustyData Docs
Site Tarifs Contact

Codes d'erreur

L'API utilise les codes HTTP standards. Tous les corps de réponse en erreur suivent le format {"detail": "<description>"} (ou un tableau pour les erreurs de validation).

Vue d'ensemble

CodeFamilleCause typique
200OKSuccès, lire le corps
400ClientRequête malformée (query trop courte, JSON invalide)
401AuthClé manquante ou invalide
403AuthPlan insuffisant pour cet endpoint, ou clé publiable hors de son périmètre
404ClientRessource inexistante (identifiant BAN inconnu)
409ClientL'écriture heurte l'état actuel de vos lieux (capacité, réduction suspecte), ou le plafond de clés publiables du plan
422ClientValidation Pydantic (paramètres incohérents), ou adresse impossible à analyser
429QuotaQuota mensuel dépassé
500ServeurErreur interne (parser, Meilisearch, moteur de traitement)
503ServeurService d'authentification temporairement indisponible

Réussite particulière : 200 OK sans match

Une recherche qui ne retourne aucun résultat reste un succès HTTP. Le champ status du corps signale la situation :

{
  "status": "no_results",
  "message": "No verified match found",
  "matches": []
}

De même pour une requête trop courte sur /address/autocomplete :

{
  "status": "TOO_SHORT",
  "message": "The request is too short, complete your input",
  "suggestions": []
}

Côté code client : testez systématiquement status avant de consommer suggestions / matches / results.

400 Bad Request

La requête est malformée : query q manquante, JSON corps non valide, paramètre obligatoire absent.

{ "detail": "Request too short or invalid" }

Action : corriger la requête côté client. Ces erreurs ne sont pas comptées dans le quota.

401 Unauthorized

Le header Authorization est absent, mal formé, ou la clé n'est plus valide (révoquée, compte fermé).

{ "detail": "API key required. Please provide a valid API key in the Authorization header." }
{ "detail": "Invalid API key" }

Action : vérifier le header (préfixe Bearer obligatoire, sans guillemets autour de la clé), vérifier que la clé n'a pas été révoquée dans l'espace client.

403 Forbidden

La clé est valide mais son plan ne donne pas accès à l'endpoint appelé. Cas typique : appel de /route/compute avec une clé Growth (réservé Business).

{ "detail": "Your plan (GROWTH) does not have access to this service. Please upgrade your plan." }

Action : upgrader le plan, ou utiliser un endpoint accessible — voir Plans & quotas.

Une clé publiable (tdp_…) rend trois autres 403, et ceux-là portent un detail objet : la clé est authentique et le service le sait, c'est l'usage qui est refusé. Le CORS reste permissif exprès, pour que le JavaScript de votre page puisse lire ce corps et afficher un message plutôt qu'une erreur réseau opaque.

Clé publiable hors de son périmètre

Une clé publiable n'ouvre qu'un seul couple méthode + chemin, GET /locations/search. Tout le reste de l'API est fermé, y compris GET /me et la liste de vos référentiels.

{
  "detail": {
    "code": "cle_publiable_hors_perimetre",
    "message": "Une clé publiable n'ouvre que GET /locations/search."
  }
}

Action : pour tout autre appel, utiliser votre clé secrète depuis votre serveur.

En-tête Origin absent

Une clé publiable se valide sur l'origine de la page appelante. Un navigateur pose Origin tout seul ; un appel serveur, non — c'est le symptôme d'une clé publiable employée là où il fallait la clé secrète.

{
  "detail": {
    "code": "origine_absente",
    "message": "Une clé publiable exige un en-tête Origin. Utilisez votre clé secrète pour un appel serveur."
  }
}

Origine non autorisée

L'Origin reçu ne figure pas dans la liste déclarée sur la clé. Rappel des règles de correspondance : https obligatoire, et un joker (https://*.exemple.fr) couvre les sous-domaines mais pas le domaine lui-même.

{
  "detail": {
    "code": "origine_non_autorisee",
    "message": "Ce domaine n'est pas déclaré sur cette clé publiable."
  }
}

Action : ajouter le domaine à la clé depuis l'espace client, écran Clés API, action Modifier les domaines — la clé ne change pas, votre page n'a pas à être redéployée.

404 Not Found

L'identifiant fourni (typiquement à GET /address/view/{id}) ne correspond à aucune adresse de la base BAN.

Action : vérifier l'identifiant. Les IDs peuvent évoluer entre deux millésimes BAN si une voie est renommée — préférer toujours id_ban au champ id interne pour le stockage long terme.

409 Conflict

Propre aux écritures de Mes lieux (/locations/*) : la requête est valide, mais elle heurte l'état actuel de votre référentiel. Le detail est un objet, pas une phrase — son champ code se teste sans analyser du texte. Seule exception, le conflit de code décrit plus bas, dont le detail est la chaîne "code_deja_utilise" : c'est justement à la forme du detail que vous distinguez les deux refus que peut rendre la création d'un référentiel.

Capacité de sites dépassée

Votre plan inclut un nombre de sites (200 / 2 000 / 10 000 selon Starter / Growth / Business). L'écriture dépasserait ce nombre, tolérance de 10 % comprise.

{
  "detail": {
    "code": "capacite_sites_depassee",
    "sites_actifs": 2200,
    "capacite": 2000,
    "plafond": 2200
  }
}

Action : désactiver les sites qui ne servent plus (ils sortent du décompte, sans être supprimés), ou passer au plan supérieur. Réessayer à l'identique ne changera rien.

Plafond de référentiels atteint

Votre plan autorise aussi un nombre de référentiels (5 / 25 / 100 selon Starter / Growth / Business). POST /locations/referentiels le refuse à la borne, sans tolérance : à 5 référentiels actifs sur un plan Starter, le sixième est refusé. Seuls les référentiels actifs comptent.

{
  "detail": {
    "code": "capacite_referentiels_depassee",
    "referentiels_actifs": 5,
    "maximum": 5
  }
}

Action : archiver un référentiel dont vous n'avez plus l'usage (DELETE), ce qui libère une place, ou passer au plan supérieur. Changer le code demandé ne sert à rien : ce refus ne porte pas sur le code.

Code de référentiel déjà utilisé

Le code demandé est déjà porté par un référentiel du compte. Il l'est aussi quand ce référentiel est archivé : l'unicité vaut quel que soit le statut, parce que le code identifie vos sites dans toutes les URLs de l'API. Archiver rend une place, pas un code.

{
  "detail": "code_deja_utilise"
}

Action : choisir un autre code.

Réduction suspecte à la synchronisation

Un sites:sync désactiverait plus de la moitié de vos sites actifs — le symptôme habituel d'un export tronqué côté source.

{
  "detail": {
    "code": "reduction_suspecte",
    "actifs_avant": 840,
    "actifs_apres": 12,
    "message": "Cette synchronisation désactiverait plus de la moitié des sites actifs. Renvoyez avec confirmer_reduction=true si c'est voulu."
  }
}

Action : vérifier l'export avant tout. Si la réduction est bien voulue, renvoyer le même appel avec "confirmer_reduction": true.

Plafond de clés publiables atteint

Émis par l'espace client, à la création d'une clé publiable : votre plan en inclut un nombre fixe, sans tolérance. Une clé se crée une par une, donc un dépassement ne peut être qu'intentionnel.

{
  "detail": {
    "code": "capacite_cles_publiables_depassee",
    "plafond": 2,
    "actives": 2,
    "message": "Votre plan inclut 2 clés publiables. Révoquez-en une ou changez de plan."
  }
}

Action : révoquer une clé publiable devenue inutile, ou passer au plan supérieur. Un plafond à 0 signifie que le plan n'en inclut aucune.

422 Unprocessable Entity

Validation Pydantic — les paramètres passent le format mais violent une règle métier. Le corps liste les erreurs en détail.

{
  "detail": [
    {
      "type": "value_error",
      "loc": [],
      "msg": "Value error, population_min must be less than or equal to population_max",
      "input": { "population_min": 1500, "population_max": 150 }
    }
  ]
}

Action : lire msg et loc, corriger le payload.

Adresse impossible à analyser

Un 422 signale aussi une adresse dont le moteur n'a pu extraire aucun élément exploitable — ni numéro, ni voie, ni code postal, ni commune. Le corps prend alors la forme courte, sans detail :

{
  "status": "API_ERROR",
  "message": "Unable to parse address"
}

Cette forme est celle de /address/verify et /address/autocomplete. Sur /address/proximity et /route/*, le même cas rend un 422 de forme detail, qui couvre aussi l'adresse analysable mais absente de la BAN :

{
  "detail": "Unable to resolve 'adresse' to a verified match"
}

Le distinguer du 500 compte : un 422 dit que la saisie est en cause et qu'il faut la corriger, un 500 que le service a échoué et qu'il faut réessayer.

Ne pas confondre non plus avec l'adresse analysable mais absente de la BAN. Sur /address/verify et /address/autocomplete, où l'adresse est la recherche, c'est un 200 sans résultat (voir plus haut). Sur /address/proximity et /route/*, où elle sert de paramètre — centre de recherche, borne d'itinéraire — il n'y a pas de recherche à rendre vide : c'est le 422 ci-dessus.

Action : vérifier la saisie côté appelant. Ne pas réessayer à l'identique : la réponse ne changera pas.

Domaine de clé publiable refusé

À la création d'une clé publiable ou à la modification de ses domaines, chaque origine est validée à la saisie plutôt qu'à l'appel : https obligatoire, le domaine seul (ni chemin, ni paramètre), et un joker écrit uniquement en tête, suivi d'au moins deux étiquettes (https://*.fr est refusé). Le detail est un objet, et son message nomme l'entrée fautive.

{
  "detail": {
    "code": "origine_invalide",
    "message": "« exemple.fr » : une origine doit commencer par https://."
  }
}

Une même clé porte dix domaines au maximum :

{
  "detail": {
    "code": "trop_d_origines",
    "message": "Une clé publiable porte au plus 10 domaines."
  }
}

Action : corriger l'entrée signalée, ou répartir vos domaines sur plusieurs clés — un plan en inclut plusieurs.

429 Too Many Requests

Le quota mensuel est dépassé. Concerne d'abord le plan Discovery (20 000 requêtes / mois — hard cap) ; les autres plans suivent les volumes contractuels.

{ "detail": "Rate limit exceeded. Max allowed usage for plan DISCOVERY is 20000 requests." }
{ "detail": "Your plan (STARTER) has exceeded the rate limit for this service. Please upgrade your plan." }

Action : attendre la prochaine période de facturation, ou upgrader. Il n'y a pas de header Retry-After pour l'instant — le quota se réinitialise au début de la période mensuelle de votre abonnement.

Un second 429 existe, propre aux clés publiables : une limitation de débit par adresse IP du visiteur, appliquée en amont du service. Elle n'a pas de corps JSON et pas d'en-tête CORS — le JavaScript de votre page la voit donc comme une erreur réseau, pas comme une réponse lisible. Elle vise le script qui rejoue vos appels en boucle, pas un visiteur normal : votre quota mensuel, lui, reste celui du compte émetteur.

500 Internal Server Error

Erreur côté API : parser d'adresses indisponible, Meilisearch en surcharge, moteur de traitement injoignable sur les endpoints de routage.

Action : retry avec un backoff exponentiel (1s, 3s, 9s, 27s…). Si les 500 persistent au-delà de quelques minutes, contactez-nous avec les X-Request-ID des appels concernés (à venir).

503 Service Unavailable

Le service d'authentification (validation de votre clé) est temporairement injoignable. L'API préfère échouer explicitement plutôt que de servir des requêtes sans vérifier l'identité.

{ "detail": "Authentication service unavailable" }

Action : retry avec backoff exponentiel. La durée typique d'indisponibilité est de l'ordre de la seconde — si ça persiste, contactez-nous.

Pattern de retry recommandé

Pour les codes 500, 502, 503, 504 : backoff exponentiel avec jitter. Pour 429 : pas de retry — c'est une erreur structurelle, attendez le reset. Pour 4xx (autre que 429) : pas de retry — corrigez la requête.

import time, random, requests

def call_with_retry(url, headers, params=None, json=None, max_attempts=4):
    for attempt in range(max_attempts):
        r = requests.get(url, headers=headers, params=params, timeout=10) \
            if json is None else \
            requests.post(url, headers=headers, json=json, timeout=10)
        if r.status_code < 500 and r.status_code != 429:
            return r          # succès ou erreur cliente, on rend
        if r.status_code == 429 or attempt == max_attempts - 1:
            return r          # quota: pas de retry. Dernier essai: rend.
        sleep = (2 ** attempt) + random.random()
        time.sleep(sleep)     # 1s, 3s, 7s, ... avec jitter

Prochains pas