Docs
Overview
Documentation built for shipping a production geocoding integration quickly.
Wherabouts gives you a clean HTTP interface for address autocomplete, reverse geocoding, nearby lookup, and canonical address retrieval. This page is the opinionated path through the API: what to call first, how authentication works, what validation happens on the server, and how to structure a reliable implementation.
First request
Quickstart
If you are integrating Wherabouts for the first time, start with autocomplete. It exercises the authentication path, the public API hostname, and the response envelope you will see across the address search surface.
curl
curl "https://api.wherabouts.com/api/v1/addresses/autocomplete?q=123+Main+St&country=AU" \
-H "Authorization: Bearer wh_live_your_api_key"Contract
Use the published OpenAPI spec
The public REST contract is also published as an OpenAPI document so SDK work, contract review, and external tooling can target the same source of truth as the docs page.
SDK preview
Use the first-party JS/TS client
Phase 2 starts by giving JavaScript and TypeScript consumers a typed client instead of forcing every integration to hand-roll `fetch` wrappers. The current preview SDK is a lightweight REST client over the same public v1 endpoints documented here.
TypeScript SDK
import { createWheraboutsClient } from "@wherabouts/sdk";
const client = createWheraboutsClient({
apiKey: process.env.WHERABOUTS_API_KEY!,
});
const payload = await client.addresses.autocomplete({
q: "123 Main St",
country: "AU",
limit: 5,
});Authentication
Send the API key on every request
The public address API is protected with API keys rather than session auth. That keeps server-to-server usage simple while still allowing the dashboard to manage keys and inspect usage safely.
API surface
Core endpoints
These are the four address endpoints currently exposed in the app. The docs below mirror the actual path names and request validation behavior implemented on the server today.
/api/v1/addresses/autocompleteAutocomplete
Autocomplete returns up to 20 ranked address candidates for a partial query. It is the best default starting point for signup forms, checkout flows, address capture, and internal tools that need fast suggestions without embedding a map SDK.
Example Response
{
"results": [
{
"id": 104233,
"formattedAddress": "123 Main St, Melbourne VIC 3000, AU",
"streetAddress": "123 Main St",
"locality": "Melbourne",
"state": "VIC",
"postcode": "3000",
"country": "AU",
"latitude": -37.8136,
"longitude": 144.9631
}
],
"count": 1
}/api/v1/addresses/reverseReverse Geocoding
Reverse geocoding searches for the closest address within 200 meters of the provided coordinate pair and returns a single best match. This is a strong fit for mobile location capture, driver tooling, and QA flows that need to validate map-selected points.
Example Response
{
"address": {
"id": 104233,
"formattedAddress": "123 Main St, Melbourne VIC 3000, AU",
"streetAddress": "123 Main St",
"locality": "Melbourne",
"state": "VIC",
"postcode": "3000",
"country": "AU",
"longitude": 144.9631,
"latitude": -37.8136,
"confidence": 92
},
"distance": 18,
"query": {
"lat": -37.8136,
"lng": 144.9631
}
}/api/v1/addresses/nearbyNearby Search
Nearby search returns multiple address records ordered by distance from the query point. It is useful when you need to inspect candidate addresses near an asset, confirm catchment coverage, or build operational tooling for support and logistics teams.
Example Response
{
"results": [
{
"id": 104233,
"country": "AU",
"state": "VIC",
"locality": "Melbourne",
"postcode": "3000",
"streetName": "Main",
"streetType": "St",
"numberFirst": "123",
"longitude": 144.9631,
"latitude": -37.8136,
"distance": 42
}
],
"count": 1,
"query": {
"lat": -37.8136,
"lng": 144.9631,
"radius": 500
}
}/api/v1/addresses/{id}Address by ID
After you have selected an address from autocomplete or another indexed flow, use the address ID endpoint to retrieve the underlying record again without re-running search. This keeps downstream workflows deterministic and removes ambiguity around free-form input.
Example Response
{
"id": 104233,
"country": "AU",
"state": "VIC",
"locality": "Melbourne",
"postcode": "3000",
"streetName": "Main",
"streetType": "St",
"streetSuffix": null,
"buildingName": null,
"flatType": null,
"flatNumber": null,
"levelType": null,
"levelNumber": null,
"numberFirst": "123",
"numberLast": null,
"longitude": 144.9631,
"latitude": -37.8136,
"confidence": 92,
"gnafPid": "GAVIC123456789"
}/api/v1/addresses/geocodeForward Geocode
Forward geocoding converts a human-readable address into a canonical address record with latitude and longitude. Pass an unstructured query in `q` or set `structured=true` and supply individual fields (`street`, `locality`, `state`, `postcode`). The server returns the single best match.
Example Response
{
"id": 104233,
"formattedAddress": "123 Main St, Melbourne VIC 3000, AU",
"streetAddress": "123 Main St",
"locality": "Melbourne",
"state": "VIC",
"postcode": "3000",
"country": "AU",
"latitude": -37.8136,
"longitude": 144.9631,
"confidence": 92
}/api/v1/geocode/batchBatch Submit
Batch geocoding accepts an array of address strings and processes them asynchronously. The endpoint returns a `jobId` immediately; poll `GET /api/v1/geocode/batch/{jobId}` until `status` is `completed`, then fetch results.
Example Response
{
"jobId": "job_abc123xyz"
}/api/v1/geocode/batch/{jobId}Batch Poll
Poll this endpoint after submitting a batch job. When `status` is `completed` the results endpoint is ready. When `status` is `failed` the job encountered an unrecoverable error.
Example Response
{
"jobId": "job_abc123xyz",
"status": "processing",
"total": 500,
"processed": 142
}/api/v1/geocode/batch/{jobId}/resultsBatch Results
Once `status` is `completed`, fetch the full result set from this endpoint. Results are ordered to match the input array — each entry contains either a resolved address or a `null` match with an error reason.
Example Response
{
"results": [
{
"input": "123 Main St Melbourne VIC",
"match": {
"id": 104233,
"formattedAddress": "123 Main St, Melbourne VIC 3000, AU",
"latitude": -37.8136,
"longitude": 144.9631,
"confidence": 92
}
},
{
"input": "not a real address xyz",
"match": null,
"error": "no_match"
}
]
}/api/v1/zonesCreate Zone
Creates a named zone defined by a GeoJSON Polygon geometry. Zones are used for point-in-polygon tests, address enumeration, and triggering webhook events when devices enter or exit. Each project may have up to 500 zones.
Example Response
{
"id": "zone_01HX3K9R",
"projectId": "proj_abc",
"name": "Melbourne CBD",
"createdAt": "2026-06-05T00:00:00.000Z"
}/api/v1/zonesList Zones
Returns the full list of zones for the given project. Zone geometry (GeoJSON) is included so the map can render all polygons in one request.
Example Response
{
"zones": [
{
"id": "zone_01HX3K9R",
"name": "Melbourne CBD",
"geometry": {
"type": "Polygon",
"coordinates": [[[144.95, -37.82], [144.97, -37.82], [144.97, -37.81], [144.95, -37.81], [144.95, -37.82]]]
},
"createdAt": "2026-06-05T00:00:00.000Z"
}
]
}/api/v1/zones/{id}Get Zone
Returns the full zone record including geometry. Useful for re-rendering a specific zone on the map or verifying the stored polygon.
Example Response
{
"id": "zone_01HX3K9R",
"projectId": "proj_abc",
"name": "Melbourne CBD",
"geometry": {
"type": "Polygon",
"coordinates": [[[144.95, -37.82], [144.97, -37.82], [144.97, -37.81], [144.95, -37.81], [144.95, -37.82]]]
},
"createdAt": "2026-06-05T00:00:00.000Z"
}/api/v1/zones/{id}Update Zone
Replaces the zone's name, geometry, or both. Partial updates are supported — omit fields you do not want to change. The geometry is re-validated by PostGIS on every update.
Example Response
{
"id": "zone_01HX3K9R",
"projectId": "proj_abc",
"name": "Melbourne CBD (updated)",
"updatedAt": "2026-06-05T01:00:00.000Z"
}/api/v1/zones/{id}Delete Zone
Deletes the zone record. Any webhook subscriptions listening to this zone's boundary events will stop firing. This action cannot be undone.
Example Response
204 No Content/api/v1/zones/containsZone Contains
Point-in-polygon test using PostGIS ST_Contains. Provide a project ID and a coordinate; the API returns all zones that contain the point. Returns an empty array when the point is outside all zones.
Example Response
{
"zones": [
{
"id": "zone_01HX3K9R",
"name": "Melbourne CBD"
}
]
}/api/v1/zones/{id}/addressesZone Addresses
Returns paginated address records from the GNAF dataset that are spatially contained within the zone polygon. Large zones may contain tens of thousands of addresses; the response is capped at 10 000 per request and includes a `total` count.
Example Response
{
"addresses": [
{
"id": 104233,
"formattedAddress": "123 Main St, Melbourne VIC 3000, AU",
"latitude": -37.8136,
"longitude": 144.9631
}
],
"total": 3412
}/api/v1/devices/{deviceId}/locationPush Location
Upserts the device's latest position. The server runs a PostGIS point-in-polygon check against all project zones and computes enter/exit events relative to the device's previous position. Any zone crossings trigger configured webhook subscriptions.
Example Response
{
"deviceId": "truck-42",
"recorded": true,
"crossings": [
{ "zoneId": "zone_01HX3K9R", "event": "zone.enter" }
]
}/api/v1/devices/{deviceId}/zonesDevice Zones
Returns the zones that contain the device's most recently recorded position. Useful for real-time dashboards that need to display a device's current zone membership without re-running a PIP query client-side.
Example Response
{
"deviceId": "truck-42",
"zones": [
{
"id": "zone_01HX3K9R",
"name": "Melbourne CBD"
}
]
}/api/v1/webhooksCreate Webhook
Creates a webhook subscription that receives POST requests whenever a device crosses a zone boundary. The response includes a `signingSecret` that is shown exactly once — store it securely. All subsequent deliveries are signed with HMAC-SHA256 using that secret.
Example Response
{
"id": "wh_01HX9AB",
"projectId": "proj_abc",
"url": "https://your-app.example.com/webhooks/wherabouts",
"events": ["zone.enter", "zone.exit"],
"signingSecret": "whsec_abc123xyz",
"createdAt": "2026-06-05T00:00:00.000Z"
}/api/v1/webhooksList Webhooks
Returns all active and failing webhook subscriptions for the project. The `signingSecret` is never included in list responses — it is only returned once at creation time.
Example Response
{
"webhooks": [
{
"id": "wh_01HX9AB",
"url": "https://your-app.example.com/webhooks/wherabouts",
"events": ["zone.enter", "zone.exit"],
"status": "active",
"createdAt": "2026-06-05T00:00:00.000Z"
}
]
}/api/v1/webhooks/{id}Delete Webhook
Permanently deletes the webhook subscription. No further deliveries will be attempted. Use this to clean up failing subscriptions or remove subscriptions that are no longer needed.
Example Response
204 No Content/api/v1/regionsClassify Coordinate
Classifies a latitude/longitude into the official ABS/ASGS regions that contain it — state, SA1–SA4, LGA, postcode, electoral divisions, and mesh block — keyed by layer. Outside Australia the regions object is empty.
Example Response
{
"query": { "lat": -37.8136, "lng": 144.9631 },
"regions": {
"state": { "code": "2", "name": "Victoria" },
"sa2": { "code": "206041122", "name": "Melbourne" },
"lga": { "code": "24600", "name": "Melbourne (C)" },
"poa": { "code": "3000", "name": "3000" }
}
}Async lifecycle
Batch geocoding lifecycle
Batch geocoding is a three-step async flow. Submit the job, poll for completion, then fetch results.
Batch lifecycle (JavaScript)
// Step 1 — submit
const { jobId } = await fetch("https://api.wherabouts.com/api/v1/geocode/batch", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": "wh_live_your_api_key",
},
body: JSON.stringify({ addresses: ["123 Main St Melbourne VIC", "456 George St Sydney NSW"] }),
}).then((r) => r.json());
// Step 2 — poll until completed
let status = "pending";
while (status !== "completed" && status !== "failed") {
await new Promise((resolve) => setTimeout(resolve, 2000));
const poll = await fetch(`https://api.wherabouts.com/api/v1/geocode/batch/${jobId}`, {
headers: { "X-API-Key": "wh_live_your_api_key" },
}).then((r) => r.json());
status = poll.status;
}
// Step 3 — fetch results
const { results } = await fetch(
`https://api.wherabouts.com/api/v1/geocode/batch/${jobId}/results`,
{ headers: { "X-API-Key": "wh_live_your_api_key" } }
).then((r) => r.json());Webhook delivery
Webhook delivery and HMAC verification
Wherabouts delivers webhook events by POSTing to your subscription URL when a device crosses a zone boundary. Use the HMAC signature to verify authenticity.
Operational behavior
Errors and constraints
The API keeps failures explicit and predictable. The main categories are authentication failures, request validation failures, and not-found responses for queries that do not resolve to an address.
Error Envelope
{
"error": {
"code": "bad_request",
"message": "Query parameter 'q' must be at least 2 characters."
}
}Observability
Health checks and timing
Baseline platform observability starts with a public health surface and response timing data. These routes are intended for synthetic checks, deployment verification, and integration smoke tests.
Delivery checklist
Integration checklist
If you want the cleanest production implementation, treat the docs as an execution checklist rather than a reference page you skim once.
Next steps
Move from docs to implementation
Once your first request works, the fastest next move is to generate a dedicated API key for the environment you are shipping and test the exact endpoint mix you expect to use in production.