Address validation and geocoding in Go
About 8 minutes. Updated 18 September 2026.
Install the Go client and resolve a typed address to a G-NAF id and a coordinate on the server, with near misses to offer back when nothing matches and typed errors for quota and rate limits.
The Go client is for the server: validating an address somebody typed, filling a coordinate on an order, or cleaning a table of addresses you already hold. It has no dependencies beyond the standard library.
01Install it
go get github.com/locio-au/locio-go@latestGo 1.22 or newer. The import path is github.com/locio-au/locio-go and the package name is locio.
02Resolve what somebody typed
/v1/addresses/resolve takes a line of text and answers whether it is a real address, which one it is, and where it is, in one call.
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/locio-au/locio-go"
)
func main() {
// A secret key. This runs on your server, never in a browser.
//
// WithCountry says which register to search. Leave it off and the
// service searches AU, which is the only one with data today; your
// server's own location is never consulted.
c := locio.New(os.Getenv("LOCIO_API_KEY"), locio.WithCountry("AU"))
res, err := c.Resolve(context.Background(), "1 george st sydenham nsw 2044")
if err != nil {
log.Fatal(err)
}
if !res.Matched {
fmt.Println("no match")
return
}
fmt.Println(res.Address.Formatted)
fmt.Println(res.Address.ID)
fmt.Println(res.Address.Lat, res.Address.Lng)
}Store 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. AddressDetailPID carries the same value on an Australian record, for code that wants to say plainly that it is holding a G-NAF pid.
03Offer near misses when nothing matches
A typo should not become a dead end. /v1/addresses/similar finds the nearest real addresses so you can show them as options.
// Nothing matched. Offer the nearest real addresses back rather
// than telling somebody their address does not exist.
near, err := c.Similar(ctx, typed, 5)
if err != nil {
return err
}
for _, a := range near {
fmt.Println(a.Formatted, a.ID)
}04Handle the errors you will actually see
Failures come back as a typed *locio.Error carrying the status, so quota, rate limiting and a revoked key are all things you can branch on rather than strings you have to match.
res, err := c.Resolve(ctx, typed)
var apiErr *locio.Error
if locio.AsError(err, &apiErr) {
switch apiErr.Status {
case 402:
// Out of quota for the month.
case 429:
// Rate limited. Back off and retry.
case 401:
// The key is wrong, or revoked.
}
}
if locio.IsNotFound(err) {
// An id that is not in the current release, or one that belongs to a
// country this key is not searching.
}What it costs
One unit per request. A resolve is one call, and the near misses only cost another when the first one did not match. Refused requests are free.
Related
- Address autocomplete in plain JavaScript
- 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
- 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.