---
title: "Explore administrative places near a point"
method: GET
path: "/v1/explore"
tags: ["Places"]
---

# Explore administrative places near a point

`GET /v1/explore`

## Request

- Base URL: `https://geo.thinair.co`
- URL: `https://geo.thinair.co/v1/explore`
- Auth: HTTP bearer

Browsing / discovery search — administrative places (cities, neighbourhoods, boroughs, counties, regions) near a location. Supports population filtering, ranking by significance or distance, and per-layer radius policy. Venues and points of interest are served by /v1/places, not here.

## Query parameters

- `location` string: Center point — free-text address or 'lat,lon' string.
- `lat` number: Center latitude (alternative to `location`).
- `lon` number: Center longitude (alternative to `location`).
- `layers` string: Comma-separated layers: locality, neighbourhood, borough, county, region (default locality). venue, address and street are not served here — use /v1/places for venues and points of interest. An unsupported or unknown layer is rejected with 400 INVALID_QUERY.
- `radius` string: Search radius. Bare numbers are miles unless `units` is set — `radius=30` is 30 miles, `radius=30&units=kilometers` is 30 km; kilometers via `units` or a km suffix ('150km'); aliases mi/km/imperial/metric. A value carrying its own unit suffix ('150km', '30mi') is also accepted for compatibility and its suffix wins. Per-layer policy: locality / county / region default 150 km (93.206 mi) and cap at 500 km (~310 mi); neighbourhood / borough default 25 km (15.534 mi) and cap at 100 km (~62 mi). A request naming several layers takes the TIGHTEST default and cap among them. An over-maximum radius returns HTTP 200 with a `structured_error` of `radius_exceeds_max` naming the governing cap and no items.
- `units` 'miles' | 'kilometers': Units for a bare numeric `radius`: miles (default) or kilometers; aliases mi, mile, imperial (miles) and km, kilometer, kilometre, kilometres, metric (kilometers), case-insensitive, echoed as miles|kilometers. An empty `units=` is treated as absent. Ignored when `radius` already carries a unit suffix (a contradicting pair is rejected). On its own it does not supply a radius — the default is per layer (150 km for locality/county/region, 25 km for neighbourhood/borough; a mixed request takes the tightest) — but it selects the display unit of `meta.radius` / `meta.units`.
- `size` integer: Max results (default 10, clamped 1..50).
- `min_population` integer: Minimum population filter; applied JS-side.
- `sort` 'population' | 'distance' | 'combined': Ranking basis (default: population — significance, using population where the source has it, with distance as the tiebreak). `distance` is nearest first; `combined` blends population against a distance decay. The basis applied is echoed in `meta.ranking`.
- `boundary_country` string: ISO 3166-1 alpha-2 or alpha-3 country code restriction.

## Response `200`

Explore results — items list with optional upstream_error fallback.

- ExploreResponse
  - `items` ExploreItem[], required
    - `name` string
    - `label` string
    - `layer` string: The place's own layer — one of locality, neighbourhood, borough, county, region. This is the classification; there is no other tier field.
    - `population` number: Population, present ONLY where the source carries one (coverage is partial and varies by layer). Absent means unknown — never zero-filled.
    - `distance_km` number: Distance from the center point in kilometers (the canonical unit).
    - `distance` number: The same distance in the request's display unit (`meta.units`), 1 decimal.
    - `distance_unit` 'miles' | 'kilometers': Display unit of `distance` — always equal to `meta.units`.
    - `bearing` integer: Initial bearing from the center point to this place, integer degrees 0-359 clockwise from true north.
    - `coordinates` object
      - `lat` number, required: Latitude in decimal degrees, WGS84.
      - `lon` number, required: Longitude in decimal degrees, WGS84.
    - `region` string
    - `country` string
  - `meta` object, required
    - `upstream_ms` number, required
    - `feature_count` number, required
    - `cacheHit` boolean
    - `query` string, required
    - `center_lat` number
    - `center_lon` number
    - `center_label` string
    - `radius_km` number: Search radius actually applied, in kilometers (the canonical unit) — 150 on the default path. Present on every response except the legacy unparseable-radius fall-through, where the engine decided.
    - `radius` number: The applied radius expressed in `units`, rounded to 3 decimals — 93.206 (miles) on the default path, 150 with `units=kilometers`.
    - `units` 'miles' | 'kilometers': Display unit of `radius`: the request's `units` if sent; else the unit of a suffixed `radius` string ('30mi' → miles, '30km' / '800m' → kilometers); else miles — the platform default, which also covers a bare numeric `radius` and the no-input path. `units` alone does not supply a radius; it only selects this.
    - `layers` string[]: The layers actually served, in the order requested. A layer that received no share of `size` is absent here and `truncated` is true.
    - `ranking` string: Plain-language statement of the ranking basis actually applied.
    - `truncated` boolean: True when a layer produced more candidates than its share of `size`. False means no truncation was observed — with no upstream total, it is not a claim that nothing more exists.
    - `total_candidates` integer: Count of matching candidates upstream, before this API's `min_population` filter and duplicate-place removal are applied — NOT the number of items in this response's `items` array, and not how many more results you could page through. Present ONLY when the backend reports a true, exact total for every layer served; omitted when it cannot (including whenever `min_population` is greater than zero; an explicit `min_population: 0` means no filter and keeps the exact total), and never estimated.
  - `upstream_error` string, nullable
  - `structured_error` StructuredError
    - `code` string, required: Stable, snake_case identifier callers can switch on.
    - `message` string, required
    - `details` object

## Other responses

- `400`: Invalid query parameters.
- `429`: Quota exceeded or rate-limited.

---

[API](https://skmtc.dev/thinair/apis/thinair-geo-api.md) · [All operations](https://skmtc.dev/thinair/apis/thinair-geo-api/llms.txt) · [OpenAPI document](https://skmtc.dev/thinair/apis/thinair-geo-api/revisions/8fac2d5e4b34?raw)
