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

OSM Tier Endpoints

The OSM tier is a free, OpenStreetMap-only geocoder reached through the osm-geocode scope. It is a different engine from the v2 geocoder, with a different response shape and a different cost model, and the differences are easy to trip over when porting code between the two.

v2 tierOSM tier
Endpoints/v2/autocomplete/, /v2/retrieve/, …/osm-autocomplete/, /osm-geocode/, /osm-reverse/
Sessionsyes — billed per sessionnone
Coordinates in suggestionsno — must call /v2/retrieve/yes, inline
Billingper sessionper request, rate-limited

Rate limit: 10,000 requests per key per day, plus a global monthly cap. Exhausting it returns a quota error carrying a self-host URL — the engine is open source at MapMetrics/atlas-osm-geocoder.

No retrieve step

The single biggest structural difference: OSM-tier suggestions already carry coordinates. Every row from /osm-autocomplete/ has a center and a geometry, so there is no suggest → retrieve round trip and no session token to thread through.

bash
curl "https://gateway.mapmetrics-atlas.net/osm-autocomplete/?q=amsterdam&token=YOUR_API_KEY"
json
{
  "type": "FeatureCollection",
  "query": "amsterdam",
  "query_ms": 12,
  "results": [
    {
      "id": "poi.0",
      "type": "Feature",
      "place_type": ["poi"],
      "relevance": 1.0,
      "text": "Amsterdam Museum",
      "place_name": "Amsterdam Museum, NL",
      "center": [4.891, 52.3704],
      "geometry": { "type": "Point", "coordinates": [4.891, 52.3704] },
      "context": [{ "id": "country.nl", "text": "NL", "short_code": "nl" }],
      "properties": { "layer": "poi", "popularity": 7043 },
      "matching_text": "Amsterdam",
      "matching_place_name": "Amsterdam"
    }
  ],
  "attribution": "© OpenStreetMap contributors, MapMetrics Atlas"
}

Porting v2 code here means deleting the retrieve call, not rewriting it. In the geocoding SDKs this shows up as createSession() throwing on the OSM tier, with autocomplete() available in its place.

results vs features — the key differs by endpoint

The rows are not always under the same key

/osm-autocomplete/ returns its rows under results. /osm-geocode/ returns them under features. Some worker builds emit both keys with the same content. Reading only one of them yields an empty list against the other endpoint — with no error, because HTTP 200 and a well-formed envelope came back either way.

Read defensively:

js
const rows = body.results ?? body.features ?? [];

Both envelopes otherwise agree: type: "FeatureCollection", a query echo, query_ms, and an attribution string.

id is not unique here either

Exactly as on v2, id collides within a single response — and on this tier it collides across genuinely different places. q=amsterdam currently returns "id": "poi.0" for both Amsterdam Museum and Amsterdam Oosterpark Inn.

Do not use id as a list key. It is opaque debug metadata, not a primary key.

Sub-paths are ignored, not routed

/osm-geocode/anything/ returns the same free-text results

The gateway matches on the /osm-geocode/ prefix and ignores whatever follows. /osm-geocode/, /osm-geocode/category/ and /osm-geocode/banana/ all return HTTP 200 with byte-identical free-text results.

There is no sub-path API here. A URL like /osm-geocode/category/ looks like a category endpoint and behaves like an ordinary search, so a typo or an invented path silently returns plausible, wrong data instead of a 404. Only the paths listed on this page mean anything.

For real category filtering, see Category Search on the v2 tier.

Self-hosting

A self-hosted atlas-osm-geocoder instance uses different paths and no token: /search, /reverse, /autocomplete, with no /osm- prefix and no token parameter. Every SDK exposes this as a distinct selfHosted() constructor that never sends a credential.

See Also