Three GET endpoints, no key, no rate limit, CORS open to everything. Encoding and decoding are pure functions over a committed word table, so every response is deterministic and cacheable forever — the routes are statically rendered and served from the edge.
GET /api/encode
Coordinates → address.
| Parameter | Required | Meaning |
|---|---|---|
| lat | yes | WGS84 latitude in degrees |
| lng | yes | WGS84 longitude in degrees |
| depth | no | How many words to return, 1–4. Defaults to 3. |
Returns the address, the per-word breakdown with each word’s level and semantic group, the cell centre in both CRSs, the point scale factor there, the footprint as GeoJSON, and the chain of containing addresses.
A position outside the coverage area returns 422 with error: "out_of_extent" rather than a nonsense address.
GET /api/decode
Address → coordinates. Same response shape as encode.
| Parameter | Required | Meaning |
|---|---|---|
| address | yes | The address. Dots, spaces or slashes all work, and a leading /// is accepted. |
| risk | no | Set to include the confusion set — the plausible wrong addresses and how far each lands you. |
A word in the wrong slot returns error: "wrong-level", which a flat scheme cannot detect at all — with one global vocabulary, any word is legal in any position.
GET /api/neighbours
The eight cells touching an address, with the true ground distance to each centre. This endpoint has no equivalent in a non-topological scheme.
/api/outages/reports
The one stateful endpoint, behind the outages screen. Live power-cut reports: a three-word address, an anonymous client token, and an expiry 10 minutes after the reporter last touched it — or the collective expiry of an outage that 3 or more networks corroborate, 15 minutes and growing as networks join; each report says whether it stands corroborated. GET returns every report still inside its cool-off; POST with { address, clientId, turnstile } creates a report or refreshes the caller’s existing one on that cell — the turnstile token comes from the site key the response carries as turnstileSiteKey, and a new report without a valid one is 403 verification_failed; DELETE with { id, clientId } ends it and deletes the row; expired rows are deleted on the next request, so nothing is kept. Every response carries the full active list. An address on the cell one of the board’s places stands on returns 409 board_place: the board’s own ground is never a home. A refresh earlier than the last 5 minutes of the countdown returns 409 too_early. Rate-limited to 3 active reports per client and 12 per network (429 rate_limited / network_limited) and to a dozen writes a minute per network and per client (429 too_many), never cached, and — unlike the three endpoints above — not part of the stable public surface: it exists to serve the screen.
Its companion POST /api/outages/contacts with { address, channel, authorityId, clientId } records that the caller reached the board from a cell that has a live report — channel is phone or whatsapp, authorityId one of the board places — one row per device, cell and channel, refreshed rather than repeated. Every response, from either endpoint, carries both lists as reports and contacts.
Notes for integrators
- Everything is WGS84 on the way in. Coordinates from Survey of India toposheets are on the Everest 1830 datum and differ by 200–300 m in this region. Datum- shift before you encode — see accuracy.
- Truncate, don’t round. To disclose a coarser position, drop trailing words rather than reducing coordinate precision. A prefix is a real address for a real, larger cell; a rounded coordinate is a point that may sit in a different cell entirely.
- Cache freely. Responses are pure functions of their inputs and will never change for a given lexicon version. They are served with a one-year immutable cache header.
- Coverage is finite. A 50 km × 60 km extent over the Chennai Metropolitan Area. Anything outside it is an error, deliberately, rather than a wrapped-around address.
Doing it without the network
The whole model — projection, tessellation, lexicon — is about 70 KB of committed JSON and a few hundred lines of arithmetic. For an offline or embedded client, porting it is strictly easier than calling this API, and the method page specifies it completely.