Validating Postal Code Formats Across Countries
9 min read · updated August 11, 2026
A regular expression can tell you a postal code has the right shape for a country. It cannot tell you the code exists, and for at least sixty countries the right answer is that there is no code to validate. Treating a required postal code field as universal is the most common way a checkout form locks out a legitimate customer.
What a pattern can and cannot tell you
Postal code validation comes in three strengths and they are often confused. A format check tests the string against the country’s pattern — five digits for Germany, a letter-digit alternation for Canada. An existence check tests the code against a list of codes actually in use, which requires a dataset that changes as new codes are allocated. A consistency check tests the code against the rest of the address, catching a valid code in the wrong city.
This page is about the first, because it is the one you can do offline with no data feed and it catches most typing errors. Be clear about what it does not catch: 99999 is a well-formed US ZIP and is not a real one. If correctness matters — for shipping, for tax jurisdiction, for address verification — a format check is a first filter and not the answer.
Patterns for a labelled set of countries
The authoritative source for each country’s addressing system, including whether it has postal codes and what shape they take, is the Universal Postal Union, which publishes member countries’ postal addressing systems from the declarations of the national operators themselves. Google’s libaddressinput publishes a machine-readable dataset of per-country address metadata including postal code patterns, which is what most checkout forms are built on.
Country Pattern (regex body) Example
United States \d{5}(-\d{4})? 94043, 94043-1351
Canada [A-Z]\d[A-Z] ?\d[A-Z]\d K1A 0B1
United Kingdom see note below SW1A 1AA, M1 1AE
Germany \d{5} 10115, 01067
France \d{5} 75008
Netherlands \d{4} ?[A-Z]{2} 1012 AB
Poland \d{2}-\d{3} 00-001
Japan \d{3}-\d{4} 100-8994
India \d{6} 110001
Brazil \d{5}-?\d{3} 01310-100
Australia \d{4} 2000
Sweden \d{3} ?\d{2} 114 55
Ireland [A-Z]\d[\dW] ?[A-Z\d]{4} D02 AF30The United Kingdom does not reduce to one short pattern. Royal Mail postcodes take six forms — A9 9AA, A99 9AA, A9A 9AA, AA9 9AA, AA99 9AA and AA9A 9AA — with letter restrictions that differ by position, so a correct validator is a union of alternatives rather than a single class expression. Royal Mail publishes the specification in its Programmers Guide, and any regex short enough to read is either rejecting valid postcodes or accepting invalid ones.
Countries with no postal code
Roughly sixty UPU member countries have no postal code system, and this is the part of the subject most often got wrong, because a form with a required postal code field is unusable in those places and the person affected has no way to explain the problem to you.
Among the better-known cases: Hong Kong and Macau have no postal codes; the United Arab Emirates has none, so 00000 is what people type; Qatar has none; and many countries across sub-Saharan Africa and the Caribbean — including Angola, Botswana, Burundi, Ghana for most purposes, Fiji, and several Caribbean states — either have no system or have one that is not used in ordinary addressing. Ireland is the instructive case in the other direction: it had no postal code outside Dublin district numbers until Eircode launched in 2015, so datasets and code written before then contain the assumption that Ireland has none, and it is still around.
The libaddressinput dataset encodes this properly: a country with no postal code has no postal code pattern and does not list the field as used, which is the signal your form should read rather than a hand-maintained exception list.
Four traps in the data
- Leading zeros. German
01067, US02134, French01000. Any pipeline that stores a postal code as an integer, or that lets a spreadsheet touch the column, destroys these silently. Postal codes are strings in every language, always. - Letters that are excluded. Canadian postal codes never contain D, F, I, O, Q or U, and W and Z do not appear in the first position, because they are confusable with digits or reserved. A permissive
[A-Z]class accepts codes that cannot exist. The Netherlands similarly excludes certain two-letter combinations. - The space is optional and load-bearing. Canadian and UK codes are conventionally written with a space, and are frequently entered without one. Accept both on input, normalise to one canonical form for storage, and render with the space. Comparing a stored unspaced code with a user’s spaced code is a whole class of false negatives.
- Codes that are not postal codes. Some countries have a code system used only for certain destinations — post office boxes, large-volume recipients, or the military. US military addresses use
APO,FPOandDPOwith a state-position code of AA, AE or AP, and a validator that only knows the fifty state abbreviations rejects them.
A validation strategy that does not lock people out
- Make the country field the first thing chosen, and drive everything else from it. A postal code field cannot be validated before you know the country, and cannot be shown at all for a country that has none.
- Hide the field entirely where the country has no postal code system, rather than showing it as optional. An optional field with no meaning still invites
00000, and a fake value in a real field is worse than a missing one. - Validate format as a warning, not a hard block, unless you have a downstream system that will genuinely fail. Tell the user the code does not look like a code for that country and let them proceed.
- Normalise on storage — uppercase, canonical spacing — and keep the raw input alongside it, so that a normalisation bug is recoverable.
- Where accuracy matters commercially, use an address verification service for the existence and consistency checks. A regex was never going to do that job — the same conclusion reached in phone number formatting by country, where the equivalent library exists for the same reason.
If a model is extracting addresses from documents rather than a user typing them, the same rules apply with one addition: a model will happily invent a plausible postal code to fill a field it thinks should be populated. Ask for a null rather than a guess, explicitly, and treat a code that matches the pattern but was not present in the source as the failure it is. The field-by-field extraction approach in formatting an address for a given country is what makes that detectable.