Skip to main content

GET /v1/autocomplete

Overview​

Prefix search designed for search boxes: partial words and typos are welcome. Feed it what the user has typed so far (18 rue de la pai) and get ranked suggestions back, optionally biased around the user's position. The response has the same shape as /v1/geocode, so a search box can switch between the two without changing its parsing.

Cost: 1 credit per call, whatever limit you ask for (see Credit cost). Availability: every plan, including Free.

Two behaviours are worth reading before you integrate: how suggestions from our several regional indexes are merged and ordered, and the fact that the cost does not depend on the number of results.

Request​

GET https://api.geocodeme.net/v1/autocomplete

Required header: X-Api-Key: gm_YOUR_API_KEY

Parameters​

ParameterTypeRequiredDefaultDescription
qstringYesn/aPartial query (max 256 characters), e.g. 18 rue de la pai
limitintegerNo5Maximum number of suggestions (1 to 20). Does not change the cost: the call debits 1 credit at limit=1 as at limit=20
latfloatNon/aBias latitude: puts the suggestions of the region closest to this point first, and sharpens ranking inside that region. Must be sent together with lon. See Result ordering
lonfloatNon/aBias longitude. Must be sent together with lat
langstringNon/aResult language: fr or en. Default: local place names
countrycodesstringNon/aRegion selector, not a country filter: it picks which regional indexes are queried, never what they return. The aliases eu (continental Europe), gp (Guadeloupe), mq (Martinique), gf (French Guiana), yt (Mayotte) and re (Réunion) each select one index, and listing several selects several (gp,mq). fr means the whole covered area, exactly as if the parameter were absent (it does not restrict results to French data), and it designates no index of its own, so next to any other code it is that other code that decides (gp,fr returns exactly what gp returns). A code we do not serve as a region designates nothing either: de on its own returns no suggestions, and eu,de returns all of continental Europe. A code can only ever bring in the index it designates: fr and any uncovered code designate none, so putting either next to an alias can never widen the answer; a second alias does widen, as a list should (gp,mq). See Which regions get searched
formatstringNojsonResponse format: json or geojson
Overseas territories (DOM)

The aliases gp, mq, gf, yt and re are resolved by our regional router to target the matching overseas region. In the results, however, address.country_code remains fr for these territories, the OpenStreetMap convention (they are part of France). See Coverage for the covered areas.

Which regions get searched​

Here countrycodes carries a single decision: where to look. The suggestion engine behind autocomplete has no forwardable country filter, so the parameter can only select regional indexes; it can never restrict what a selected index gives back.

  • The six aliases select one index each. eu picks the continental index, gp/mq/gf/yt/re pick their island. Listing several is read as a logical OR, and it is the only way to ask for two regions at once (gp,mq).
  • fr means "the whole covered area", exactly like sending nothing at all. It is not a narrowing filter and it designates no index of its own, so it only has an effect when it is alone.
  • Any other code designates nothing. It cannot filter (there is nothing to filter with), so it selects no index. When nothing in the list selects an index, the call returns an empty list and no regional request is issued at all.
countrycodesIndexes queriedWhat you get
(absent)all sixsuggestions from the whole covered area
frall six, or the same subset the bias would have picked without the parameterthe same thing as sending nothing; fr is not a filter, and it does not disable the bias pruning either
eucontinental onlyanything in continental Europe
gpGuadeloupe onlyanything in Guadeloupe
gp,mqGuadeloupe + Martiniqueanything in those two islands
gp,frGuadeloupe onlyexactly what gp returns: one request, not six
fr,eucontinental onlyexactly what eu returns
eu,decontinental onlyall of continental Europe; de filters nothing here
gp,deGuadeloupe onlyanything in Guadeloupe; de filters nothing here either
denoneno suggestions, and no regional request is issued
fr,denoneno suggestions, same as de on its own
zznoneno suggestions; an uncovered code selects nothing

Codes are case-insensitive, surrounding spaces are ignored and duplicates are removed, so gp,fr, FR,GP and GP , FR are one and the same request.

The rule is the same as on /v1/geocode: an alias chooses the index that is searched, and a code can only ever bring in what it designates, so mixing fr, or any code we do not serve as a region, with an alias never widens the answer (a second alias does, exactly as gp,mq says it should). gp,fr is gp on both endpoints; fr,eu searches the continental index on both.

What still differs is what a country code can do. On /v1/geocode a code that is not an alias is forwarded to the search engine as a real country filter, applied inside the selected indexes. Here it has nowhere to go, and that shows up in three places if you share countrycodes values between the two endpoints:

  • eu,de returns all of continental Europe here, and German results only on /v1/geocode.
  • de on its own returns nothing at all here, where /v1/geocode returns German results. An empty list is the honest answer: we will not silently serve the whole continent for a request we cannot honour.
  • fr on its own means the whole covered area here (Germany included), where on /v1/geocode it means all French data, mainland and overseas.

If a suggestion list must hold a single country, filter client-side on address.country_code. Asking for limit=20, filtering, and displaying five costs the same single credit as asking for five (see Credit cost).

Result ordering: how regions are merged​

Autocomplete is not backed by a single index. Continental Europe and each of the five overseas regions run their own suggestion engine (Coverage). Every call queries the regions selected by countrycodes (all six when the parameter is absent or fr and no bias is given, see Which regions get searched), asks each of them for limit results, and merges what comes back into one list truncated to limit.

When you pass lat/lon and no countrycodes (or countrycodes=fr, which asks for no restriction and is treated identically), the service first queries only the region containing that point (plus continental Europe if the point is in an overseas region). The remaining regions are queried right after, except those whose coverage area is entirely farther from your bias point than the last suggestion already retained: those could only rank after the cut and be dropped. Whenever a region of the first pass returned nothing at all, all the remaining ones are queried. Both passes feed the same merge, so the list you normally get is the one the full fan-out would have produced; the saved work is work whose results the merge would have dropped anyway.

The one exception is time. The call is bounded end to end at 2.5 s, both passes included. If the first pass has eaten nearly all of that, the second one is cut short (or not started at all), and the list comes back shorter than the full fan-out would have been. Truncating is the deliberate choice: on a search box a slightly short list is repaired by the very next keystroke, whereas a late list arrives after the user has typed three more characters. Such a response is served normally (200, same schema, no extra header) and is never cached, so the next keystroke starts from a fresh fan-out. It is not reported in X-Coverage-Degraded either; nothing failed.

The merge orders region blocks, never individual results. Inside a block, suggestions keep the exact order the region returned them: that in-region ranking is the only relevance signal available, and it already accounts for the lat/lon bias. The merge decides only which region goes first; blocks are then concatenated and the list cut to limit.

With a lat/lon bias​

Regions are ordered by the great-circle distance from the bias point to that region's closest result: nearest region first.

The same query with two different bias points therefore returns two different lists. Distances below are measured from the bias point to the nearest result of each region (for a query matching a place near Villeurbanne for Europe, and near each overseas region's main city):

Bias pointRegion order (distance to that region's nearest result)
Lyon, lat=45.7640&lon=4.8357Europe (2 km) → Guadeloupe (6,898 km) → Martinique (6,987 km) → French Guiana (7,116 km) → Mayotte (7,667 km) → Réunion (8,995 km)
Pointe-à-Pitre, lat=16.2410&lon=-61.5330Guadeloupe (0 km) → Martinique (188 km) → French Guiana (1,610 km) → Europe (6,899 km) → Mayotte (12,168 km) → Réunion (13,393 km)

With limit=5 and a bias in Lyon, all five suggestions come from the continental region; you do not get one suggestion per region. A region that returns nothing sinks to the last position and creates no hole in the list; a region that is down during the fan-out is simply absent (see Degraded coverage).

Without a lat/lon bias​

Ordering falls back to an explicit, fixed priority: Europe, then Guadeloupe, Martinique, French Guiana, Mayotte, Réunion (blocks concatenated in that order).

This is a documented prior, not a relevance ranking: the continental index holds orders of magnitude more objects than the five overseas regions combined, so with no other information it is the likeliest source of the expected result. To put an overseas region first without a bias point, use the explicit lever: countrycodes=gp, mq, gf, yt or re.

Consequences for limit​

There is no round-robin and no fixed cycle across regions. With limit=12, all twelve suggestions may come from a single region; that is the intended behaviour, not a coverage bug. If your search box must show results from several regions at once, issue one call per region with the matching countrycodes (each call costs its own credit) rather than expecting the merge to interleave them.

Sending lat/lon is the single most effective thing a search box can do: without it, a user in Fort-de-France typing a local street gets the continental block first.

Credit cost: per call, not per result​

One call to /v1/autocomplete debits 1 credit, whatever limit you asked for. limit=1 and limit=20 cost exactly the same: X-RateLimit-Monthly-Remaining goes down by 1 in both cases. The fan-out across the regional engines is not billed either; however many regions are queried, still one credit.

Sizing consequence: over-sampling is free. If you want to filter suggestions client-side (drop results outside a bounding box, deduplicate, keep a single country, re-rank by your own criteria), ask for limit=20, filter, and display the five you need. It costs the same single credit as asking for limit=5; the only price is a slightly larger response body.

What does drive consumption is the number of calls: a search box that fires on every keystroke debits one credit per keystroke. Debounce the input (250 to 300 ms is a common value) and skip queries below three characters. The same rule applies to /v1/geocode and /v1/reverse, which also debit 1 credit per call regardless of limit; see Credits & Quotas for the endpoints whose cost does depend on request size (match, matrix, batch).

Code Examples​

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"

Filter on an overseas territory (here Guadeloupe):

curl -H "X-Api-Key: gm_YOUR_API_KEY" \
"https://api.geocodeme.net/v1/autocomplete?q=pointe-a-pitre&countrycodes=gp"

Response​

info

All responses include rate limiting headers (X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Monthly-Remaining, etc.).

Same schema as /v1/geocode, including the top-level name field. Two points are specific to this endpoint:

  • importance is null on every suggestion with the engine version we run: its GeoJSON output exposes no comparable relevance score. Never sort on it; the order results arrive in is the ranking (see Result ordering).
  • display_name is composed from the suggestion's own components, so its punctuation differs slightly from /v1/geocode (which returns Nominatim's own string): 18 Rue de la Paix, 75002 Paris, Île-de-France, France. One more reason to display name and display_name rather than parsing either.
{
"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
}
}

The name field​

name is the OpenStreetMap name tag of the suggested object, exposed at the top level of each result: the same field, with the same contract, as on /v1/geocode and /v1/reverse.

PropertyValue
Typestring or null
Example values"École élémentaire Jules Ferry", "Boulangerie Chez Momo", "Musée du Louvre"
null whenthe OSM object carries no name tag, the usual case for a plain street or a numbered address. Always JSON null, never an empty string
Not to be confused withtype (the OSM category: school, bakery, house) and display_name (the full address line)

Recommended rendering for a suggestion list: name as the title, display_name as the address line underneath, and fall back to display_name alone when name is null. Do not split display_name on its first comma to get a title: on 18 Rue de la Paix, 75002 Paris, … that yields 18 Rue de la Paix, and on a Nominatim-style string such as 4, Rue Victor Hugo, … it yields 4.

name is never an address component: address keeps holding address parts only (house_number, road, city, town, village, county, state, postcode, country, country_code).

With format=geojson, the response is a GeoJSON FeatureCollection, exactly as on /v1/geocode; name is present in each feature's properties, before display_name, and is null under the same conditions.

Degraded coverage: X-Coverage-Degraded​

Autocomplete fans out across all covered regions (Europe + the five French overseas regions). If a region fails, the API still answers 200 with the results of the healthy regions and sets the response header:

X-Coverage-Degraded: re,yt

The header has exactly one meaning: every region listed really failed. It is a fault signal, and only that. A region gets listed when its instance refused the connection, returned a 5xx, returned a body we could not read, or returned nothing at all while it had its full per-region time budget.

Two things that can also shorten your list are deliberately not reported there, because nothing is broken:

  • a region we never queried. With a lat/lon bias, regions whose entire coverage area lies farther from you than the last suggestion already retained are skipped; they could only have been truncated away (see Result ordering). Not queried is not down.
  • a region cut short by the request budget. The call is bounded end to end at 2.5 s. Under load, a region still answering when the budget runs out is dropped from the merge rather than waited for. It never proved anything about its own health, so we do not denounce it.

When the list is merely shorter, the header is simply absent. Its presence therefore always means something is genuinely down on our side, which is what makes it worth logging.

Caching follows the same line. A response is stored only when it is both failure-free and complete. A list shortened by a failure or by budget pressure is served normally but never cached, so the next call starts from a fresh fan-out and the list becomes whole again as soon as the pressure passes; a two-second saturation can never freeze a short list for a day.

Integrating this in a search box​

/v1/autocomplete is built for type-ahead, where the user's next keystroke is the cheapest repair mechanism you have. Four recommendations follow from that:

  • Do not show a "partial coverage" warning while the user is typing. The header is operational telemetry for your dashboards, not a message for the person filling in the field. Surfacing it turns a two-second incident on one instance into a visible product defect.
  • Do not retry on a short list. A list of 3 suggestions instead of 5 is not an error, and re-issuing the request doubles the load precisely when the service is under pressure. The next keystroke fires a fresh request ~200 ms later and repairs the list for free.
  • Treat as an error only a list that is empty and stays empty. A single empty response mid-typing is ordinary; a four-letter prefix that matches nothing looks exactly like that. Wait until the user has stopped typing and the result is still empty before showing "no address found".
  • Log the header, don't display it. Recording X-Coverage-Degraded alongside your request logs is what lets you correlate a bad user session with an outage on our side, and it is the one place where the header pays for itself.

The same reasoning applies to the 2.5 s bound: if a call is slow, let it be truncated rather than compensating with a client-side retry or a longer timeout; a suggestion list that lands after the query has changed is worse than a short one.

Error Responses​

Missing q parameter​

{
"error": {
"code": "MISSING_PARAMETER",
"message": "Parameter 'q' is required",
"field": "q"
}
}

lat without lon (or the reverse)​

{
"error": {
"code": "INVALID_PARAMETER",
"message": "'lat' and 'lon' must be provided together",
"field": "lon"
}
}
tip

All endpoints are also subject to INVALID_API_KEY (401) and RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED (429) errors. See Error Codes for the full reference.