---
title: "Reverse geocode (versioned alias, JSON body)"
method: POST
path: "/v1/reverse"
tags: ["Versioned aliases (v1)"]
---

# Reverse geocode (versioned alias, JSON body)

`POST /v1/reverse`

## Request

- Base URL: `https://api.acuris-geo.com`
- URL: `https://api.acuris-geo.com/v1/reverse`
- Auth: one of:
  - API key in header `X-Acuris-Key`
  - HTTP bearer
  - API key in header `X-API-Key`
  - API key in query parameter `key`
  - none

Versioned alias of `GET /reverse` — same handler, same response shape, same
billing. Accepts `lat`/`lng`/`radius_m`/`limit`/`country` as a JSON body
instead of a query string (scalar body fields are folded onto the query
params; query-string values win on collision). Results are sorted by distance
ascending and include a `distance_m` field.

**Credits:** Stripe-plan keys: flat `reverse=1` transaction per request, regardless
of `limit`. Trial/dev keys: 1 transaction per returned address, pre-capped to the
remaining balance. `units_charged` echoes the number of addresses returned
(equals the trial-key debit; the Stripe debit is always 1).

**Availability:** Available for ~160 countries. Detected per country from the
reference DB (rooftop-coordinate index) and expands automatically; countries
without it return **501** `reverse_not_available` — currently including
`che`, `jpn`, `tur`, `kor`, `isr`, `arg`, `chl`, `vnm`, `pak`, `phl`,
`hkg`; `gbr` reverse is served via the PAF host.

`GET /v1/reverse` with the same query parameters as the unversioned route
is also accepted.

## Request body

- object
  - `country` string, required
  - `lat` number, float, required
  - `lng` number, float, required
  - `radius_m` integer: Search radius in metres. Capped at `50000`.
  - `limit` integer: Max results to return. Capped at `10`. **Ignored for RapidAPI traffic** — forced to `1`.

## Response `200`

Match found. When `limit=1` (default) the body is a single result;
when `limit>1` the body wraps `results: []` with `count`.

- ReverseResponse: Reverse geocoding response. When the request's `limit=1` (default), the body is a single flat result. When `limit>1`, the body contains a `results` array of flat result objects plus `count` and top-level `match_type` / `match_score` reflecting the best (closest) match.
  - `country` string
  - `lat` number, float: Latitude of the matched address (not the query point).
  - `lng` number, float
  - `distance_m` integer: Distance in metres from the query point to this result.
  - `accuracy_type` string, nullable: What the returned coordinate represents. `null` on `no_match`. **This is an OPEN set.** New tiers are added as reference data improves. Clients MUST pass unrecognized values through rather than throwing, and SHOULD branch on `point_coordinate` rather than on these literals. A **point coordinate** identifies one address: * `rooftop` — a point on the building itself. * `arrival_point` — the address's entrance, gate or curb-side access point. A point for *this* address, typically a few metres from the roof — deliberately not claimed as the building footprint. Everything else is derived rather than observed — a segment or an area, not an address: * `street_interpolated` — HNO estimated along a street segment polyline (SEG cascade) from the position of neighbouring house numbers. * `street_centroid` — street confirmed; exact HNO not located. **Singapore:** block numbers are not linear along a road and a letter-suffixed block (e.g. `700D`) is a separate building with its own postcode, so Singapore never returns `street_interpolated`. An unconfirmed block (or a street with no block) answers `street_centroid` at the street's centre, and `standardized.postcode` is left empty unless you supplied a postcode that exists on that street — a postcode names one building, and another building's postcode is never shown as yours. * `landmark_proximity` — India only. The address itself resolved no further than its postcode/locality, so the coordinate is that of a LANDMARK the caller named in the input ("opp. Swami Samarth Math"). It is a point on a *neighbouring* place, never the address, and it is only ever used when it out-ranks what would otherwise be returned. The landmark used is named back to you in `landmark_used` (`name`, `matched_from`, `scope`, `source`). * `postcode_centroid` — only postcode matched. * `locality_centroid` — only city/locality matched. * `admin_centroid` — only admin region (state/province) matched.
  - `point_coordinate` boolean: `true` when the coordinate identifies one address (`rooftop` or `arrival_point`), `false` for any derived/area tier. **Not yet emitted by `/reverse`** — until it is, derive it from `accuracy_type` (`rooftop` / `arrival_point`).
  - `match_type` 'rooftop' | 'parcel_centroid' | 'interpolated' | 'street_level' | 'zip_centroid' | 'city_centroid' | 'no_match' | 'non_deliverable': Internal/legacy precision tier with the score table below. New clients should prefer `accuracy_type` for branching. Unlike `accuracy_type`, this enum is CLOSED — the score table is fixed and no further values will be added. An `arrival_point` coordinate is reported here as `rooftop`, because the address is premise-verified either way; only `accuracy_type` states what the coordinate is. Score table: * `rooftop` → 1.0 * `parcel_centroid` → 0.9 * `interpolated` → 0.8 * `street_level` → 0.6 * `zip_centroid` → 0.4 * `city_centroid` → 0.2 * `no_match` → 0.0 * `non_deliverable` → 0.0 — the input is a PO Box / Rural Route / Postfach etc. and can never resolve to a street address (USA/CAN/MEX/DEU/AUT/CHE detection). Returned on the 400 body (with `non_deliverable_type`) unless `iso20022: true` opted in to a labelled 200.
  - `match_score` number, float
  - `input_corrected` boolean: Always `false` for reverse geocoding (input is coordinates, not an address).
  - `street` string
  - `hno` string
  - `city` string
  - `state` string: Present for countries with regions (USA, CAN).
  - `zip` string
  - `query` object: Echo of the effective query parameters (after clamping).
    - `lat` number
    - `lng` number
    - `radius_m` integer
  - `results` object[]: Only present when `limit > 1`.
  - `count` integer: Only present when `limit > 1`; equals `results.length`.
  - `units_charged` integer: Echoes the number of addresses returned (`count`, or `1` when the body is a flat single result). This equals the trial-key debit; Stripe-plan keys are debited a flat `1` per request regardless. Always `1` for RapidAPI traffic.
  - `balance` integer: Remaining geocode transactions after keyed call.
  - `free_remaining` integer: Remaining anonymous geocodes for this IP today.

## Other responses

- `400`: Missing/invalid country, lat, lng, radius_m, or limit
- `401`: Invalid or inactive API key
- `403`: Insufficient transaction balance, trial expired, or anonymous limit reached. Key-level 403s: `{"error": "key_blocked", "message"}` when the API key has been blocked (the `message` says why), and `{"error": "ip_not_allowed", "message", "ip"}` when the key is restricted to approved source IP addresses and this request came from another one (`ip` is the address we saw). Source-IP restriction is optional and set up per key on request via support.
- `404`: No address within `radius_m`. Body includes `match_type: no_match` and `reasons: ["no_address_within_radius"]`.
- `429`: Too many requests (anonymous limit or WAF/limiter)
- `501`: Reverse geocoding not available for this country (`reverse_not_available`).

---

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