Address autocomplete in plain JavaScript
About 10 minutes. Updated 11 September 2026.
Add Australian address autocomplete to any HTML form with one script and no framework: a debounced call to the address API as someone types, a list to pick from, and the G-NAF id saved with the form.
This is the smallest useful integration: an input, a list under it, and a hidden field that carries the address id into whatever handles the form. It works in any page, with any backend, and there is nothing to install.
01The markup
A search input, an empty list for suggestions, and a hidden input for the id. The hidden field is the point of the exercise: store the id against your record and you can read the address back a year later without knowing how it was typed.
<label for="address">Delivery address</label>
<input id="address" type="search" autocomplete="off" placeholder="145 sydney road">
<ul id="suggestions" hidden></ul>
<input type="hidden" name="address_id" id="address_id">
<script type="module" src="/address-search.js"></script>02The script
Wait for 200ms of quiet, then call /v1/addresses with what has been typed so far. Every result is already a resolved address, so picking one needs no second call. An in flight request is abandoned when the next one starts, so a slow answer for "14" cannot land after the fast one for "145 sy".
// address-search.js
const KEY = "lc_live_..."; // a public key, with this site under its allowed sites
const API = "https://api.locio.com.au";
const input = document.querySelector("#address");
const list = document.querySelector("#suggestions");
const hidden = document.querySelector("#address_id");
let timer;
let inflight;
input.addEventListener("input", () => {
clearTimeout(timer);
hidden.value = "";
const q = input.value.trim();
if (q.length < 3) {
list.hidden = true;
return;
}
// 200ms of quiet before asking. Every request is a unit, and one per
// keystroke buys nothing a slightly later one would not.
timer = setTimeout(() => search(q), 200);
});
async function search(q) {
inflight?.abort();
inflight = new AbortController();
const url = new URL(`${API}/v1/addresses`);
url.searchParams.set("q", q);
url.searchParams.set("limit", "8");
try {
const res = await fetch(url, {
headers: { Authorization: `Bearer ${KEY}` },
signal: inflight.signal,
});
if (!res.ok) return; // 402 is quota, 429 is rate; both are worth logging
const { data } = await res.json();
render(data);
} catch (err) {
if (err.name !== "AbortError") console.error(err);
}
}
function render(addresses) {
list.replaceChildren(
...addresses.map((address) => {
const item = document.createElement("li");
const button = document.createElement("button");
button.type = "button";
button.textContent = address.formatted;
button.addEventListener("click", () => pick(address));
item.append(button);
return item;
}),
);
list.hidden = addresses.length === 0;
}
function pick(address) {
input.value = address.formatted;
hidden.value = address.id; // store this, not the text
list.hidden = true;
input.dispatchEvent(new CustomEvent("address:picked", { detail: address, bubbles: true }));
}Store address.id. It is the address's id in its country's register, which in Australia is the G-NAF Address Detail PID, sent under address_detail_pid as well. It is stable across releases for an address that has not changed, where the formatted line is not, so it is the thing to key your own records on rather than the text.
03Use what was picked
The script fires an event with the whole address on it. Coordinates, the ABS mesh block and every G-NAF component are there without another request.
document.addEventListener("address:picked", (event) => {
const { id, lat, lng, components } = event.detail;
console.log(id, lat, lng, components.postcode);
});What it costs
One unit per request, and the debounce keeps that to a handful per address. The free tier covers 10,000 a month, which is a few thousand completed addresses. Refused requests are free.
Related
- Address autocomplete in React with the Locio package
- Address autocomplete in React
- Address autocomplete in Vue with the Locio package
- Address autocomplete in Vue
- Stand up a React site with Australian address search
- Normalise shipping addresses at checkout
- Put a searched address on a map with MapLibre
- Validate and geocode Australian addresses in PHP
- Australian address autocomplete for WooCommerce
- Address validation and geocoding in Go
- Use Locio from Claude Code, Claude Desktop and Cursor
- Give an agent address tools with the Anthropic API
- Clean a spreadsheet of addresses with an agent
- The API reference, with every call in curl, Node, Python, Ruby, Go and PHP.