Developers

Does libphonenumber check if a phone number is active? isValidNumber vs reachable

No. libphonenumber checks numbering-plan metadata, not the network. What isValidNumber proves, what it can't, and how to check if a number is live.

By Published 7 min read

On this page

No. libphonenumber checks whether a phone number fits its country's numbering plan, using metadata that ships with the library. It never contacts a network, so it can't tell you whether the number has been issued to anyone, whether the phone is switched on, which network serves it after a port, or whether it's registered on a messaging app. isValidNumber returning true means "this number could exist", not "this number is active". To find out whether a number is live, send the validated E.164 number to an HLR lookup, which asks the number's home network.

What does libphonenumber actually check?

Google's libphonenumber packages every country's numbering plan as metadata: country codes from ITU-T E.164, lengths, and the digit ranges each country has opened for each kind of service. Its main functions read that metadata and nothing else:

  • isPossibleNumber "quickly guesses whether a number is a possible phone number by using only the length information" (libphonenumber README).
  • isValidNumber does "full validation of a phone number for a region using length and prefix information".
  • getNumberType returns the type of the range the number falls in, such as MOBILE, FIXED_LINE, TOLL_FREE or VOIP, or FIXED_LINE_OR_MOBILE where the plan doesn't separate them, as in the United States.
  • The carrier mapper (PhoneNumberToCarrierMapper in Java, phonenumbers.carrier in Python) returns a carrier name for some mobile ranges.

The library's FAQ defines a valid number range as "one from which numbers can be freely assigned by carriers to users", and then says it plainly: "Do not rely on libphonenumber to determine whether numbers are currently assigned to a specific user and reachable." The FAQ adds that confirming reachability needs a verification step, such as an SMS or a call.

The carrier mapper has the same limit. Its documentation says the name "is the one the number was originally allocated to, however if the country supports mobile number portability the number might not belong to the returned carrier anymore" (PhoneNumberToCarrierMapper). That is why getSafeDisplayName returns an empty string for any region with mobile number portability.

A six-rung ladder: a code panel ticks the two bottom rungs, possible and valid, offline, while the four upper rungs, assigned, active, ported and apps, stay open until a network tower answers them.A six-rung ladder: a code panel ticks the two bottom rungs, possible and valid, offline, while the four upper rungs, assigned, active, ported and apps, stay open until a network tower answers them.
A validation library answers the first two rungs; only the network knows if a number is assigned and live.

Which questions can libphonenumber answer, and which need the network?

Think of it as a ladder. libphonenumber answers the first two rungs for free, offline, in microseconds. Every rung above that needs live data.

RungQuestionAnswered bylibphonenumber?
1. PossibleIs the length right for the country?isPossibleNumberYes
2. ValidIs it in a range the numbering plan opens for assignment?isValidNumber, getNumberTypeYes
3. AssignedHas an operator issued this number to a subscriber?The operator's records, via an HLR lookup (invalid = not assigned)No
4. Active on the networkCan the phone be reached right now?HLR lookup: reachable or unreachableNo
5. PortedWhich network serves it today?MNP lookup or HLR lookupNo, only the original operator
6. Registered on appsIs it registered on a messaging app?Registration checksNo

Two side questions also need outside data: the current line type (carrier lookup) and the number's reputation (spam reputation). We compare rungs 3 to 5 in HLR vs MNP vs number validation, and the guide to checking if a phone number is active covers every meaning of "active".

What does validation look like in code?

The calls are the same in every port. We ran the JavaScript and Python examples below on 2026-10-10 with libphonenumber-js 1.13.13 and phonenumbers 9.0.40; the outputs are real.

JavaScript with libphonenumber-js. Import from /max on the server: with the default min metadata, isValid() checks mostly lengths and leading digits, and getType() usually returns undefined. libphonenumber-js has no carrier-name lookup. More in our JavaScript validation guide.

JavaScript
import { parsePhoneNumberFromString } from "libphonenumber-js/max";

const p = parsePhoneNumberFromString("+12001230101");
p.isPossible(); // true  (right length for +1)
p.isValid();    // false (area code 200 is not in use)

const m = parsePhoneNumberFromString("+447911123456");
m.isValid();    // true
m.getType();    // "MOBILE"
m.number;       // "+447911123456": store this, then check the network

Python with phonenumbers. The carrier module shows the portability problem: a name for the range, but no "safe" name, because Romania has number portability. See the Python validation guide.

Python
import phonenumbers
from phonenumbers import carrier, PhoneNumberFormat

n = phonenumbers.parse("+40721234567", None)
phonenumbers.is_valid_number(n)          # True
phonenumbers.number_type(n)              # 1 (PhoneNumberType.MOBILE)
carrier.name_for_number(n, "en")         # 'Vodafone' (original operator of the range)
carrier.safe_display_name(n, "en")       # ''  (Romania has number portability)
phonenumbers.format_number(n, PhoneNumberFormat.E164)  # '+40721234567'

Java with Google's library (com.googlecode.libphonenumber:libphonenumber, plus the separate carrier artifact for the mapper). Details in the Java and Kotlin guide.

JAVA
import com.google.i18n.phonenumbers.*;
import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
import java.util.Locale;

PhoneNumberUtil util = PhoneNumberUtil.getInstance();
PhoneNumber n = util.parse("+40721234567", null); // throws NumberParseException
boolean valid = util.isValidNumber(n);
PhoneNumberUtil.PhoneNumberType type = util.getNumberType(n);
String e164 = util.format(n, PhoneNumberUtil.PhoneNumberFormat.E164);

PhoneNumberToCarrierMapper carriers = PhoneNumberToCarrierMapper.getInstance();
String original = carriers.getNameForNumber(n, Locale.ENGLISH);  // range's original operator
String safe = carriers.getSafeDisplayName(n, Locale.ENGLISH);    // "" in portable regions

Not one of these calls tells you if +40721234567 is in service. They can't: the answer lives in the operator's subscriber register.

How do you check whether a valid number is reachable?

Send the E.164 number to an HLR lookup. It asks the number's home network and returns status: reachable, unreachable (assigned, but the phone is off or out of coverage), invalid (the network reports it as not assigned) or unknown. It also returns ported, roaming (true or false only), network, mcc_mnc and country.

With a test key, +447700900002 is a documented test number that returns unreachable. Test calls are free and never reach a network; see test mode. libphonenumber reports this range as invalid on purpose, because the UK regulator reserves it for drama, so let it through your validator only when you use a test key.

Shell
curl https://api.mobilevalidate.com/v1/lookup \
  -H "Authorization: Bearer $MOBILEVALIDATE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"numbers": ["+447700900002"], "checks": ["hlr"]}'

Response (excerpt, test mode):

JSON
{
  "e164": "+447700900002",
  "number_status": "valid",
  "checks": {
    "number.hlr": {
      "status": "completed",
      "attributes": {
        "status": "unreachable",
        "ported": false,
        "roaming": false,
        "network": "Test Mobile",
        "mcc_mnc": "23415",
        "country": "GB"
      },
      "billed": false
    }
  }
}

The API runs rungs 1 and 2 itself: input that isn't a valid number comes back as number_status: "invalid_number" and is never checked or billed. Treat unknown, pending and unsupported_country as "no answer", never as "inactive". The HLR lookup results guide explains each status.

When should you use libphonenumber, and when a live check?

MomentWhat to runWhy
Sign-up form, while typinglibphonenumber (min metadata is fine in the browser)Instant feedback, no cost
Sign-up form, on submitlibphonenumber /max on the server, store E.164Client checks can be bypassed
Before sending an OTPValidation, then an HLR lookup with max_age: 0Offer another channel at once if the number is unreachable or invalid
Cleaning a listValidation on every row, then a bulk HLR lookup on the valid onesDrop impossible numbers for free; pay only for the network check
Routing SMS by networkMNP lookupCurrent network for ported numbers, cheaper than HLR

Cost. libphonenumber is free. Our HLR and MNP lookups are paid per conclusive answer, at the real-time or bulk price on the pricing page, and the MNP lookup costs less than the HLR lookup. For the HLR lookup, reachable, unreachable and invalid are billed; unknown is free. Invalid input, duplicates and repeat checks served from your account's cache aren't charged, test keys are never billed, and POST /v1/jobs/estimate shows the maximum cost of a list before it runs.

What mistakes do developers make with isValidNumber?

  1. Reading "valid" as "exists". A valid number may never have been issued, or may have been disconnected and is waiting to be reissued. Only the network knows.
  2. Validating on the server with min metadata. In libphonenumber-js, isValid() with the default import checks mostly lengths and leading digits, not exact ranges. Use libphonenumber-js/max for the final check.
  3. Showing the carrier mapper's name as the current carrier. It is the range's original operator. After a port it is wrong, silently. Use getSafeDisplayName if you must display it, and an MNP lookup if you route on it.
  4. Treating getNumberType as the live line type. The type belongs to the range. FIXED_LINE_OR_MOBILE is common in North America, and a number ported to a VoIP service keeps its old range.
  5. Letting the metadata go stale. Numbering plans change every few weeks. An old version rejects newly opened ranges, as the libphonenumber-js README warns. Update the library regularly.
  6. Turning a missing answer into "inactive". An HLR unknown or a timeout means no answer. Retry later or fall back; don't block a real user on it.
  7. Sending HLR lookups for landlines. Only mobile numbers have a home register to ask. Check the line type first if your list mixes both.

Check only numbers you have a lawful reason to process, such as your own users and people who gave you their number; the acceptable use policy applies to every lookup.

What is the short answer?

  • libphonenumber answers "could this number exist?" from metadata: possible, valid and range type.
  • It never answers "is it assigned?", "is it on right now?", "which network serves it today?" or "is it on an app?".
  • Its carrier name is the original operator and can be wrong after a port.
  • Validate first because it's free, then use an HLR lookup when you need to know the number is live.

To try the live step, get a free test key and run the request above.

Sources

  1. libphonenumber FAQ: What does it mean for a phone number to be valid? — Google, 2026
  2. libphonenumber README — Google, 2026
  3. PhoneNumberToCarrierMapper (getNameForNumber, getSafeDisplayName) — Google libphonenumber, 2026
  4. libphonenumber-js README — GitHub (catamphetamine), 2026
  5. python-phonenumbers README — GitHub (daviddrysdale), 2026
  6. Recommendation ITU-T E.164: The international public telecommunication numbering plan — ITU, 2010

Frequently asked questions

Does isValidNumber mean the phone number is in service?

No. isValidNumber means the digits fall inside a range that the country's numbering plan opens for assignment. It does not know whether an operator has issued the number to anyone, or whether the phone is switched on. libphonenumber's own FAQ says not to rely on it for that.

What is the difference between isPossibleNumber and isValidNumber?

isPossibleNumber checks only the length for the country. isValidNumber also checks the leading digits against the ranges in the metadata. +1 200 123 0101 has the right length for the United States, so it is possible, but it is not valid, because area code 200 is not in use.

Can libphonenumber tell me the current carrier of a number?

No. The carrier mapper in the Java library and the Python port returns the operator the range was originally allocated to. In countries with number portability that can be wrong, and getSafeDisplayName returns an empty string there. libphonenumber-js has no carrier-name lookup at all.

How do I check if a phone number is active, then?

Validate it with libphonenumber first, which is free and offline. Then send the E.164 number to an HLR lookup, which asks the number's home network whether it is reachable, unreachable or not assigned. Use an MNP lookup if you only need the current network.

Does libphonenumber-js /max check whether a number exists?

No. The max metadata makes isValid() check exact number ranges and lets getType() return a type, but it is still metadata. A number can pass isValid() with /max and still be unassigned, disconnected or switched off.

All articles

Know before you send.

Tell us about your use case. We review every request and set you up with test and live keys.