Tutorial

How to validate postal codes

A regex catches typos in postal codes; a lookup catches codes that do not exist. Here is how to do both, with copy-paste patterns for 20 countries.

Updated October 1, 2026 ยท 2 min read

Regex patterns by country

CountryPatternExample
United States^\d{5}(?:[-\s]\d{4})?$10001, 10001-2062
United Kingdom^[A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}$SW1A 1AA
Canada^[ABCEGHJ-NPRSTVXY]\d[ABCEGHJ-NPRSTV-Z] ?\d[ABCEGHJ-NPRSTV-Z]\d$K1A 0B1
Germany^\d{5}$10115
France^\d{5}$75008
Netherlands^\d{4} ?[A-Z]{2}$1012 AB
India^[1-9]\d{5}$110001
Brazil^\d{5}-?\d{3}$01310-100
Japan^\d{3}-?\d{4}$100-0001
Australia^\d{4}$2000
Spain^\d{5}$28013
Italy^\d{5}$00184
Poland^\d{2}-\d{3}$00-950
Portugal^\d{4}-\d{3}$1000-001
Sweden^\d{3} ?\d{2}$111 22
Switzerland^\d{4}$8001
Austria^\d{4}$1010
Ireland (Eircode)^(?:[AC-FHKNPRTV-Y]\d{2}|D6W) ?[0-9AC-FHKNPRTV-Y]{4}$D02 X285
Mexico^\d{5}$06600
South Korea^\d{5}$03187

Each country page lists the official pattern from Google’s address metadata.

JavaScript

const POSTAL = {
  US: /^\d{5}(?:[-\s]\d{4})?$/,
  GB: /^[A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}$/i,
  CA: /^[ABCEGHJ-NPRSTVXY]\d[ABCEGHJ-NPRSTV-Z] ?\d[ABCEGHJ-NPRSTV-Z]\d$/i,
  DE: /^\d{5}$/,
};

function isValidPostalFormat(code, country) {
  const re = POSTAL[country];
  return re ? re.test(code.trim()) : true; // unknown country: don't block
}

Python

import re

POSTAL = {
    "US": re.compile(r"^\d{5}(?:[-\s]\d{4})?$"),
    "GB": re.compile(r"^[A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}$", re.I),
    "NL": re.compile(r"^\d{4} ?[A-Z]{2}$", re.I),
}

def is_valid_postal_format(code: str, country: str) -> bool:
    pattern = POSTAL.get(country)
    return bool(pattern.match(code.strip())) if pattern else True

PHP

$postal = [
    'US' => '/^\d{5}(?:[-\s]\d{4})?$/',
    'CA' => '/^[ABCEGHJ-NPRSTVXY]\d[ABCEGHJ-NPRSTV-Z] ?\d[ABCEGHJ-NPRSTV-Z]\d$/i',
    'BR' => '/^\d{5}-?\d{3}$/',
];

function isValidPostalFormat(string $code, string $country, array $postal): bool
{
    return isset($postal[$country]) ? (bool) preg_match($postal[$country], trim($code)) : true;
}

When a regex is not enough

99999 matches the US pattern but is not a ZIP code, and SW1A 1ZZ matches the UK pattern but does not exist. To know that a code is real, and to fill in city and state automatically, look it up:

curl "https://api.zipcodestack.com/v1/validate?code=99999&country=us" -H "apikey: YOUR-API-KEY"
# { "result": { "valid": false, "format_valid": true, ... } }

Try it without code in the free postal code validator.

Frequently asked questions

Is there one regex for all postal codes?

No. Formats range from 3 digits (Iceland) to letter-digit mixes (UK, Canada) and 8 digits (Brazil), and about 60 countries have no postal codes at all. Validate per country.

What is the regex for a US ZIP code?

^\d{5}(?:[-\s]\d{4})?$ accepts 5-digit ZIP codes and ZIP+4 (12345-6789).

What is the regex for a UK postcode?

A practical pattern is ^[A-Z]{1,2}\d[A-Z\d]? ?\d[A-Z]{2}$ (case-insensitive). It covers all current formats like SW1A 1AA, M1 1AE and B33 8TH.

Postal code API

Get this data in your app

zipcodestack looks up, validates and searches postal codes in 149 countries: city, state, coordinates, distances and radius search, as JSON.

/v1/search documentation International postal code API All documentation

300 free requests every month. No credit card required.

GET https://api.zipcodestack.com/v1/search?codes=10001&country=us

{
  "results": {
    "10001": [{
      "postal_code": "10001",
      "country_code": "US",
      "latitude": 40.7484,
      "longitude": -73.9967,
      "city": "New York",
      "state": "New York",
      "state_code": "NY",
      "province": "New York"
    }]
  }
}

Keep reading

Start using our ZIP Code API for free today!

Get 300 requests / month for free