Quick answer
The regex for a US ZIP code is ^\d{5}(?:-\d{4})?$: exactly five digits, optionally followed by a hyphen and the four-digit ZIP+4 extension. It matches 90210, 02134 and 90210-1234, and rejects 4- or 6-digit codes and extensions without the hyphen. Store ZIP codes as text, never as numbers, so codes in New England keep their leading zero.
- Tested examples
- Token-by-token breakdown
- Code in 5 languages
- Live tester
How to use the US ZIP code regex
- 1Copy the pattern with Copy pattern in the tester, or take a ready-made snippet for your language from the code section below.
- 2Check the sample lines in Test string: every line the US ZIP code regex accepts is highlighted, rejected lines stay plain.
- 3Paste your own values one per line to see which pass, and read Explanation for what each part of the pattern does.
- 4Open the Code tab for JavaScript, Python, Go, C#, PHP or Dart with the pattern translated and escaped.
- 5Validate again on the server: client-side checks give users quick feedback, not security.
How does the US ZIP code regex work?
Read it left to right. Each piece below matches one part of the input:
^\d{5}Start of the string and exactly five digits.(?:-\d{4})?Optionally a hyphen and the four-digit ZIP+4 add-on.$End of the string.
Which strings does the US ZIP code regex match?
The whole input must match, from the first character to the last. Every row below is checked against the pattern by our automated test suite before each release.
| Input | Matches? | Why |
|---|---|---|
90210 | Yes | Beverly Hills. |
10001 | Yes | New York. |
02134 | Yes | Boston: the leading zero is part of the code. |
90210-1234 | Yes | ZIP+4. |
9021 | No | Four digits (often a ZIP that lost its leading zero). |
902101 | No | Six digits. |
90210-123 | No | The extension needs four digits. |
90210 1234 | No | A space instead of the hyphen. |
902101234 | No | Nine digits without the hyphen (see the variant below). |
ABCDE | No | Letters. |
Why do ZIP codes lose their leading zero?
Because something treated them as numbers. Spreadsheets, CSV imports and <input type="number"> all turn 02134 into 2134, and every ZIP code in Massachusetts, Connecticut, New Jersey, Puerto Rico and much of New England starts with 0. Keep ZIP codes as strings end to end: type="text" with inputmode="numeric" and autocomplete="postal-code" gives a numeric keypad without the conversion.
If you receive data that has already lost zeros, pad it back with zip.padStart(5, "0") before validating, but only when you know the source is US data.
Does a valid ZIP code format mean the ZIP exists?
No. Not every five-digit number is assigned (00000 passes this pattern), ZIP codes are delivery routes rather than areas, and they change. To check that a ZIP exists or matches a city and state, use the USPS address APIs or an address-verification service at checkout.
To accept a nine-digit ZIP with or without the hyphen, use ^\d{5}(?:-?\d{4})?$, and normalize to the hyphenated form before storing. For other countries, see the UK postcode and India PIN code patterns.
How do I use the US ZIP code regex in JavaScript, Python, Go, C# and PHP?
The pattern is translated for each engine rather than pasted as is: named groups, anchors, escapes, shorthand classes and flags are adjusted so each snippet behaves as closely to the JavaScript version as its engine allows. Differences that remain (Go's RE2 has no lookarounds or backreferences, for example) are called out under each one. The Code tab in the tester above also exports Dart, and updates as you edit the pattern.
JavaScript and TypeScript
const regex = /^\d{5}(?:-\d{4})?$/;
function isValid(value) {
return regex.test(value);
}
console.log(isValid("90210"));Python
import re
pattern = re.compile(r"^[0-9]{5}(?:-[0-9]{4})?\Z")
def is_valid(value: str) -> bool:
return pattern.search(value) is not None
print(is_valid("90210"))- \d and \w are written as [0-9] and [A-Za-z0-9_] because in Python they also match digits and letters from other scripts.
- JavaScript's $ only matches at the very end of the string. Python's $ also matches before a trailing newline, so the pattern uses \Z instead.
Go
package main
import (
"fmt"
"regexp"
)
func main() {
re := regexp.MustCompile(`^\d{5}(?:-\d{4})?$`)
fmt.Println(re.MatchString("90210"))
}C#
using System;
using System.Text.RegularExpressions;
// The timeout stops a pathological input from hanging the thread (ReDoS).
var regex = new Regex(@"^[0-9]{5}(?:-[0-9]{4})?\z", RegexOptions.None, TimeSpan.FromSeconds(1));
Console.WriteLine(regex.IsMatch("90210"));- \d and \w are written as [0-9] and [A-Za-z0-9_] because in .NET they also match digits and letters from other scripts.
- JavaScript's $ only matches at the very end of the string. .NET's $ also matches before a trailing newline, so the pattern uses \z instead.
PHP
<?php
$pattern = '/^\d{5}(?:-\d{4})?$/D';
function isValid(string $value, string $pattern): bool
{
return preg_match($pattern, $value) === 1;
}
var_dump(isValid('90210', $pattern));- The D modifier makes $ match only at the very end, like JavaScript (PCRE's $ also matches before a trailing newline).
Which related regex patterns are useful?
- Regex for UK Postcodes: All six UK postcode formats, with or without the space, including SW1A 1AA.
- Regex for Indian PIN Codes: Six-digit Indian postal codes that never start with 0, with an optional space.
- Regex for Phone Numbers (E.164 Format): International numbers in E.164: a plus, a country code and up to 15 digits in total.
- Regex for Numbers Only: Digits only, plus variants for signs, decimals and thousands separators.
Browse all 25 patterns in the regex pattern library, or build your own in the regex tester.
Frequently asked questions
Last updated by the Playxoft engineering team.