Skip to content

Both copy text to your clipboard — Build with AI copies a setup prompt to paste into Claude Code, Cursor, Codex or Copilot; Copy page as Markdown copies this page to paste into a chat. How it works

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: the token parameter is a query parameter, not a header. Trailing slash is mandatory on every v2 path — /v2/autocomplete (no trailing slash) returns 404. Always call /v2/autocomplete/.

How it Works

  • Send a partial query using the q parameter, 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 ord value and call /v2/retrieve/.
  • Pass a stable session_token across 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

ParameterTypeReqExampleDescription
qstringNieuwezijds Voorburgwal 147The partial search text. Not text.
tokenstringYOUR_API_KEYAuth token, passed as a query parameter.
countrystringnlISO-2 lowercase country code to restrict results.
session_tokenstringsess_a1b2c3Groups keystrokes into one billable session. See Sessions.
proximitystring5.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.

bash
# 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

bash
curl "https://gateway.mapmetrics-atlas.net/v2/autocomplete/?q=Nieuwezijds%20Voorburgwal%20147&country=nl&session_token=sess_a1b2c3&token=YOUR_API_KEY"

Example Response

json
{
  "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:

bash
# 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 echoed

Copy 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:

js
const retrievable = results.filter((r) => 'ord' in r);   // ✅
const retrievable = results.filter((r) => r.ord !== null); // ❌ crashes on strict decoders

q=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.