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).isValidNumberdoes "full validation of a phone number for a region using length and prefix information".getNumberTypereturns the type of the range the number falls in, such asMOBILE,FIXED_LINE,TOLL_FREEorVOIP, orFIXED_LINE_OR_MOBILEwhere the plan doesn't separate them, as in the United States.- The carrier mapper (
PhoneNumberToCarrierMapperin Java,phonenumbers.carrierin 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.
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.
| Rung | Question | Answered by | libphonenumber? |
|---|---|---|---|
| 1. Possible | Is the length right for the country? | isPossibleNumber | Yes |
| 2. Valid | Is it in a range the numbering plan opens for assignment? | isValidNumber, getNumberType | Yes |
| 3. Assigned | Has an operator issued this number to a subscriber? | The operator's records, via an HLR lookup (invalid = not assigned) | No |
| 4. Active on the network | Can the phone be reached right now? | HLR lookup: reachable or unreachable | No |
| 5. Ported | Which network serves it today? | MNP lookup or HLR lookup | No, only the original operator |
| 6. Registered on apps | Is it registered on a messaging app? | Registration checks | No |
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.
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 networkPython 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.
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.
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 regionsNot 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.
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):
{
"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?
| Moment | What to run | Why |
|---|---|---|
| Sign-up form, while typing | libphonenumber (min metadata is fine in the browser) | Instant feedback, no cost |
| Sign-up form, on submit | libphonenumber /max on the server, store E.164 | Client checks can be bypassed |
| Before sending an OTP | Validation, then an HLR lookup with max_age: 0 | Offer another channel at once if the number is unreachable or invalid |
| Cleaning a list | Validation on every row, then a bulk HLR lookup on the valid ones | Drop impossible numbers for free; pay only for the network check |
| Routing SMS by network | MNP lookup | Current 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?
- 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.
- Validating on the server with min metadata. In libphonenumber-js,
isValid()with the default import checks mostly lengths and leading digits, not exact ranges. Uselibphonenumber-js/maxfor the final check. - 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
getSafeDisplayNameif you must display it, and an MNP lookup if you route on it. - Treating
getNumberTypeas the live line type. The type belongs to the range.FIXED_LINE_OR_MOBILEis common in North America, and a number ported to a VoIP service keeps its old range. - 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.
- Turning a missing answer into "inactive". An HLR
unknownor a timeout means no answer. Retry later or fall back; don't block a real user on it. - 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
- libphonenumber FAQ: What does it mean for a phone number to be valid? — Google, 2026
- libphonenumber README — Google, 2026
- PhoneNumberToCarrierMapper (getNameForNumber, getSafeDisplayName) — Google libphonenumber, 2026
- libphonenumber-js README — GitHub (catamphetamine), 2026
- python-phonenumbers README — GitHub (daviddrysdale), 2026
- 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.
Related services and guides
More from the blog
All articlesGuides
How to check if a phone number is active (and what "active" really means)
Valid, active, reachable and registered mean different things. The methods for checking each one, what they cost and where every method falls short.
7 min read
Guides
HLR lookup vs MNP lookup vs number validation: what each one answers
Validation checks the digits, MNP finds the current network, HLR asks the network if the number is live. What each answers, costs and misses.
7 min read
Developers
E.164 regex: why a pattern is not enough to validate phone numbers
The common E.164 regex accepts impossible numbers. Real counterexamples, the shortest valid international numbers, and what to use a regex for instead.
7 min read

