Autocomplete Endpoint (v2)
The v2 Autocomplete API provides instant place and address suggestions based on partial user input. It is typically used in search boxes where results update in real time as the user types.
Base URL: all v2 examples on this page use
https://gateway.mapmetrics-atlas.net. Auth: thetokenparameter is a query parameter, not a header. Trailing slash is mandatory on every v2 path —/v2/autocomplete(no trailing slash) returns404. Always call/v2/autocomplete/.
How it Works
- Send a partial query using the
qparameter, such as"Nieuwezijds Voorburgwal 147". - The API returns a list of matching suggestions (venues, streets, addresses, localities) with no coordinates attached — see below.
- To resolve a suggestion to actual coordinates, take its
ordvalue and call/v2/retrieve/. - Pass a stable
session_tokenacross all keystrokes of one search so the whole typing sequence bills as a single session — see Sessions for the full billing model.
Common mistake: q, not text
The parameter is q. Sending text=Nieuwezijds Voorburgwal 147 does not error — it returns HTTP 200 with "results": [] and "q": "". It silently looks like "no matches" instead of failing loudly. This is the single most common integration mistake against this endpoint.
proximity is longitude first
proximity takes the form <lon>,<lat> — longitude before latitude. Reversing the order does not error either; it returns plausible-looking but wrong results. A query centred on Maastricht with lat/lon swapped returned hits 22–27 km away, in the wrong town entirely.
Endpoint
GET https://gateway.mapmetrics-atlas.net/v2/autocomplete/Parameters
| Parameter | Type | Req | Example | Description |
|---|---|---|---|---|
q | string | ✅ | Nieuwezijds Voorburgwal 147 | The partial search text. Not text. |
token | string | ✅ | YOUR_API_KEY | Auth token, passed as a query parameter. |
country | string | ❌ | nl | ISO-2 lowercase country code to restrict results. |
session_token | string | ❌ | sess_a1b2c3 | Groups keystrokes into one billable session. See Sessions. |
proximity | string | ❌ | 5.7423,50.8514 | <lon>,<lat> bias point. Longitude first. |
That is the whole list. Anything else you send is ignored — see There is no category filter below for the most commonly attempted extra parameter.
There is no category filter on this endpoint
category= is silently ignored here
/v2/autocomplete/ has no category parameter. category=, categories=, poi_category= and layers= are all accepted with HTTP 200 and then discarded — the response is byte-for-byte identical to the same query without them (verified by diffing responses with elapsed_ms stripped). Nothing tells you the filter didn't apply.
# These two return identical results. The second one filters nothing.
curl "…/v2/autocomplete/?q=hotel%20amsterdam&country=nl&token=YOUR_API_KEY"
curl "…/v2/autocomplete/?q=hotel%20amsterdam&country=nl&category=restaurant&token=YOUR_API_KEY"Categories on this endpoint are resolved from the query text, not from a parameter: q=hotel amsterdam ranks hotels highly, q=supermarket utrecht ranks supermarkets highly. It is a ranking bias, not a filter — you will still get non-matching rows mixed in, and how well it works varies by category. Measured over the top 15 results for a city-scoped query, hotel and supermarket land ~13–14 matching rows, restaurant ~9–11, cafe ~8, and fuel is weakest at ~2. Treat it as "mostly the right kind", never as a guarantee.
If you need an actual filter — a hard "only supermarkets, ranked by distance" — use /v2/category/ instead. That endpoint really does filter, is proximity-ranked, carries distance_m, and fails honestly (an unknown category returns zero rows rather than unfiltered ones).
Example
curl "https://gateway.mapmetrics-atlas.net/v2/autocomplete/?q=Nieuwezijds%20Voorburgwal%20147&country=nl&session_token=sess_a1b2c3&token=YOUR_API_KEY"Example Response
{
"count": 2,
"q": "Nieuwezijds Voorburgwal 147",
"mode": "autocomplete",
"country": "nl",
"elapsed_ms": 4,
"results": [
{
"id": "address.36035832929227138",
"ord": 276363,
"layer": "address",
"country": "nl",
"hn": "147",
"housenumber": "147",
"text": "Nieuwezijds Voorburgwal 147",
"place_name": "Nieuwezijds Voorburgwal 147, Amsterdam",
"locality": "Amsterdam",
"category": null,
"brand": null
},
{
"id": "place.0",
"layer": "place",
"country": "nl",
"text": "Amsterdam",
"place_name": "Amsterdam, Netherlands",
"locality": "Amsterdam",
"category": null,
"brand": null
}
]
}Note that no result carries center, geometry, or bbox — coordinates are deliberately withheld. Suggestions are cheap to serve at every keystroke; coordinate resolution is the billable step, and it only happens when you call /v2/retrieve/ on the suggestion the user picked.
Three traps in the result objects
The second result above is not a typo. Three properties of this response shape break naive client code, and none of them announce themselves.
hn is a house number string, not a flag
hn is typed string | null and holds the actual house number — "147", not true. The name reads like a boolean and the field sits next to housenumber, so it is routinely misread as "this row has a house number".
This matters because hn is passed straight through to /v2/retrieve/, where the wrong value is silently discarded and you get the street centroid instead of the building:
# hn=147 — the house number. Resolves to the building.
…/v2/retrieve/?country=nl&layer=address&ord=276363&hn=147
# → "center": [4.890890, 52.373158], "housenumber": "147"
# hn=true — discarded. Same answer as omitting hn entirely, ~146 m away.
…/v2/retrieve/?country=nl&layer=address&ord=276363&hn=true
# → "center": [4.890498, 52.371864] ← street centroid, no housenumber echoedCopy hn from the suggestion verbatim. Never synthesise it.
Non-retrievable rows omit ord entirely
Some rows — injected locality entries, like the bare "Amsterdam" above — cannot be retrieved. On those rows the ord key is absent from the JSON object, not present-and-null. Code that does result["ord"] or result.ord! on a strict/typed decoder throws rather than yielding null.
Test for presence, not for null:
const retrievable = results.filter((r) => 'ord' in r); // ✅
const retrievable = results.filter((r) => r.ord !== null); // ❌ crashes on strict decodersq=amsterdam&country=nl currently returns 15 rows, two of which have no ord. Every geocoding SDK exposes this as suggestion.isRetrievable so you don't have to probe the raw object.
id is not unique within a response
Never use id as a list key
id is not a primary key and is not unique inside a single response. q=amsterdam&country=nl returns "id": "place.0" on two different rows. Feeding that into a keyed list — React key, Flutter ValueKey, Compose items(key = …), SwiftUI id: — produces duplicate keys. Compose throws outright (IllegalArgumentException: Key … was already used); React and SwiftUI corrupt row state more quietly.
id is also useless for retrieval — see the ord is the handle note. Treat it as opaque debug metadata.
For a stable key, derive one from content, or use the list index. The search UI layers ship a uniquifyRowIds helper that suffixes repeats (place.0, place.0#2) so a list can be keyed safely without discarding rows.