GET /v1/autocomplete
Vue d'ensemble
Recherche par préfixe conçue pour les champs de recherche : mots partiels et fautes de frappe bienvenus. Envoyez ce que l'utilisateur a tapé jusque-là (18 rue de la pai) et recevez des suggestions classées, avec possibilité de les biaiser autour de sa position. La réponse a la même forme que /v1/geocode : un champ de recherche peut basculer de l'un à l'autre sans changer son parsing.
Coût : 1 crédit par appel, quel que soit le limit demandé ; voir Coût en crédits. Disponibilité : tous les plans, y compris Free.
Deux comportements méritent une lecture avant l'intégration : la façon dont les suggestions de nos différents index régionaux sont fusionnées et classées, et le fait que le coût ne dépende pas du nombre de résultats.
Requête
GET https://api.geocodeme.net/v1/autocomplete
Header requis : X-Api-Key: gm_YOUR_API_KEY
Paramètres
| Paramètre | Type | Requis | Défaut | Description |
|---|---|---|---|---|
q | string | Oui | aucun | Requête partielle (256 caractères max), par ex. 18 rue de la pai |
limit | integer | Non | 5 | Nombre maximal de suggestions (1 à 20). Sans effet sur le coût : l'appel débite 1 crédit à limit=1 comme à limit=20 |
lat | float | Non | aucun | Latitude de biais : place en tête les suggestions de la région la plus proche de ce point, et affine le classement à l'intérieur de cette région. À envoyer avec lon. Voir Ordre des résultats |
lon | float | Non | aucun | Longitude de biais, à envoyer avec lat |
lang | string | Non | aucun | Langue des résultats : fr ou en. Défaut : noms de lieux locaux |
countrycodes | string | Non | aucun | Sélecteur de région, pas un filtre pays : il choisit quels index régionaux sont interrogés, jamais ce qu'ils renvoient. Les alias eu (Europe continentale), gp (Guadeloupe), mq (Martinique), gf (Guyane), yt (Mayotte) et re (La Réunion) sélectionnent chacun un index, et en lister plusieurs en sélectionne plusieurs (gp,mq). fr désigne toute la zone couverte, exactement comme si le paramètre était absent (il ne restreint pas les résultats à la donnée française), et il ne désigne aucun index en propre : à côté de n'importe quel autre code, c'est cet autre code qui décide (gp,fr rend exactement ce que rend gp). Un code que nous ne servons pas comme région ne désigne rien non plus : de seul ne renvoie aucune suggestion, et eu,de renvoie toute l'Europe continentale. Un code ne peut apporter que l'index qu'il désigne : fr et tout code hors couverture n'en désignent aucun, donc les mettre à côté d'un alias ne peut jamais élargir la réponse ; un second alias, lui, élargit, comme une liste le doit (gp,mq). Voir Quelles régions sont interrogées |
format | string | Non | json | Format de la réponse : json ou geojson |
Les alias gp, mq, gf, yt et re sont résolus par notre routeur régional pour cibler la région d'outre-mer correspondante. Dans les résultats, en revanche, address.country_code reste fr pour ces territoires, selon la convention OpenStreetMap (ils font partie de la France). Voir Couverture pour les zones couvertes.
Quelles régions sont interrogées
Ici, countrycodes ne porte qu'une seule décision : où chercher. Le moteur de suggestions de l'autocomplétion n'a aucun filtre pays transmissible : le paramètre ne peut donc que sélectionner des index régionaux, jamais restreindre ce qu'un index sélectionné rend.
- Les six alias sélectionnent un index chacun.
eudésigne l'index continental,gp/mq/gf/yt/redésignent leur île. En lister plusieurs se lit comme un OU logique, et c'est le seul moyen de demander deux régions à la fois (gp,mq). frdésigne « toute la zone couverte », exactement comme si vous n'envoyiez rien. Ce n'est pas un filtre restrictif, et il ne désigne aucun index en propre : il n'a donc d'effet que s'il est seul.- Tout autre code ne désigne rien. Il ne peut pas filtrer (il n'y a rien avec quoi filtrer), donc il ne sélectionne aucun index. Quand plus rien dans la liste ne sélectionne d'index, l'appel rend une liste vide et aucune requête régionale n'est émise.
countrycodes | Index interrogés | Ce que vous obtenez |
|---|---|---|
| (absent) | les six | des suggestions de toute la zone couverte |
fr | les six, ou le même sous-ensemble que le biais aurait retenu sans le paramètre | la même chose que ne rien envoyer : fr n'est pas un filtre, et il ne désactive pas non plus l'élagage par le biais |
eu | continental seul | tout ce qui est en Europe continentale |
gp | Guadeloupe seule | tout ce qui est en Guadeloupe |
gp,mq | Guadeloupe + Martinique | tout ce qui est sur ces deux îles |
gp,fr | Guadeloupe seule | exactement ce que rend gp (une requête, pas six) |
fr,eu | continental seul | exactement ce que rend eu |
eu,de | continental seul | toute l'Europe continentale ; de ne filtre rien ici |
gp,de | Guadeloupe seule | tout ce qui est en Guadeloupe ; de n'y filtre rien non plus |
de | aucun | aucune suggestion, et aucune requête régionale émise |
fr,de | aucun | aucune suggestion, comme de seul |
zz | aucun | aucune suggestion : un code hors couverture ne sélectionne rien |
Les codes sont insensibles à la casse, les espaces autour sont ignorés et les doublons supprimés : gp,fr, FR,GP et GP , FR sont une seule et même requête.
La règle est la même que sur /v1/geocode : un alias choisit l'index interrogé, et un code ne peut apporter que ce qu'il désigne ; mélanger fr, ou n'importe quel code que nous ne servons pas comme région, avec un alias n'élargit donc jamais la réponse (un second alias, lui, élargit, exactement comme gp,mq l'annonce). gp,fr vaut gp sur les deux endpoints ; fr,eu interroge l'index continental sur les deux.
Ce qui diffère encore, c'est ce qu'un code pays peut faire. Sur /v1/geocode, un code qui n'est pas un alias est transmis au moteur de recherche comme un vrai filtre pays, appliqué à l'intérieur des index sélectionnés. Ici, il n'a nulle part où aller, ce qui se voit à trois endroits si vous partagez des valeurs countrycodes entre les deux endpoints :
eu,derend ici toute l'Europe continentale, et seulement les résultats allemands sur/v1/geocode;deseul ne rend ici rien du tout, là où/v1/geocoderend les résultats allemands. La liste vide est la réponse honnête : nous ne servirons pas tout le continent en silence pour une requête que nous ne pouvons pas honorer ;frseul désigne ici toute la zone couverte (Allemagne comprise), là où sur/v1/geocodeil désigne toute la donnée française, métropole et outre-mer.
Si une liste de suggestions doit ne contenir qu'un pays, filtrez côté client sur address.country_code. Demander limit=20, filtrer, puis afficher cinq résultats coûte le même crédit unique que d'en demander cinq (voir Coût en crédits).
Ordre des résultats : comment les régions sont fusionnées
L'autocomplétion ne repose pas sur un index unique. L'Europe continentale et chacun des cinq DROM font tourner leur propre moteur de suggestions (Couverture). Chaque appel interroge les régions sélectionnées par countrycodes (les six quand le paramètre est absent ou vaut fr et qu'aucun biais n'est fourni, voir Quelles régions sont interrogées), demande limit résultats à chacune, puis fusionne les réponses en une seule liste tronquée à limit.
Quand vous passez lat/lon sans countrycodes (ou avec countrycodes=fr, qui ne demande aucune restriction et est traité à l'identique), le service n'interroge d'abord que la région qui contient ce point (plus l'Europe continentale si le point est dans un DROM). Les régions restantes sont interrogées juste après, sauf celles dont toute la zone de couverture est plus éloignée de votre point de biais que la dernière suggestion déjà retenue : elles ne pourraient que se classer après la coupe et être jetées. Dès qu'une région de la première passe n'a rien rendu du tout, toutes les restantes sont interrogées. Les deux passes alimentent la même fusion : la liste que vous obtenez est donc normalement celle qu'aurait produite la diffusion complète, et le travail économisé est celui dont la fusion aurait de toute façon jeté les résultats.
La seule exception est le temps. L'appel est borné de bout en bout à 2,5 s, les deux passes comprises. Si la première a consommé presque tout ce budget, la seconde est écourtée (voire pas lancée du tout) et la liste revient plus courte que ne l'aurait faite la diffusion complète. Tronquer est le choix assumé : sur un champ de saisie, une liste un peu courte est réparée par la frappe suivante, alors qu'une liste tardive arrive quand l'utilisateur a déjà tapé trois caractères de plus. Une telle réponse est servie normalement (200, même schéma, aucun header supplémentaire) et n'est jamais mise en cache : la frappe suivante repart sur une diffusion fraîche. Elle n'est pas non plus signalée dans X-Coverage-Degraded : rien n'a échoué.
La fusion classe des blocs régionaux, jamais les résultats un par un. À l'intérieur d'un bloc, les suggestions conservent exactement l'ordre rendu par la région : ce classement interne est le seul signal de pertinence disponible, et il intègre déjà le biais lat/lon. La fusion ne décide que de quelle région passe en premier ; les blocs sont ensuite concaténés et la liste coupée à limit.
Avec un biais lat/lon
Les régions sont classées par la distance orthodromique entre le point de biais et le résultat le plus proche de cette région : la région la plus proche en premier.
La même requête avec deux points de biais différents renvoie donc deux listes différentes. Les distances ci-dessous sont mesurées du point de biais au résultat le plus proche de chaque région (pour une requête qui correspond à un lieu proche de Villeurbanne pour l'Europe, et du chef-lieu de chaque DROM) :
| Point de biais | Ordre des régions (distance au résultat le plus proche de la région) |
|---|---|
Lyon : lat=45.7640&lon=4.8357 | Europe (2 km) → Guadeloupe (6 898 km) → Martinique (6 987 km) → Guyane (7 116 km) → Mayotte (7 667 km) → La Réunion (8 995 km) |
Pointe-à-Pitre : lat=16.2410&lon=-61.5330 | Guadeloupe (0 km) → Martinique (188 km) → Guyane (1 610 km) → Europe (6 899 km) → Mayotte (12 168 km) → La Réunion (13 393 km) |
Avec limit=5 et un biais à Lyon, les cinq suggestions viennent de la région continentale ; vous n'obtenez pas une suggestion par région. Une région qui ne renvoie rien passe en dernière position et ne crée aucun trou dans la liste ; une région indisponible pendant la diffusion est simplement absente (voir Couverture dégradée).
Sans biais lat/lon
Le classement retombe sur une priorité explicite et fixe : Europe, puis Guadeloupe, Martinique, Guyane, Mayotte, La Réunion, blocs concaténés dans cet ordre.
C'est un a priori documenté, pas un classement de pertinence : l'index continental contient des ordres de grandeur plus d'objets que les cinq DROM réunis, donc sans autre information c'est de lui que vient le plus probablement le résultat attendu. Pour placer un DROM en tête sans point de biais, utilisez le levier explicite : countrycodes=gp, mq, gf, yt ou re.
Conséquences sur limit
Il n'y a ni tourniquet ni cycle fixe entre régions. Avec limit=12, les douze suggestions peuvent toutes venir d'une seule région ; c'est le comportement voulu, pas un défaut de couverture. Si votre champ de recherche doit montrer simultanément des résultats de plusieurs régions, faites un appel par région avec le countrycodes correspondant (chaque appel coûte son propre crédit) plutôt que d'attendre de la fusion qu'elle les entrelace.
Envoyer lat/lon est la chose la plus efficace qu'un champ de recherche puisse faire : sans biais, un utilisateur à Fort-de-France qui tape une rue locale reçoit d'abord le bloc continental.
Coût en crédits : par appel, pas par résultat
Un appel à /v1/autocomplete débite 1 crédit, quel que soit le limit demandé. limit=1 et limit=20 coûtent exactement la même chose : X-RateLimit-Monthly-Remaining diminue de 1 dans les deux cas. La diffusion vers les moteurs régionaux n'est pas facturée non plus : quel que soit le nombre de régions interrogées, toujours un crédit.
Conséquence de dimensionnement : le sur-échantillonnage est gratuit. Si vous voulez filtrer les suggestions côté client (écarter les résultats hors d'une emprise, dédoublonner, ne garder qu'un pays, reclasser selon vos propres critères), demandez limit=20, filtrez, et affichez les cinq qu'il vous faut. Cela coûte le même crédit unique que limit=5 ; le seul prix est un corps de réponse un peu plus gros.
Ce qui pilote réellement la consommation, c'est le nombre d'appels : un champ de recherche qui part à chaque frappe débite un crédit par frappe. Temporisez la saisie (250 à 300 ms est une valeur courante) et ignorez les requêtes de moins de trois caractères. La même règle vaut pour /v1/geocode et /v1/reverse, qui débitent eux aussi 1 crédit par appel quel que soit limit ; voir Crédits et quotas pour les endpoints dont le coût dépend réellement de la taille de la requête (match, matrix, batch).
Exemples de code
- cURL
- Python
- JavaScript
curl -H "X-Api-Key: gm_YOUR_API_KEY" \
"https://api.geocodeme.net/v1/autocomplete?q=18%20rue%20de%20la%20pai&limit=3&lat=48.8566&lon=2.3522"
Filtrer sur un territoire d'outre-mer (ici la Guadeloupe) :
curl -H "X-Api-Key: gm_YOUR_API_KEY" \
"https://api.geocodeme.net/v1/autocomplete?q=pointe-a-pitre&countrycodes=gp"
import os
import requests
api_key = os.environ["GEOCODEME_API_KEY"]
response = requests.get(
"https://api.geocodeme.net/v1/autocomplete",
params={
"q": "18 rue de la pai",
"limit": 3,
"lat": 48.8566,
"lon": 2.3522,
},
headers={"X-Api-Key": api_key},
)
response.raise_for_status()
if response.headers.get("X-Coverage-Degraded"):
print(f"Résultats partiels, régions indisponibles : {response.headers['X-Coverage-Degraded']}")
for result in response.json()["results"]:
print(f"{result['display_name']} ({result['lat']}, {result['lon']})")
const apiKey = process.env.GEOCODEME_API_KEY;
const params = new URLSearchParams({
q: "18 rue de la pai",
limit: "3",
lat: "48.8566",
lon: "2.3522",
});
const response = await fetch(
`https://api.geocodeme.net/v1/autocomplete?${params}`,
{ headers: { "X-Api-Key": apiKey } }
);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const degraded = response.headers.get("X-Coverage-Degraded");
if (degraded) {
console.warn(`Résultats partiels, régions indisponibles : ${degraded}`);
}
const data = await response.json();
for (const result of data.results) {
console.log(`${result.display_name} (${result.lat}, ${result.lon})`);
}
Réponse
Toutes les réponses incluent les headers de limitation de débit (X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Monthly-Remaining, etc.).
Même schéma que /v1/geocode, champ name de premier niveau compris. Deux points sont spécifiques à cet endpoint :
importancevautnullsur toutes les suggestions avec la version du moteur que nous exploitons : sa sortie GeoJSON n'expose aucun score de pertinence comparable. Ne triez jamais dessus ; l'ordre d'arrivée des résultats est le classement (voir Ordre des résultats).display_nameest composé à partir des composantes de la suggestion ; sa ponctuation diffère donc légèrement de celle de/v1/geocode, qui renvoie la chaîne produite par Nominatim, par exemple18 Rue de la Paix, 75002 Paris, Île-de-France, France. Une raison de plus d'affichernameetdisplay_nameplutôt que d'en parser un.
{
"results": [
{
"lat": 48.8687,
"lon": 2.3315,
"name": null,
"display_name": "18 Rue de la Paix, 75002 Paris, Île-de-France, France",
"type": "house",
"importance": null,
"address": {
"house_number": "18",
"road": "Rue de la Paix",
"city": "Paris",
"state": "Île-de-France",
"postcode": "75002",
"country": "France",
"country_code": "fr"
}
},
{
"lat": 48.8698,
"lon": 2.3305,
"name": "Boulangerie Chez Momo",
"display_name": "Boulangerie Chez Momo, 22 Rue de la Paix, 75002 Paris, Île-de-France, France",
"type": "bakery",
"importance": null,
"address": {
"house_number": "22",
"road": "Rue de la Paix",
"city": "Paris",
"state": "Île-de-France",
"postcode": "75002",
"country": "France",
"country_code": "fr"
}
}
],
"attribution": "© OpenStreetMap contributors",
"meta": {
"took_ms": 23,
"cache_hit": false
}
}
Le champ name
name est le tag OpenStreetMap name de l'objet suggéré, exposé au premier niveau de chaque résultat : le même champ, avec le même contrat, que sur /v1/geocode et /v1/reverse.
| Propriété | Valeur |
|---|---|
| Type | string ou null |
| Exemples de valeurs | "École élémentaire Jules Ferry", "Boulangerie Chez Momo", "Musée du Louvre" |
null quand | l'objet OSM ne porte pas de tag name : le cas courant d'une simple rue ou d'une adresse numérotée. Toujours null au sens JSON, jamais une chaîne vide |
| À ne pas confondre avec | type (la catégorie OSM : school, bakery, house) et display_name (la ligne d'adresse complète) |
Rendu recommandé pour une liste de suggestions : name en titre, display_name en ligne d'adresse dessous, et repli sur display_name seul quand name vaut null. Ne découpez pas display_name sur sa première virgule pour obtenir un titre : sur 18 Rue de la Paix, 75002 Paris, … cela donne 18 Rue de la Paix, et sur une chaîne de style Nominatim comme 4, Rue Victor Hugo, … cela donne 4.
name n'est jamais une composante d'address : address ne contient que des composantes d'adresse (house_number, road, city, town, village, county, state, postcode, country, country_code).
Avec format=geojson, la réponse est une FeatureCollection GeoJSON, exactement comme sur /v1/geocode ; name est présent dans les properties de chaque feature, avant display_name, et vaut null dans les mêmes conditions.
Couverture dégradée : X-Coverage-Degraded
L'autocomplétion interroge en parallèle toutes les régions couvertes (l'Europe + les cinq DROM). Si une région échoue, l'API répond quand même 200 avec les résultats des régions saines et ajoute le header de réponse :
X-Coverage-Degraded: re,yt
Ce header a un sens et un seul : les régions listées ont réellement échoué. C'est un signal de panne, rien d'autre. Une région y figure quand son instance a refusé la connexion, renvoyé une 5xx, renvoyé un corps illisible, ou n'a rien renvoyé alors qu'elle disposait de son temps de réponse plein.
Deux situations qui peuvent elles aussi raccourcir votre liste n'y sont volontairement pas signalées, parce que rien n'est cassé :
- une région que nous n'avons pas interrogée. Avec un biais
lat/lon, les régions dont toute la zone de couverture est plus éloignée de vous que la dernière suggestion déjà retenue sont écartées : elles n'auraient pu qu'être tronquées (voir Ordre des résultats). Non interrogée ne veut pas dire en panne. - une région écourtée par le budget de la requête. L'appel est borné de bout en bout à 2,5 s. Sous charge, une région qui répondait encore à l'expiration du budget est retirée de la fusion plutôt qu'attendue. Elle n'a rien prouvé sur son propre état de santé : nous ne la dénonçons pas.
Quand la liste est simplement plus courte, le header est tout bonnement absent. Sa présence signifie donc toujours que quelque chose est réellement tombé chez nous ; c'est ce qui le rend digne d'être journalisé.
La mise en cache suit la même ligne. Une réponse n'est stockée que si elle est à la fois exempte de panne et complète. Une liste raccourcie par une panne ou par la pression du budget est servie normalement mais jamais mise en cache : l'appel suivant repart sur une diffusion fraîche et la liste redevient entière dès que la pression retombe ; deux secondes de saturation ne peuvent pas figer une liste courte pour une journée.
Intégrer cela dans un champ de recherche
/v1/autocomplete est conçu pour la saisie assistée, où la frappe suivante de l'utilisateur est le mécanisme de réparation le moins cher dont vous disposiez. Quatre recommandations en découlent :
- N'affichez pas d'avertissement « couverture partielle » pendant la saisie. Ce header est de la télémétrie d'exploitation pour vos tableaux de bord, pas un message destiné à la personne qui remplit le champ. L'exposer transforme un incident de deux secondes sur une instance en défaut produit visible.
- Ne retentez pas sur une liste courte. Trois suggestions au lieu de cinq n'est pas une erreur, et réémettre la requête double la charge précisément quand le service est sous pression. La frappe suivante part ~200 ms plus tard et répare la liste gratuitement.
- Ne traitez comme une erreur qu'une liste vide et qui le reste. Une réponse vide isolée en cours de frappe est banale ; un préfixe de quatre lettres qui ne correspond à rien ressemble exactement à cela. Attendez que l'utilisateur ait cessé de taper et que le résultat soit toujours vide avant d'afficher « aucune adresse trouvée ».
- Journalisez le header, ne l'affichez pas. Consigner
X-Coverage-Degradedà côté de vos logs de requêtes est ce qui vous permet de corréler une mauvaise session utilisateur avec une panne de notre côté, et c'est le seul endroit où ce header est rentable.
Le même raisonnement vaut pour la borne de 2,5 s : si un appel est lent, laissez-le être tronqué plutôt que de compenser par une nouvelle tentative côté client ou par un timeout plus long ; une liste de suggestions qui arrive après que la requête a changé est pire qu'une liste courte.
Réponses d'erreur
Paramètre q manquant
{
"error": {
"code": "MISSING_PARAMETER",
"message": "Parameter 'q' is required",
"field": "q"
}
}
lat sans lon (ou l'inverse)
{
"error": {
"code": "INVALID_PARAMETER",
"message": "'lat' and 'lon' must be provided together",
"field": "lon"
}
}
Tous les endpoints sont également soumis aux erreurs INVALID_API_KEY (401) et RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED (429). Consultez Codes d'erreur pour la référence complète.