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
| Code | Famille | Cause typique |
|---|---|---|
200 | OK | Succès, lire le corps |
400 | Client | Requête malformée (query trop courte, JSON invalide) |
401 | Auth | Clé manquante ou invalide |
403 | Auth | Plan insuffisant pour cet endpoint, ou clé publiable hors de son périmètre |
404 | Client | Ressource inexistante (identifiant BAN inconnu) |
409 | Client | L'écriture heurte l'état actuel de vos lieux (capacité, réduction suspecte), ou le plafond de clés publiables du plan |
422 | Client | Validation Pydantic (paramètres incohérents), ou adresse impossible à analyser |
429 | Quota | Quota mensuel dépassé |
500 | Serveur | Erreur interne (parser, Meilisearch, moteur de traitement) |
503 | Serveur | Service 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
- Authentification — détails 401/403
- Plans & quotas — comprendre les 403/429
- Référence complète — voir le détail par endpoint