Guides
Last updated
July 20, 2025

5 Ways to Validate Phone Numbers in Zod

Nicolas Rios
Nicolas Rios
Table of Contents:
ON THIS PAGE
Get your free
phone validation
 API key now
stars rating
4.8 from 1,863 votes
See why the best developers build on Abstract
START FOR FREE
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
No credit card required

Validating phone numbers in Zod is a common step for maintaining data quality in applications. We'll explore five ways to implement this with code snippets, look at the drawbacks of traditional approaches, and see how Abstract API helps overcome them without the usual trade-offs.

How to Implement Phone Number Validation in Zod

Here are four common methods to validate phone numbers in Zod. Each approach uses a different tool or technique, from simple regular expressions to specialized libraries for comprehensive checks.

Direct Regex Match

When you only need a basic syntactic check, you can use Zod’s built-in `regex` method. This approach uses a regular expression to validate the phone number's format. It requires no external dependencies and executes quickly in any JavaScript environment, as shown in this example.

const phone = z.string().regex(/^([+]?[\s0-9]+)?(\d{3}|[(]?[0-9]+[)])?([-]?[\s]?[0-9])+$/, 'invalid');

.refine() with validator.js

For more specific validation, you can combine Zod's `refine` method with the `validator.js` library. The library's `isMobilePhone` function encapsulates a maintained catalog of country-specific rules, which helps keep the main Zod schema clean and readable. This method checks against numerous regional formats.

import validator from 'validator';
const phone = z.string().refine(v => validator.isMobilePhone(v, 'any'), { message: 'invalid' });

transform / superRefine with libphonenumber-js

A more powerful option involves the `libphonenumber-js` library, often used with Zod's `transform` method. This combination can parse, validate, and normalize phone numbers into the standard E.164 format. The code attempts to parse the input, and if it is a valid number, it returns the normalized version, as detailed in this Zod issue.

import { parsePhoneNumberFromString } from 'libphonenumber-js';
const phone = z
  .string()
  .transform((v, ctx) => {
    const p = parsePhoneNumberFromString(v, { defaultCountry: 'US', extract: false });
    if (!p?.isValid()) {
      ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'invalid' });
      return z.NEVER;
    }
    return p.number;
  });

z.e164() in Zod 4

Zod 4 introduced a dedicated `e164` helper for validating numbers against the strict E.164 profile. This built-in function is a simple regex that confirms the number starts with a '+' and is followed by one to fifteen digits. It offers a clean API surface without extra code.

const phone = z.e164();

Challenges of Phone Number Validation in Zod

These methods offer quick solutions but introduce significant issues with maintenance and accuracy. The validation often fails to confirm if a number is actually usable in a production environment.

  • Zod lacks a native phone schema. This forces developers to use large, static regex patterns that break as global numbering rules change. This approach creates brittle code and high maintenance overhead.
  • Libraries like libphonenumber-js accept non-dialable numbers. Zod's refine method simply forwards the library's result, which allows invalid numbers to pass validation unless you add extra logic to catch these edge cases.
  • The metadata in libraries like libphonenumber-js often lags behind official numbering plan updates. A validator built on this library can reject valid new numbers or approve old, deprecated ones until the data updates.
  • Zod only validates a number's format, not its real-world status. A syntactically correct number from z.e164() might be unreachable or unassigned. The validation provides no guarantee the number is actually usable.

Validate Phone Numbers with Abstract API
Validate phone numbers effectively in Zod to maintain clean user data and improve application reliability.
Get started for free

How Abstract API Handles Phone Number Validation in Zod

Abstract API addresses the core weaknesses of traditional phone validation inside a Zod schema. It offloads the logic to a cloud service that supplies real-time phone intelligence to an asynchronous .refine predicate.

  • It moves beyond simple syntax checks to confirm if a number is actually assigned and reachable.
  • The service removes the maintenance burden of local pattern updates because it stays current with numbering plans for over 195 countries.
  • A single HTTPS call returns a simple validity flag plus metadata like carrier, location, and line type, without the need for large offline libraries.

How to Bring Abstract API to Your Dev Environment

Once you understand Abstract’s capabilities, it is simple to add its phone number validation API to your project.

  • Sign up at Abstract and copy your phone validation API key from the dashboard.
  • Install the necessary packages:
  • /cod
  • Add your API key to a local .env file as ABSTRACT_API_KEY=your_key.
  • Create a Zod schema that calls Abstract inside an async .refine predicate.
  • Use schema.parseAsync(data) in your route or controller to trigger the validation.
  • Optionally, map the full Abstract response to a typed object if you need carrier or location data downstream.
npm i zod node-fetch dotenv –-save

Sample Phone Number Validation Implementation with Abstract API

The code below defines a Zod schema for a phone number. It uses an asynchronous .refine function to call the Abstract API endpoint with the number that needs validation. The function awaits the response and returns true only if the API confirms the number is valid. This process confirms the number is not just syntactically correct but also active and reachable.

import 'dotenv/config'
import { z } from 'zod'
import fetch from 'node-fetch'

const phoneSchema = z.string().nonempty().refine(
  async (value) => {
    const url = `https://phonevalidation.abstractapi.com/v1/?api_key=${process.env.ABSTRACT_API_KEY}&phone=${encodeURIComponent(value)}`
    const res = await fetch(url)
    const data = await res.json()
    return data.valid === true  // Zod passes only when Abstract says the number is valid
  },
  { message: 'Phone number is invalid or inactive' }
)

// usage: await phoneSchema.parseAsync(req.body.phone)

The API returns a simple JSON object to the .refine function. While the valid:true field is all you need for the pass or fail logic in Zod, the full response provides much more context:

{
  "phone": "14152007986",
  "valid": true,
  "format": { "international": "+14152007986", "local": "(415) 200-7986" },
  "country": { "code": "US", "name": "United States", "prefix": "+1" },
  "location": "California",
  "type": "mobile",
  "carrier": "T-Mobile USA, Inc."
}
  • format: Lets you normalize phone numbers for consistent storage and display.
  • country, location, carrier: Enable geo-based logic, fraud heuristics, or custom rate limits.
  • type: Distinguishes between mobile and landline numbers, so you can decide whether to send an SMS.

Final Thoughts

Traditional phone validation methods rely on static patterns that only check syntax and require constant updates. They cannot confirm if a number is active.

Abstract API replaces this flawed model with a real-time check against live carrier data. Consider an account with Abstract API to get your free API key and reliably validate user phone numbers.

Validate Phone Numbers with Abstract API
Validate phone numbers in your Zod schemas to collect accurate information from your users.
Get started for free

Related Articles

Phone Validation
key now
Get your free
stars rating
4.8 from 1,863 votes
See why the best developers build on Abstract
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
No credit card required