BytePane

Regex Patterns for API Validation: Email, URL, Phone (Copy-Paste Ready)

Regex14 min read

Why Regex Validation Matters for APIs

Every API that accepts user input needs validation. Unvalidated data leads to database corruption, injection attacks, broken downstream systems, and poor user experience. Regular expressions are the most common tool for format validation -- checking that an email looks like an email, a URL looks like a URL, and a phone number contains the right digits.

This guide provides production-ready regex patterns you can copy directly into your codebase. Each pattern includes a practical version (good enough for 99% of cases) and a strict version (for compliance-heavy applications). All patterns have been tested against real-world edge cases, including internationalized inputs and adversarial strings.

Test any pattern from this guide instantly with our Regex Tester tool. Paste the pattern, add test strings, and see matches highlighted in real time -- all in your browser, no server required.

Email Validation Regex

Email validation is the most common and most debated regex use case. The full RFC 5322 specification is absurdly complex (it allows quoted strings, comments, and folding whitespace), so in practice, developers choose between a simple format check and a more thorough pattern. The truth is that the only way to truly validate an email is to send a verification message to it.

Simple Email Regex (Recommended for Most Apps)

This pattern covers 99.9% of real email addresses. It ensures there is a local part, an @ symbol, a domain, and a TLD. It deliberately avoids being overly strict so it does not reject valid but unusual addresses.

// Simple email regex -- covers 99.9% of real addresses
const EMAIL_SIMPLE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

// Test cases:
EMAIL_SIMPLE.test('[email protected]');        // true
EMAIL_SIMPLE.test('[email protected]');    // true
EMAIL_SIMPLE.test('[email protected]');     // true
EMAIL_SIMPLE.test('[email protected]');      // true
EMAIL_SIMPLE.test('user@example');            // false (no TLD)
EMAIL_SIMPLE.test('@example.com');            // false (no local)
EMAIL_SIMPLE.test('user @example.com');       // false (space)
EMAIL_SIMPLE.test('');                        // false (empty)

Stricter Email Regex (HTML5 Standard)

This pattern is based on the HTML5 specification for <input type="email"> validation. It is more restrictive about allowed characters in the local and domain parts while still accepting all common email formats.

// HTML5 spec email regex
const EMAIL_HTML5 = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;

// Python equivalent:
# import re
# EMAIL_HTML5 = re.compile(
#     r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@"
#     r"[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?"
#     r"(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$"
# )

Email Validation in Practice

// Production email validation function
function validateEmail(email) {
  // Step 1: Basic format check with regex
  const pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  if (!pattern.test(email)) {
    return { valid: false, error: 'Invalid email format' };
  }

  // Step 2: Length check (RFC 5321 limits)
  const [local, domain] = email.split('@');
  if (local.length > 64) {
    return { valid: false, error: 'Local part exceeds 64 characters' };
  }
  if (domain.length > 253) {
    return { valid: false, error: 'Domain exceeds 253 characters' };
  }

  // Step 3: Check for disposable email domains (optional)
  const disposable = ['tempmail.com', 'throwaway.email', 'guerrillamail.com'];
  if (disposable.includes(domain.toLowerCase())) {
    return { valid: false, error: 'Disposable email addresses not allowed' };
  }

  return { valid: true };
}

// Step 4 (server-side): Send verification email
// This is the ONLY way to truly validate an email address

URL Validation Regex

URL validation is another case where regex works well for basic checks but falls short for complete RFC 3986 compliance. For most APIs, you need to validate that a string is a well-formed HTTP or HTTPS URL with a valid domain.

Practical URL Regex

// URL regex -- HTTP/HTTPS with domain validation
const URL_PATTERN = /^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_+.~#?&/=]*)$/;

// Test cases:
URL_PATTERN.test('https://example.com');                  // true
URL_PATTERN.test('https://www.example.com/page');         // true
URL_PATTERN.test('http://sub.domain.co.uk/path?q=1');    // true
URL_PATTERN.test('https://example.com/path#section');     // true
URL_PATTERN.test('ftp://example.com');                    // false
URL_PATTERN.test('example.com');                          // false
URL_PATTERN.test('https://');                             // false

Better Approach: Use the URL Constructor

For JavaScript and Node.js, the built-in URL constructor is more reliable than any regex. It parses URLs according to the WHATWG URL Standard and throws on invalid input.

// URL validation using the URL constructor (recommended)
function validateUrl(input) {
  try {
    const url = new URL(input);

    // Optional: restrict to HTTP/HTTPS
    if (!['http:', 'https:'].includes(url.protocol)) {
      return { valid: false, error: 'Only HTTP and HTTPS URLs are allowed' };
    }

    // Optional: block localhost and private IPs (SSRF prevention)
    const hostname = url.hostname;
    if (hostname === 'localhost' || hostname === '127.0.0.1' ||
        hostname.startsWith('192.168.') || hostname.startsWith('10.') ||
        hostname.startsWith('172.16.')) {
      return { valid: false, error: 'Private URLs not allowed' };
    }

    return { valid: true, parsed: url };
  } catch {
    return { valid: false, error: 'Invalid URL format' };
  }
}

// Python equivalent:
# from urllib.parse import urlparse
# def validate_url(input_url):
#     try:
#         result = urlparse(input_url)
#         return all([result.scheme in ('http', 'https'), result.netloc])
#     except ValueError:
#         return False

Slug and Path Validation

// URL slug (kebab-case, lowercase, no special chars)
const SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;

SLUG.test('my-blog-post');      // true
SLUG.test('post-123');          // true
SLUG.test('My-Post');           // false (uppercase)
SLUG.test('-leading-dash');     // false
SLUG.test('trailing-dash-');    // false
SLUG.test('double--dash');      // false

// URL path segment (allows slashes)
const URL_PATH = /^\/(?:[a-z0-9-]+\/)*[a-z0-9-]+\/?$/;

URL_PATH.test('/blog/my-post');     // true
URL_PATH.test('/api/v2/users/');    // true

Phone Number Validation Regex

Phone number validation is notoriously difficult because formats vary wildly by country. A US number looks like (555) 123-4567, a UK number looks like +44 7911 123456, and a Japanese number looks like 090-1234-5678. For production applications, use Google's libphonenumber library. For basic format checking, these patterns work as a first pass.

US Phone Number

// US phone: (xxx) xxx-xxxx, xxx-xxx-xxxx, xxx.xxx.xxxx, xxxxxxxxxx
const US_PHONE = /^(?:\+1\s?)?(?:\(?\d{3}\)?[\s.-]?)\d{3}[\s.-]?\d{4}$/;

US_PHONE.test('(555) 123-4567');    // true
US_PHONE.test('555-123-4567');      // true
US_PHONE.test('555.123.4567');      // true
US_PHONE.test('5551234567');        // true
US_PHONE.test('+1 555 123 4567');   // true
US_PHONE.test('123-456');           // false (too short)
US_PHONE.test('(555) 123-45678');   // false (too long)

International Phone (E.164 Format)

// E.164 format: + country code + subscriber number (7-15 digits total)
const E164_PHONE = /^\+?[1-9]\d{6,14}$/;

E164_PHONE.test('+14155552671');     // true (US)
E164_PHONE.test('+447911123456');    // true (UK)
E164_PHONE.test('+5511987654321');   // true (Brazil)
E164_PHONE.test('+81901234567');     // true (Japan)
E164_PHONE.test('+0123456789');      // false (starts with 0)
E164_PHONE.test('+123');             // false (too short)

Phone Validation in Practice

// Normalize phone input before validation
function normalizePhone(input) {
  // Strip everything except digits and leading +
  return input.replace(/(?!^\+)[^\d]/g, '');
}

function validatePhone(input, country = 'US') {
  const cleaned = normalizePhone(input);

  const patterns = {
    US: /^\+?1?\d{10}$/,
    UK: /^\+?44\d{10}$/,
    BR: /^\+?55\d{10,11}$/,
    E164: /^\+?[1-9]\d{6,14}$/,  // International fallback
  };

  const pattern = patterns[country] || patterns.E164;
  return pattern.test(cleaned);
}

// For production: use libphonenumber
// npm install google-libphonenumber
// const phoneUtil = require('google-libphonenumber').PhoneNumberUtil.getInstance();
// const number = phoneUtil.parse('+14155552671', 'US');
// phoneUtil.isValidNumber(number);  // true

Date and Time Validation Regex

Date validation with regex checks the format but not the logic (February 30th passes a format check but is not a real date). Use regex for format enforcement and a date library for logical validation.

// ISO 8601 date: YYYY-MM-DD
const ISO_DATE = /^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$/;

ISO_DATE.test('2026-03-06');    // true
ISO_DATE.test('2026-12-31');    // true
ISO_DATE.test('2026-13-01');    // false (month 13)
ISO_DATE.test('2026-00-15');    // false (month 00)
ISO_DATE.test('26-03-06');      // false (2-digit year)

// ISO 8601 datetime with timezone: YYYY-MM-DDTHH:MM:SSZ
const ISO_DATETIME = /^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])T(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/;

ISO_DATETIME.test('2026-03-06T14:30:00Z');           // true
ISO_DATETIME.test('2026-03-06T14:30:00+05:30');      // true
ISO_DATETIME.test('2026-03-06T14:30:00.123Z');       // true
ISO_DATETIME.test('2026-03-06T25:00:00Z');            // false (hour 25)

// Time only: HH:MM or HH:MM:SS (24-hour)
const TIME_24H = /^(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d)?$/;

TIME_24H.test('14:30');       // true
TIME_24H.test('14:30:59');    // true
TIME_24H.test('24:00');       // false
TIME_24H.test('2:30');        // false (need leading zero)

// Complete date validation (format + logic)
function isValidDate(dateString) {
  if (!/^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$/.test(dateString)) {
    return false;  // Format check
  }
  const date = new Date(dateString);
  const [year, month, day] = dateString.split('-').map(Number);
  // Logic check: does the parsed date match the input?
  return date.getFullYear() === year
      && date.getMonth() + 1 === month
      && date.getDate() === day;
}

isValidDate('2026-02-28');  // true
isValidDate('2026-02-29');  // false (2026 is not a leap year)
isValidDate('2024-02-29');  // true  (2024 is a leap year)

Additional Useful Patterns

Beyond emails, URLs, and phones, here are other common validation patterns you will need when building APIs. Each pattern is ready to copy into your codebase.

Password Strength

// Strong password: 8+ chars, uppercase, lowercase, digit, special char
const STRONG_PASSWORD = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;

STRONG_PASSWORD.test('MyP@ss1word');   // true
STRONG_PASSWORD.test('weakpass');       // false (no uppercase, digit, special)
STRONG_PASSWORD.test('Short1!');        // false (less than 8 chars)

// Note: NIST now recommends checking passwords against breach databases
// (Have I Been Pwned API) rather than enforcing complex rules.
// Minimum 8 characters + breach check > complex regex rules

Username

// Username: 3-20 chars, alphanumeric + underscore/hyphen
// Must start with a letter
const USERNAME = /^[a-zA-Z][a-zA-Z0-9_-]{2,19}$/;

USERNAME.test('john_doe');        // true
USERNAME.test('dev-user123');     // true
USERNAME.test('ab');              // false (too short)
USERNAME.test('1user');           // false (starts with number)
USERNAME.test('user name');       // false (space)

IPv4 and IPv6 Addresses

// IPv4: 0.0.0.0 to 255.255.255.255
const IPV4 = /^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/;

IPV4.test('192.168.1.1');     // true
IPV4.test('10.0.0.255');      // true
IPV4.test('256.1.1.1');       // false (256 > 255)
IPV4.test('1.2.3');           // false (only 3 octets)

// IPv6 (simplified -- accepts most common formats)
const IPV6 = /^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/;

IPV6.test('2001:0db8:85a3:0000:0000:8a2e:0370:7334');  // true

Credit Card Number (Luhn-checkable formats)

// Major card formats (format check only -- always use Luhn + processor)
const VISA = /^4\d{12}(?:\d{3})?$/;          // Starts with 4, 13 or 16 digits
const MASTERCARD = /^5[1-5]\d{14}$/;          // Starts with 51-55, 16 digits
const AMEX = /^3[47]\d{13}$/;                // Starts with 34/37, 15 digits

// Strip spaces/dashes before testing
const cleaned = '4111 1111 1111 1111'.replace(/[\s-]/g, '');
VISA.test(cleaned);  // true

// Always validate with Luhn algorithm + payment processor
function luhnCheck(num) {
  let sum = 0;
  let even = false;
  for (let i = num.length - 1; i >= 0; i--) {
    let digit = parseInt(num[i], 10);
    if (even) { digit *= 2; if (digit > 9) digit -= 9; }
    sum += digit;
    even = !even;
  }
  return sum % 10 === 0;
}

Hex Color Code

// Hex color: #RGB or #RRGGBB (with optional alpha)
const HEX_COLOR = /^#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;

HEX_COLOR.test('#fff');         // true (shorthand)
HEX_COLOR.test('#FF5733');      // true (6-digit)
HEX_COLOR.test('#FF573380');    // true (8-digit with alpha)
HEX_COLOR.test('#gggggg');      // false (invalid hex chars)
HEX_COLOR.test('FF5733');       // false (no #)

Quick Reference Table

Copy the pattern you need from this summary table. Test them all in our Regex Tester before deploying.

InputPatternNotes
Email (simple)/^[^\s@]+@[^\s@]+\.[^\s@]+$/Good for 99.9% of emails
URL (HTTP/S)Use new URL() constructorMore reliable than regex
US Phone/^(?:\+1\s?)?(?:\(?\d{3}\)?[\s.-]?)\d{3}[\s.-]?\d{4}$/All common US formats
Intl Phone/^\+?[1-9]\d{6,14}$/E.164 format
ISO Date/^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$/YYYY-MM-DD format only
Password8+ chars, upper+lower+digit+specialPrefer breach DB check
IPv40-255 per octet, 4 groupsFull range validation
Hex Color#RGB, #RRGGBB, #RRGGBBAAWith optional alpha

Avoiding Over-Engineering: When Regex Is Not the Answer

Regex is a powerful tool, but it is not always the right one. Over-engineered regex patterns become maintenance nightmares, introduce subtle bugs, and give a false sense of security. Here are cases where you should use a different approach.

  1. Email deliverability -- No regex can tell you if an email exists. Send a verification email instead of trying to catch every edge case with a 5,000-character pattern.
  2. URL parsing -- The URL constructor in JavaScript (or urlparse in Python) handles edge cases that regex misses, including punycode, IPv6 hosts, and port numbers.
  3. International phone numbers -- Country-specific rules are too complex for regex. Use libphonenumber for production validation.
  4. HTML parsing -- Famously, regex cannot parse HTML. Use a DOM parser (DOMParser, Cheerio, BeautifulSoup).
  5. JSON validation -- Use JSON.parse() in a try-catch, not regex. For complex validation, use a JSON validator with schema support.
// Use validation libraries for complex schemas
// Zod (TypeScript-first schema validation)
import { z } from 'zod';

const UserSchema = z.object({
  email: z.string().email(),
  name: z.string().min(2).max(100),
  age: z.number().int().min(13).max(150),
  website: z.string().url().optional(),
  phone: z.string().regex(/^\+?[1-9]\d{6,14}$/),
});

// Validates and returns typed data or throws
const user = UserSchema.parse(req.body);

// Joi (Node.js validation)
const Joi = require('joi');
const schema = Joi.object({
  email: Joi.string().email().required(),
  phone: Joi.string().pattern(/^\+?[1-9]\d{6,14}$/),
});

Client-Side vs Server-Side Validation

A common mistake is treating client-side validation as a security measure. Client-side regex provides instant feedback for the user, but it can be bypassed by anyone using browser developer tools, curl, or Postman. Always validate on both sides.

AspectClient-SideServer-Side
PurposeUser experience (instant feedback)Security (data integrity)
Bypassable?Yes (always)No (unless server is compromised)
SpeedInstant (no network round-trip)Requires API call
Error messagesInline, field-levelReturned in API response
Required?Nice to haveMandatory
// Client-side: instant UX feedback
<input
  type="email"
  pattern="[^\s@]+@[^\s@]+\.[^\s@]+"
  title="Enter a valid email address"
  required
/>

// Server-side: the actual validation (Express middleware)
function validateBody(schema) {
  return (req, res, next) => {
    const { error, value } = schema.validate(req.body, {
      abortEarly: false,  // Return ALL errors, not just the first
      stripUnknown: true, // Remove unknown fields
    });

    if (error) {
      return res.status(400).json({
        errors: error.details.map(d => ({
          field: d.path.join('.'),
          message: d.message,
        })),
      });
    }

    req.validatedBody = value;
    next();
  };
}

app.post('/api/users', validateBody(userSchema), createUser);

Frequently Asked Questions

What is the best regex for email validation?

For most applications, the practical pattern /^[^\s@]+@[^\s@]+\.[^\s@]+$/ is sufficient. It catches obvious errors (missing @, no domain) without rejecting valid edge cases. For strict RFC 5322 compliance, use a dedicated email validation library instead of regex, as the full specification is too complex for a single pattern.

Should I use regex or a library for input validation?

Use regex for simple format checks (does it look like an email?) and libraries for strict validation (is it a deliverable email?). Libraries like Zod, Yup, and joi provide composable validation with better error messages. For URLs, the built-in URL constructor in JavaScript is more reliable than any regex.

Why does my email regex reject valid addresses?

Common reasons include: not allowing + tags ([email protected]), not supporting new TLDs longer than 3 characters (.technology, .museum), not handling subdomains ([email protected]), or requiring specific TLD formats. The safest approach is to validate loosely (has @ and a dot after it) and confirm via a verification email.

How do I validate international phone numbers with regex?

International phone numbers vary so widely in format that no single regex handles all cases reliably. Use the libphonenumber library (Google) for production validation. For a basic format check, /^\+?[1-9]\d{6,14}$/ matches E.164 format which covers most international numbers after stripping spaces and dashes.

Is client-side regex validation enough for security?

No. Client-side validation is for user experience only -- it provides instant feedback but can be bypassed by anyone using browser dev tools, curl, or Postman. Always validate on the server side as well. Client-side regex catches typos; server-side validation prevents malicious input. Both layers are necessary for a secure application.

Test Your Regex Patterns

Paste any regex pattern into our free Regex Tester to see matches highlighted in real time. Test against multiple input strings, view capture groups, and debug your patterns. Everything runs locally in your browser.

Open Regex Tester

Related Articles