Regex Patterns for API Validation: Email, URL, Phone (Copy-Paste Ready)
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 addressURL 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://'); // falseBetter 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 FalseSlug 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/'); // truePhone 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); // trueDate 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 rulesUsername
// 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'); // trueCredit 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.
| Input | Pattern | Notes |
|---|---|---|
| Email (simple) | /^[^\s@]+@[^\s@]+\.[^\s@]+$/ | Good for 99.9% of emails |
| URL (HTTP/S) | Use new URL() constructor | More 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 |
| Password | 8+ chars, upper+lower+digit+special | Prefer breach DB check |
| IPv4 | 0-255 per octet, 4 groups | Full range validation |
| Hex Color | #RGB, #RRGGBB, #RRGGBBAA | With 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.
- 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.
- URL parsing -- The
URLconstructor in JavaScript (orurlparsein Python) handles edge cases that regex misses, including punycode, IPv6 hosts, and port numbers. - International phone numbers -- Country-specific rules are too complex for regex. Use
libphonenumberfor production validation. - HTML parsing -- Famously, regex cannot parse HTML. Use a DOM parser (DOMParser, Cheerio, BeautifulSoup).
- 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.
| Aspect | Client-Side | Server-Side |
|---|---|---|
| Purpose | User experience (instant feedback) | Security (data integrity) |
| Bypassable? | Yes (always) | No (unless server is compromised) |
| Speed | Instant (no network round-trip) | Requires API call |
| Error messages | Inline, field-level | Returned in API response |
| Required? | Nice to have | Mandatory |
// 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 TesterRelated Articles
Regex Cheat Sheet
Complete reference for regex syntax, quantifiers, anchors, and lookaheads.
How to Format JSON
Format and validate JSON payloads from API responses and config files.
HTTP Status Codes Guide
Understand 400 Bad Request and other validation error responses.
JWT vs Session Cookies
Compare authentication methods for APIs that need input validation.