UseToolSuite UseToolSuite

Phone Number Regex Patterns by Country

Tested phone number regex patterns by country: the ITU-T E.164 standard, validation for North America, the EU, and Asia, and when to use libphonenumber instead.

Necmeddin Cunedioglu Necmeddin Cunedioglu 7 min read
Part of the Regular Expressions: The Complete Guide series

Practice what you learn

Regex Tester

Try it free →

Phone Number Regex Patterns by Country

Phone number formats are a famously messy dataset. The US uses a fixed 10-digit (XXX) XXX-XXXX structure, the UK has variable-length area codes, Germany allows 10–13 digit subscriber numbers, and countries like India and Brazil have shifted prefixes — adding digits to millions of active numbers.

A single regex can’t reliably validate every global phone number without false positives or negatives. But with country-specific patterns and a foundation built on telecom standards, you can handle most real-world inputs well.

This guide gives tested, production-ready patterns for the most common formats, explains the reasoning behind each, covers the pitfalls that cause production failures, and shows how to work with SMS APIs like Twilio and AWS SNS.

Don’t deploy untested regex. Open our local Regex Tester and paste any pattern below to validate it against your data in real time, in the browser.

1. The Global Standard: E.164

The E.164 standard, defined by the ITU-T, is the safest format for storing and transmitting phone numbers. Every valid, routable number on the planet can be represented in E.164.

^\+[1-9]\d{6,14}$

The rules: a literal +, then the country code (which never starts with 0), then the subscriber number. Total length is 7–15 digits. No spaces, dashes, or parentheses.

Valid: +14155551234 (USA), +442071234567 (UK), +905551234567 (Turkey), +81312345678 (Japan)

Why E.164 matters

E.164 solves the ambiguity problem. The string 07911123456 could be a valid UK mobile number (if dialed within the UK) or invalid (if dialed internationally). Storing the full E.164 format removes that context dependence.

Major telephony APIs — Twilio, SendGrid SMS, AWS SNS, Google Cloud Telephony — require E.164 input.

PropertyE.164Local Format
Globally unique✅ Yes❌ No — depends on dialing context
Machine-comparable✅ Yes — string equality works❌ No — (415) 555-1234415.555.1234
API compatibility✅ Twilio, AWS, Google accept it natively❌ Needs parsing and normalization first
Human readable❌ Unintuitive on forms✅ Familiar, localized

Recommendation: accept flexible input on the frontend, normalize it to E.164 in the backend, store the E.164 string, and convert back to a localized format only for display.

2. United States & Canada (+1)

North America uses the North American Numbering Plan (NANP): a 10-digit format — a 3-digit area code plus a 7-digit subscriber number.

^(\+1)?[-.\s]?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}$

Matches: (415) 555-1234, 415-555-1234, 415.555.1234, +1 415 555 1234, 4155551234

NANP edge cases

The pattern above is permissive, but the NANP has strict rules a real validator should respect:

  • Area codes never start with 0 or 1 (012, 115 are invalid).
  • Exchange codes (the middle 3 digits) also never start with 0 or 1.
  • Numbers with 555 in the 0100–0199 range are reserved for fictional use and never connect.

A stricter pattern enforcing these with character classes:

^(\+1)?[-.\s]?\(?[2-9]\d{2}\)?[-.\s]?[2-9]\d{2}[-.\s]?\d{4}$

Node.js validation and normalization

function validateAndNormalizeUSPhone(input) {
  // 1. Strip formatting characters
  const cleaned = input.replace(/[\s\-\(\)\.]/g, '');
  
  // 2. Validate against the NANP rules
  const pattern = /^(\+?1)?[2-9]\d{2}[2-9]\d{6}$/;
  if (!pattern.test(cleaned)) {
    throw new Error('400 Bad Request: Invalid North American phone number.');
  }
  
  // 3. Extract the digits
  const digits = cleaned.replace(/\D/g, '');
  
  // 4. Build the E.164 string for storage
  const e164 = digits.length === 10 ? `+1${digits}` : `+${digits}`;
  
  // 5. Build a display string for the UI
  const display = `(${digits.slice(-10, -7)}) ${digits.slice(-7, -4)}-${digits.slice(-4)}`;
  
  return { e164, display };
}

3. United Kingdom (+44)

UK numbers are tricky: variable-length area codes (2–5 digits) and total lengths of 10 or 11 digits (excluding the leading 0 or +44).

^(\+44|0)\d{10,11}$

Matches: +447911123456, 07911123456, 02071234567

UK number types

TypePrefixExampleDigits after 0/+44
Mobile07xxx07911 12345610
London020020 7123 456710
Other geo01xxx / 011x0161 234 567810
Non-geo03xx0345 123 456710
Premium09xx0906 123 456710

A precise pattern for UK mobile numbers (important for 2FA SMS):

^(\+44|0)7\d{9}$

4. Germany (+49)

German numbers vary in length. Mobile numbers are typically 11 digits, but landlines range from 10 to 13 depending on the area code.

^(\+49|0)\d{10,13}$

Matches: +4915112345678, 015112345678, +4930123456

German mobile prefixes

To avoid sending SMS to landlines, identify mobile networks. All mobile numbers begin with 015x, 016x, or 017x:

^(\+49|0)(1[567]\d)\d{7,8}$

5. France (+33)

France uses a consistent 10-digit structure (including the leading 0). Mobile numbers start with 06 or 07.

^(\+33|0)[1-9]\d{8}$

Matches: +33612345678, 0612345678, 0145678901

6. Turkey (+90)

Turkish numbers use a 10-digit subscriber number, with all mobile numbers starting with 5.

^(\+90|0)?\s?\(?\d{3}\)?\s?\d{3}\s?\d{2}\s?\d{2}$

Matches: +90 555 123 45 67, 0555 123 45 67, 05551234567

Turkish mobile-only pattern

^(\+90|0)?5\d{9}$

7. India (+91)

Indian mobile numbers are 10 digits, starting with 6, 7, 8, or 9.

^(\+91|0)?[6-9]\d{9}$

Matches: +919876543210, 09876543210, 9876543210

8. Brazil (+55)

Brazilian numbers include a mandatory 2-digit area code (DDD). After a recent change, mobile numbers have 9 digits (all starting with 9), while landlines keep 8.

^(\+55|0)?\d{2}9?\d{8}$

Matches: +5511987654321, 011987654321, 1187654321

9. Japan (+81)

Japanese numbers use variable-length area codes (1–5 digits). Mobile numbers start with 070, 080, or 090.

^(\+81|0)\d{9,10}$

Mobile-only:

^(\+81|0)[789]0\d{8}$

10. A Multi-Country Validation Function

A routing function that checks incoming numbers against several national patterns:

const PHONE_DICTIONARY = {
  US: { pattern: /^(\+?1)?[2-9]\d{2}[2-9]\d{6}$/, code: '+1', digits: 10 },
  GB: { pattern: /^(\+?44|0)\d{10,11}$/, code: '+44', digits: 10 },
  DE: { pattern: /^(\+?49|0)\d{10,13}$/, code: '+49', digits: 10 },
  FR: { pattern: /^(\+?33|0)[1-9]\d{8}$/, code: '+33', digits: 9 },
  TR: { pattern: /^(\+?90|0)?5\d{9}$/, code: '+90', digits: 10 },
  IN: { pattern: /^(\+?91|0)?[6-9]\d{9}$/, code: '+91', digits: 10 },
  BR: { pattern: /^(\+?55|0)?\d{2}9?\d{8}$/, code: '+55', digits: 11 },
  JP: { pattern: /^(\+?81|0)\d{9,10}$/, code: '+81', digits: 10 },
};

function validateGlobalPhone(input, targetCountryCode = null) {
  // Strip non-numeric characters
  const cleaned = input.replace(/[\s\-\(\)\.]/g, '');
  
  // If a country is specified, test only that pattern
  if (targetCountryCode && PHONE_DICTIONARY[targetCountryCode]) {
    const { pattern } = PHONE_DICTIONARY[targetCountryCode];
    return pattern.test(cleaned);
  }
  
  // Otherwise check the baseline E.164 standard
  if (/^\+[1-9]\d{6,14}$/.test(cleaned)) return true;
  
  // Fallback: test against the whole dictionary
  return Object.values(PHONE_DICTIONARY).some(({ pattern }) => pattern.test(cleaned));
}

The Python equivalent

import re

PHONE_DICTIONARY = {
    'US': r'^(\+?1)?[2-9]\d{2}[2-9]\d{6}$',
    'GB': r'^(\+?44|0)\d{10,11}$',
    'DE': r'^(\+?49|0)\d{10,13}$',
    'FR': r'^(\+?33|0)[1-9]\d{8}$',
    'TR': r'^(\+?90|0)?5\d{9}$',
    'IN': r'^(\+?91|0)?[6-9]\d{9}$',
}

def validate_global_phone(number_string: str, country_code: str = None) -> bool:
    cleaned = re.sub(r'[\s\-\(\)\.]', '', number_string)
    
    if country_code and country_code in PHONE_DICTIONARY:
        return bool(re.match(PHONE_DICTIONARY[country_code], cleaned))
        
    # Baseline E.164 check
    if re.match(r'^\+[1-9]\d{6,14}$', cleaned):
        return True
        
    # Dictionary fallback
    return any(re.match(pattern, cleaned) for pattern in PHONE_DICTIONARY.values())

11. Five Common Production Mistakes

Mistake 1: forcing exact formatting

❌ Rejecting a valid 4155551234 because the user didn’t type dashes. ✅ Strip dashes, spaces, and periods with replace() before validating.

Mistake 2: a single global pattern

❌ Enforcing \d{3}-\d{3}-\d{4} on an international form. ✅ Accept E.164, or detect the country via locale, then apply the matching national pattern.

Mistake 3: trusting regex for reachability

❌ Assuming a number that passes the regex actually exists and can receive SMS. ✅ Regex validates format. For reachability, call a carrier lookup API (Twilio Lookup, NumVerify).

Mistake 4: inconsistent storage

❌ Storing (415) 555-1234 and +14155551234 as two different strings. ✅ Normalize to E.164 before the INSERT, so equality matching works for deduplication.

Mistake 5: over-strict length limits

❌ Forcing a rigid 10-digit global limit. ✅ Germany allows up to 13 digits; the UK is 10–11. Your baseline E.164 regex should allow the full 15-digit maximum.

12. Regex vs Dedicated Libraries

ScenarioUse RegexUse libphonenumber
Client-side forms✅ Fast, non-blocking❌ Heavy (250KB+ bundle)
Server-side API🟡 OK for basics✅ Preferred
Carrier detection❌ Not possible✅ Built-in lookups
Locale-aware UI formatting❌ Error-prone✅ Accurate

Google’s libphonenumber is the standard for server-side validation. It’s available in Java, JavaScript (google-libphonenumber), Python (phonenumbers), and Go.

// Using the lighter libphonenumber-js port (~140KB)
import { parsePhoneNumber, isValidPhoneNumber } from 'libphonenumber-js';

const phone = parsePhoneNumber('+14155551234');
console.log(phone.country);          // 'US'
console.log(phone.formatNational()); // '(415) 555-1234'
console.log(phone.isValid());        // true

Further Reading


Don’t deploy patterns that block paying customers. Paste these into our local Regex Tester to verify them against your data. To measure the size of large payload files, use our Text Counter.

Necmeddin Cunedioglu
Necmeddin Cunedioglu Author
7 min read
-- views

Software developer and the creator of UseToolSuite. I write about the tools and techniques I use daily as a developer — practical guides based on real experience, not theory.