1. Regex: shape only
A regex is fine as a first filter, for example to reject letters in an input field:
const E164 = /^\+[1-9]\d{1,14}$/;
E164.test("+442079460958"); // true
E164.test("+15555555555"); // true, but not a real number
It cannot know that UK mobile numbers start with 7, that German numbers have 6 to 13 digits, or that 555 numbers are fictional.
2. libphonenumber-js: numbering-plan validation
libphonenumber-js is a JavaScript port of Google’s libphonenumber with the official metadata for every country.
npm install libphonenumber-js
import { parsePhoneNumberFromString } from "libphonenumber-js/max";
const phone = parsePhoneNumberFromString("07400 123456", "GB");
if (phone && phone.isValid()) {
console.log(phone.number); // "+447400123456" (E.164, store this)
console.log(phone.formatInternational()); // "+44 7400 123456"
console.log(phone.getType()); // "MOBILE"
console.log(phone.country); // "GB"
}
isValid()checks length and prefixes against the country’s numbering plan.isPossible()only checks the length, which is useful for live feedback while typing.getType()returnsMOBILE,FIXED_LINE,VOIP,TOLL_FREEetc. (needs the/maxmetadata).
This is exactly what the free phone number validator on this site runs.
3. A phone validation API: carrier, line type, trusted result
Client-side checks are easy to bypass and cannot tell you the carrier. For sign-ups, SMS sending and fraud checks, validate on the server with the numlookupapi API:
// Node.js 18+ (built-in fetch)
async function validatePhone(number) {
const url = `https://api.numlookupapi.com/v1/validate/${encodeURIComponent(number)}`;
const res = await fetch(url, { headers: { apikey: process.env.NUMLOOKUP_API_KEY } });
if (!res.ok) throw new Error(`numlookupapi ${res.status}`);
return res.json();
}
const result = await validatePhone("+14158586273");
// { valid: true, line_type: "mobile", carrier: "AT&T Mobility LLC",
// country_code: "US", location: "Novato", e164_format: "+14158586273", ... }
Numbers without a country code can be validated with ?country_code=GB. To check up to 100 numbers per request, POST /v1/validate with {"numbers": [...]}.
Putting it together in a form
- Validate with libphonenumber-js while the user types and show a hint.
- On submit, call your backend, which calls the API and stores
e164_format. - Reject
valid: false, and decide what to do withline_type: "voip"if you need real mobile numbers (e.g. for two-factor authentication).
Get a free API key with 100 requests per month on the pricing page.