Docs
Overview
Documentation built for shipping a production geocoding integration quickly.
Locnative 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 Locnative 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.locnative.com/api/v1/addresses/autocomplete?q=123+Main+St&country=US" \
-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 { createLocnativeClient } from "@locnative/sdk";
const client = createLocnativeClient({
apiKey: process.env.LOCNATIVE_API_KEY!,
});
const payload = await client.addresses.autocomplete({
q: "123 Main St",
country: "US",
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.
React UI
@locnative/react-ui
Production-ready React components for address autocomplete, forward and reverse geocoding, and structured address fields. Built on the SDK, styled with Tailwind, shipped with a prebuilt stylesheet, accessible to WCAG AA, and compatible with TanStack Form.
Install
npm install @locnative/react-ui @locnative/sdkStylesheet
// Import the stylesheet once, near your app root
import "@locnative/react-ui/styles.css";AddressAutocomplete
Accessible (WAI-ARIA combobox) debounced address search with keyboard navigation, proximity bias, session tokens, i18n strings, and customizable render slots. Provide a client created with createLocnativeClient.
Usage
import { createLocnativeClient } from "@locnative/sdk";
import { AddressAutocomplete } from "@locnative/react-ui";
import "@locnative/react-ui/styles.css";
const client = createLocnativeClient({
apiKey: import.meta.env.VITE_LOCNATIVE_KEY,
});
export function Checkout() {
return (
<AddressAutocomplete
client={client}
placeholder="Start typing an address…"
onSelect={(address) => console.log(address.formattedAddress)}
/>
);
}Props
clientRequiredLocnativeClientSDK client created with createLocnativeClient.
onSelectOptional(address: AddressWithParsed) => voidCalled when a suggestion is selected.
onQueryChangeOptional(query: string) => voidCalled as the input text changes.
placeholderOptionalstringInput placeholder text.
debounceMsOptionaldefault: 300numberDebounce in ms before querying the API.
minCharsToSearchOptionaldefault: 2numberMinimum characters typed before searching.
maxSuggestionsOptionaldefault: 5numberMaximum number of suggestions to show.
enableGeolocationOptionaldefault: falsebooleanUse the browser's geolocation to bias results by proximity.
userLatOptionalnumberExplicit latitude for proximity bias (instead of geolocation).
userLngOptionalnumberExplicit longitude for proximity bias (instead of geolocation).
sessionTokenOptionalstringGroup a run of keystrokes into one billable search (see SDK newSessionToken()).
disabledOptionalbooleanDisable the input.
requiredOptionalbooleanMark the input as required.
errorOptionalstringExternal error message to display.
idOptionaldefault: "locnative-autocomplete"stringid forwarded to the input element.
classNameOptionalstringClass applied to the root container.
i18nStringsOptionalPartial<AddressI18nStrings>Override built-in UI strings (no results, retry, etc.).
renderSuggestionOptional(address: AddressWithParsed, isActive: boolean) => ReactNodeRender a custom suggestion row.
renderEmptyOptional() => ReactNodeRender a custom empty state.
renderLoadingOptional() => ReactNodeRender a custom loading state.
renderErrorOptional(error: Error | null) => ReactNodeRender a custom error state.
AddressFormField
AddressAutocomplete wrapped with a <label> and error styling — a drop-in form field. Accepts every AddressAutocomplete prop plus the fields below.
Usage
import { createLocnativeClient } from "@locnative/sdk";
import { AddressFormField } from "@locnative/react-ui";
import "@locnative/react-ui/styles.css";
const client = createLocnativeClient({
apiKey: import.meta.env.VITE_LOCNATIVE_KEY,
});
export function Checkout() {
return (
<AddressFormField
client={client}
label="Delivery address"
required
onSelect={setAddress}
/>
);
}Props
labelRequiredstringText content of the <label> element.
labelClassNameOptionalstringAdditional class(es) applied to the <label> element.
errorClassNameOptionalstringAdditional class(es) applied to the error text <p> element.
…AddressAutocomplete propsOptionalAddressAutocompletePropsEvery AddressAutocomplete prop is forwarded unchanged (client, onSelect, placeholder, debounceMs, proximity bias, render slots, etc.).
AddressFieldGroup
A controlled group of structured inputs (street, suburb, state, postcode) for editing a full address. Provide value and onChange.
Usage
import { useState } from "react";
import { createLocnativeClient } from "@locnative/sdk";
import {
AddressFieldGroup,
type AddressFieldGroupValue,
} from "@locnative/react-ui";
const client = createLocnativeClient({ apiKey: "..." });
const EMPTY: AddressFieldGroupValue = {
street: "",
suburb: "",
state: "",
postcode: "",
};
function MyForm() {
const [address, setAddress] = useState<AddressFieldGroupValue>(EMPTY);
return (
<AddressFieldGroup client={client} value={address} onChange={setAddress} />
);
}Props
clientRequiredLocnativeClientSDK client created with createLocnativeClient.
valueRequiredAddressFieldGroupValueControlled value for the field group.
onChangeRequired(value: AddressFieldGroupValue) => voidChange handler called with the updated value on any field edit.
streetLabelOptionaldefault: "Street Address"stringOverride the street address field label.
suburbLabelOptionaldefault: "Suburb"stringOverride the suburb field label.
stateLabelOptionaldefault: "State"stringOverride the state field label.
postcodeLabelOptionaldefault: "Postcode"stringOverride the postcode field label.
disabledOptionaldefault: falsebooleanDisables all fields.
classNameOptionalstringAdditional CSS class applied to the root container.
ForwardGeocodeInput
Resolves a free-text address string to coordinates (forward geocoding) as the query prop changes. Controlled: the parent owns query and receives results via onResult. Renders a read-only display of the resolved latitude, longitude pair.
Usage
import { createLocnativeClient } from "@locnative/sdk";
import { ForwardGeocodeInput } from "@locnative/react-ui";
const client = createLocnativeClient({ apiKey: "..." });
function MyComponent() {
const [query, setQuery] = React.useState("");
return (
<>
<input value={query} onChange={(e) => setQuery(e.target.value)} />
<ForwardGeocodeInput
client={client}
query={query}
onResult={(r) => console.log(r.latitude, r.longitude)}
/>
</>
);
}Props
clientRequiredLocnativeClientSDK client created with createLocnativeClient.
queryRequiredstring | nullAddress text to geocode. null or empty string skips the request.
onResultOptional(r: { latitude: number | null; longitude: number | null; formattedAddress: string | null }) => voidCalled whenever the resolved result changes. All fields are null when no result is available.
placeholderOptionaldefault: "Coordinates will appear here"stringPlaceholder text for the read-only display input.
idOptionalstringid forwarded to the input element.
classNameOptionalstringAdditional CSS class applied to the input element.
disabledOptionaldefault: falsebooleanDisables the input.
ReverseGeocodeInput
Resolves a latitude/longitude pair to the nearest address (reverse geocoding). No request is made until both coordinates are non-null.
Usage
import { createLocnativeClient } from "@locnative/sdk";
import { ReverseGeocodeInput } from "@locnative/react-ui";
const client = createLocnativeClient({ apiKey: "..." });
function MyComponent() {
const [coords, setCoords] = React.useState<{
lat: number | null;
lng: number | null;
}>({ lat: null, lng: null });
return (
<ReverseGeocodeInput
client={client}
latitude={coords.lat}
longitude={coords.lng}
onResult={(r) => console.log(r.address, r.distance)}
/>
);
}Props
clientRequiredLocnativeClientSDK client created with createLocnativeClient.
latitudeRequirednumber | nullLatitude to reverse-geocode. null suppresses the request.
longitudeRequirednumber | nullLongitude to reverse-geocode. null suppresses the request.
onResultOptional(r: { address: string | null; distance: number | null }) => voidCalled whenever the resolved address changes. Both fields are null when no result is available.
placeholderOptionaldefault: "Address will appear here"stringPlaceholder text for the read-only display input.
idOptionalstringid forwarded to the input element.
classNameOptionalstringAdditional CSS class applied to the input element.
disabledOptionaldefault: falsebooleanDisables the input.
Vue UI
@locnative/vue-ui
The Vue 3 counterpart to react-ui — the same address autocomplete, geocoding inputs, and structured address fields. Built on the SDK and styled with Tailwind.
Components are planned for Phase 2
Today the package ships the shared types and utilities the components build on. The Vue 3 SFC components are not yet exported — the reference below documents the intended API.
Install
npm install @locnative/vue-ui @locnative/sdkAvailable today
The package exports the building blocks the components share — usable right now.
Exports
import {
toAddressWithParsed,
cn,
type AddressWithParsed,
type AddressI18nStrings,
type AddressSuggestionInput,
} from "@locnative/vue-ui";toAddressWithParsed(suggestion)Maps a raw SDK AddressSuggestion into the flattened AddressWithParsed shape the components use (formattedAddress, latitude, longitude, plus parsed streetAddress, suburb, state, postcode, and country).
cn(...classes)The clsx + tailwind-merge class combiner used internally for composing Tailwind classes without conflicts.
AddressWithParsed · AddressI18nStrings · AddressSuggestionInputShared types: the flattened address shape, the overridable UI strings, and the SDK suggestion input type.
Components
Phase 2 · previewThe Vue 3 SFC components mirror their react-ui counterparts. Each takes a client created with createLocnativeClient. This is the intended API; the components are not yet exported.
Preview (Phase 2)
<script setup lang="ts">
import { createLocnativeClient } from "@locnative/sdk";
import { AddressAutocomplete } from "@locnative/vue-ui";
import "@locnative/vue-ui/styles.css";
const client = createLocnativeClient({
apiKey: import.meta.env.VITE_LOCNATIVE_KEY,
});
function onSelect(address) {
console.log(address.formattedAddress, address.latitude);
}
</script>
<template>
<AddressAutocomplete :client="client" @select="onSelect" />
</template>AddressAutocompleteAccessible combobox address search with proximity bias and i18n strings.
AddressFormFieldAddressAutocomplete wrapped with a label and error styling.
AddressFieldGroupStructured street, suburb, state, and postcode inputs.
ForwardGeocodeInputResolves a free-text address string to coordinates.
ReverseGeocodeInputResolves a latitude/longitude pair to the nearest address.
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": "20 W 34th St, New York NY 10001, US",
"streetAddress": "20 W 34th St",
"locality": "New York",
"state": "NY",
"postcode": "10001",
"country": "US",
"latitude": 40.7484,
"longitude": -73.9857
}
],
"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": 215078,
"formattedAddress": "221B Baker St, London NW1 6XE, GB",
"streetAddress": "221B Baker St",
"locality": "London",
"state": "",
"postcode": "NW1 6XE",
"country": "GB",
"longitude": -0.1585,
"latitude": 51.5237,
"confidence": 90
},
"distance": 14,
"query": {
"lat": 51.5237,
"lng": -0.1585
}
}/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": 318842,
"country": "IS",
"state": "",
"locality": "Reykjavík",
"postcode": "101",
"streetName": "Laugavegur",
"streetType": "",
"numberFirst": "26",
"longitude": -21.9270,
"latitude": 64.1430,
"distance": 35
}
],
"count": 1,
"query": {
"lat": 64.1466,
"lng": -21.9426,
"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": 287513,
"country": "CA",
"state": "ON",
"locality": "Ottawa",
"postcode": "K1A 0B1",
"streetName": "Elgin",
"streetType": "St",
"streetSuffix": null,
"buildingName": null,
"flatType": null,
"flatNumber": null,
"levelType": null,
"levelNumber": null,
"numberFirst": "150",
"numberLast": null,
"longitude": -75.6989,
"latitude": 45.4201,
"confidence": 88,
"gnafPid": null
}/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": 198452,
"formattedAddress": "1600 Pennsylvania Ave NW, Washington DC 20500, US",
"streetAddress": "1600 Pennsylvania Ave NW",
"locality": "Washington",
"state": "DC",
"postcode": "20500",
"country": "US",
"latitude": 38.8977,
"longitude": -77.0365,
"confidence": 95
}/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": "20 W 34th St New York NY",
"match": {
"id": 104233,
"formattedAddress": "20 W 34th St, New York NY 10001, US",
"latitude": 40.7484,
"longitude": -73.9857,
"confidence": 95
}
},
{
"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/locnative",
"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/locnative",
"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.locnative.com/api/v1/geocode/batch", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": "wh_live_your_api_key",
},
body: JSON.stringify({ addresses: ["20 W 34th St New York NY", "221B Baker St London"] }),
}).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.locnative.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.locnative.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
Locnative 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.