← All posts Developers

Australian postcode and state validation: the regex, and why it is not enough

5 Sept 2026 · 5 min read

Australian postcode validation is checking that a postcode is four digits, that the state is one of eight codes, and that the two plausibly belong together; anything beyond that, such as whether the address exists, needs the G-NAF dataset rather than a pattern. The regex is one line; the reasons it is not enough fill the rest of this post.

The regex

// postcode: exactly four digits, leading zero allowed (NT and ACT)
const POSTCODE = /^(0[289]\d{2}|[1-9]\d{3})$/;

// state: one of the eight codes, upper case
const STATE = /^(NSW|VIC|QLD|SA|WA|TAS|NT|ACT)$/;

Two rules follow from the postcode pattern. Store the postcode as a string, because 0800 (Darwin) becomes 800 in an integer column and every label printed from it is wrong. And accept a leading zero on input even if your form pads it later; people in the Territory type it both ways.

Postcode ranges by state

  • NSW: 1000 to 2599, 2619 to 2899, 2921 to 2999
  • ACT: 0200 to 0299, 2600 to 2618, 2900 to 2920
  • VIC: 3000 to 3999, 8000 to 8999
  • QLD: 4000 to 4999, 9000 to 9999
  • SA: 5000 to 5999
  • WA: 6000 to 6797, 6800 to 6999
  • TAS: 7000 to 7999
  • NT: 0800 to 0999

The 1xxx, 8xxx and 9xxx ranges are mostly PO Boxes and large-volume receivers, which is a hint about the next section. Some postcodes cross borders: 2611 is ACT and also covers Brindabella and Uriarra in NSW, a handful of NSW towns near the Murray carry Victorian-range postcodes such as 3644 and 3500, some far-north NSW localities use Queensland-range 43xx, and 0872 spans NT, SA and WA. So a state-versus-postcode check should say “unusual”, not “wrong”.

What the regex cannot tell you

  • That the postcode matches the suburb. 2000 is Sydney; 2000 with “Parramatta” passes every pattern and is wrong.
  • That the street exists in that suburb, or that number 12 exists on that street.
  • That the address is a physical one. A PO Box passes the regex and is not in G-NAF at all.
  • That the address is current. G-NAF changes every quarter as estates are built and streets renamed.

Each of those needs the dataset, not a pattern. Matching free text against G-NAF is what the verify endpoint does: it returns the canonical record, a verdict (verified, corrected, ambiguous or unverified) and a status for every field, so a form can re-prompt for the one part that is wrong. The regex is still worth keeping, as the cheap first check that stops obvious typos before a request is made.

A sensible order

  • Regex on the client, so an obviously malformed postcode is caught with no round trip.
  • Autocomplete as the person types, so most addresses arrive already correct and structured.
  • Verify on the server for anything typed by hand or imported, and branch on the verdict.
  • Store the parts separately, as text, with the G-NAF release you matched against; the storage guide has the schema.

If you want to see how a messy address splits before you write any of this, the address format checker does it in the browser.

Get an API key Read the docs