Documentation navigation
Guides
Getting started Boundary searchAPI reference
Overview Health GET/v1/status Countries GET/v1/countries GET/v1/countries/{code} GET/v1/countries/{code}/regions GET/v1/countries/{code}/neighbors Regions GET/v1/regions GET/v1/regions/{id} GET/v1/regions/{id}/cities GET/v1/regions/{id}/children Cities GET/v1/cities GET/v1/cities/{id} GET/v1/cities/{id}/hierarchy GET/v1/cities/nearby Postal Codes GET/v1/postal-codes GET/v1/postal-codes/nearest Timezones GET/v1/timezones GET/v1/timezones/{tzId} IP Geolocation GET/v1/ip/{address} GET/v1/ip/me Search GET/v1/autocomplete GET/v1/reverse GET/v1/resolve GET/v1/search Boundaries GET/v1/boundaries/{geoname_id} Batch POST/v1/batch/cities POST/v1/batch/countries POST/v1/batch/regionsBoundary Search
A coordinate resolves to the administrative areas that contain it, each area has a boundary polygon, and every polygon can be used as a filter. One click on the map below exercises all three — and the three are priced differently, which is the part that surprises people.
Click anywhere on the map. Clicking open ocean is fine — it resolves to nothing, which is a real answer rather than an error.
What just happened
The demo above is the keyless, throttled twin of three ordinary API calls. With a key, the same click is these three requests:
-
Point to area.
GET /v1/resolve?lat=&lon=returns every administrative area whose boundary polygon covers the point, ordered country first. It returns nothing when no polygon covers the point, rather than the nearest thing it can find — that is what separates it from/v1/reverse. 1 quota unit, the standard request cost. -
Area to polygon.
GET /v1/boundaries/{geoname_id}returns that area's boundary as GeoJSON, optionally thinned with?simplify=. 5 quota units — the payload is two to three orders of magnitude larger than an ordinary list response. -
Polygon to the places inside it.
GET /v1/cities?within=<geoname_id>returns only the rows falling geometrically inside that area. It works on/v1/citiesand/v1/postal-codes, takes exactly one id, and a comma-separated list is a422rather than a silently used first value. 2 quota units.
The demo runs the first two. The third needs a key, so the panel above prints the request rather than issuing it.
What each call costs
Your plan's monthly allowance is counted in units, not in requests. Most requests cost one unit. These four do not:
GET /v1/boundaries/{geoname_id}— 5 units?within=on a list endpoint — 2 units?bbox=on a list endpoint — 1 unitGET /v1/resolve— 1 unit
The highest multiplier wins; costs never add up
A request that qualifies for more than one multiplier is charged the largest of them, not
the sum. A boundary fetch that also carries
?bbox= costs
5, not 6. Combining
?within= with
?bbox= costs
2, not 3 — and that one is not merely a rounding
convention in your favour. A bounding box narrows the candidate set through an index
before the exact point-in-polygon test runs, so the combined query is dramatically cheaper
for us to serve than the containment test alone. You are not charged extra for making our
work easier.
A rejected filter still costs throttle slots
Rate limiting and quota metering are two different meters. A
?within= request that is
rejected — a malformed id, a city id where an area was required — has already taken
2 slots from your per-second allowance, because the
cost is known before the request is validated. It meters
1 quota unit against your monthly allowance, because
nothing was served. Getting a filter wrong in a tight loop shows up as
429s well before it shows
up on the invoice.
What this means on the Free plan
Free includes 5,000 units per month. At 5 units each, that is
1,000 boundary fetches — not 5,000. It is 2,500
?within= requests, or
5,000 resolves.
The per-second limit interacts with this in a way worth stating outright. A route's cost is clamped down to your plan's own per-second limit before it is charged against it, because an unclamped 5-unit route would be unreachable on a 2-per-second plan rather than merely expensive. The clamp hands Free one boundary fetch per second: reachable, still metered. Free's monthly allowance binds long before that rate does.
The full per-plan arithmetic is on the pricing page.
Coverage, and where it runs out
Country boundaries are close to complete: 235 of 236 parseable admin-0 records carry geometry (99.6%). Region boundaries are a different story, and the headline number hides most of it.
Of 52,840 geoBoundaries polygons considered, 45,138 matched a region row (85.4%). That produced 29,691 region rows carrying geoBoundaries geometry; with the 4,379 rows already carrying Natural Earth geometry, 34,070 regions have a polygon.
Check your countries before you build on this. The
match rate is not evenly distributed and the per-country reality is what will decide
whether this works for you. At ADM1, Spain landed 16 of 22 and the Philippines 16 of 18 —
but Italy gained zero of 24, France 4 of 35, and Great
Britain 2 of 18. A polygon we could not match to a region row unambiguously is refused
rather than guessed, because a wrong match is served through
?within= as authoritative.
Coverage stops at countries and administrative regions. There is no promise of anything below the levels described here, and there is no endpoint that would serve one.
Containment performance
?within= does not test your
point against one enormous polygon. Every area's boundary is pre-cut into tiles —
106,187 of them across the dataset — so the index prunes to a handful of small pieces
before the exact test runs. The measured difference against the whole-polygon form on the
same targets, in the same run:
- United States — 19,862.0 ms p50 / 24,439.1 ms p95 against the whole polygon, versus 157.6 / 175.1 ms against the tiles. Roughly 140x.
- Russia — 5,321.8 / 5,425.0 ms versus 115.3 / 138.8 ms. Roughly 39x.
Across the probed targets the tiled p95 runs from 22.7 ms (Northern Division, Fiji) to 175.1 ms (United States), with Canada at 52.9 ms and Bavaria at 37.6 ms.
There is no blanket latency guarantee here, and we are not going to invent one. The cost of a containment query tracks the number of rows that actually match, not the size or the tile count of the area — so a dense country with many matching cities is slower than a large empty one. China's containment query measures 590 to 628 ms against a 1.0 s statement threshold, about 1.6x of headroom. Query the areas you care about and measure them; do not extrapolate from the table above.
Changes
2026-08-30 — region geometry now requires a paid plan
Breaking change. Shipped to production on 2026-08-30.
Four doors led to region geometry. All four changed for Free keys, and
three of them changed silently — still
200, with the
geometry key simply
absent and no error code anywhere on the path. If your integration reads
data.geometry and
started finding nothing there, this is why.
-
GET /v1/regions/{id}— silent. Geometry was in the default response shape here, so this one breaks without your asking for anything new. -
GET /v1/regionswith?fields=naming geometry — silent. Omitted from every row. -
GET /v1/countries/{code}/regionswith?fields=naming geometry — silent. Omitted from every row. -
GET /v1/boundaries/{geoname_id}for a region-level id — loud. A403carrying error codetier_upgrade_requiredand anupgrade_urlin the body.
The two list routes are the larger half of this and the easier half to miss.
GET /v1/regions?fields=geometry&limit=100
returned 9.5 MB of region GeoJSON for a single quota unit, while a single polygon
through /v1/boundaries/{geoname_id}
costs five. The list door was the cheaper door by two orders of magnitude.
GET /v1/countries/{code}
is not affected.
Country geometry is deliberately not gated and stays available on every plan including
Free. That asymmetry is the shape of the offer, not an unfinished edge of it: Free gets
country boundaries, and admin-level boundaries are what a paid plan buys.
Migration. Either:
- Move to a paid plan — Basic, Pro or Business — which restores region geometry on all four routes. See pricing.
-
Or call
GET /v1/boundaries/{geoname_id}with a country-level id, which serves country geometry on every plan including Free at 5 quota units per fetch.
Next
- Plans and limits: Pricing — what each plan's monthly units buy once the multipliers above are applied.
- First request: Getting Started — key, header, and a copy-paste call in four languages.
- Where the polygons come from: Attribution — the boundary sources and the licence each one carries.