Phone Number Validator - Bulk Carrier & Line Type Lookup avatar

Phone Number Validator - Bulk Carrier & Line Type Lookup

Pricing

$1.00 / 1,000 per number validateds

Go to Apify Store
Phone Number Validator - Bulk Carrier & Line Type Lookup

Phone Number Validator - Bulk Carrier & Line Type Lookup

Validate and normalise phone numbers in bulk. Returns E.164, country, region, timezone, line type and carrier. For US & Canada it adds the assigned LERG carrier, wireless/landline/VoIP type, rate centre and LATA from free public data - no API key, no per-lookup fees.

Pricing

$1.00 / 1,000 per number validateds

Rating

0.0

(0)

Developer

Scrapers Delight

Scrapers Delight

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Share

☎️ Phone Number Validator — Bulk Carrier & Line Type Lookup

Validate, normalise and enrich phone numbers in bulk. Every number comes back as E.164 with its country, region, timezone, formats and line type — and for US and Canada numbers it also comes back with the assigned carrier, the wireless / landline / VoIP class, the rate centre and the LATA.

65 fields per row. No API key. No per-lookup fee. Nothing to sign up for.


🎯 Why this one, and not a plain libphonenumber wrapper

Google's libphonenumber is the standard for phone validation, and it is genuinely excellent — everywhere except North America. Ask it what kind of line +1 212 736 5000 is and it answers FIXED_LINE_OR_MOBILE. It answers that for every geographic US and Canada number, because the NANP numbering plan does not reserve mobile ranges the way most countries do. Carrier comes back null for the same reason.

That is exactly the answer a lead list, an SMS campaign or a TCPA screen needs, and it is the one field a libphonenumber-only validator cannot give you.

This actor closes it with a second, free data source: the public LERG / NPA-NXX block assignment tables, which record who each 1,000-number block was allocated to and what class of carrier they are.

Every row below is a real value from a real Apify run on 2026-09-02, not an illustration:

Numberlibphonenumber aloneThis actor
+1 212 736 5000FIXED_LINE_OR_MOBILE, carrier nulllandline-ilec · VERIZON NEW YORK, INC. · rate centre New York City Zone 01 · LATA 132
+1 415 937 2000FIXED_LINE_OR_MOBILE, carrier nullwireless · T-MOBILE USA, INC. · rate centre Belvedere · LATA 722
+1 415 937 1234FIXED_LINE_OR_MOBILE, carrier nullclec-or-voip · BANDWIDTH.COM CLEC, LLC — CA
+1 303 913 1311FIXED_LINE_OR_MOBILE, carrier nullwireless · CELLCO PARTNERSHIP DBA VERIZON WIRELESS — CO
+1 604 685 3300FIXED_LINE_OR_MOBILE, carrier nulllandline-ilec · TELUS COMMUNICATIONS, INC. — BC

Note the first three rows: 415-937-2000 and 415-937-1234 share an area code and an exchange, and still belong to different carriers of different classes. Block assignment is per thousands-block, not per exchange, and this actor resolves it at that granularity.


🚀 Quick start

{
"phoneNumbers": [
"+1 415 937 2000",
"+1 212 736 5000",
"(604) 685-3300",
"4155552671",
"+44 20 7183 8750"
],
"defaultCountry": "US" // how to read numbers written without a "+"
}

Have more than a handful? Use any of these instead — they all merge into one run:

InputWhat it takes
phoneNumbersa list of strings
phoneNumbersTextone pasted blob — newline, comma, semicolon or tab separated
numbersFileUrla public TXT / CSV / JSON / JSONL URL; CSV headers are auto-detected
sourceDatasetIdanother Apify dataset — e.g. the output of a lead scraper

sourceField names the column or JSON key holding the number. Leave it empty and a field called phone, mobile, cell, tel, number or whatsapp is picked automatically.


📤 Output — 65 fields per row

One row per submitted number, in submission order, so the output lines up with your list.

{
"input_raw": "+1 415 937 2000",
"input_index": 0,
"e164": "+14159372000",
"is_valid": true,
"is_possible": true,
"validation_reason": null,
"country": "US",
"country_name": "United States",
"country_calling_code": "1",
"national_number": "4159372000",
"number_type": "FIXED_LINE_OR_MOBILE",
"format_national": "(415) 937-2000",
"format_international": "+1 415 937 2000",
"format_rfc3966": "tel:+14159372000",
"format_idd": "1 (415) 937-2000",
"tel_uri": "tel:+14159372000",
"extension": null,
"carrier_code": null,
"is_non_geographic": false,
"possible_countries": ["US"],
"geo_description": "California",
"carrier_name_intl": null,
"timezones": ["America/Los_Angeles"],
"timezone_primary": "America/Los_Angeles",
"digit_count": 11,
"is_mobile": true,
"is_landline": false,
"is_voip": false,
"is_toll_free": false,
"is_premium_rate": false,
"is_shared_cost": false,
"is_pager": false,
"is_uan": false,
"is_voicemail": false,
"is_personal_number": false,
"is_duplicate": false,
"duplicate_of_index": null,
"is_nanp": true,
"npa": "415",
"nxx": "937",
"line_number": "2000",
"block_id": "2",
"npa_is_erc": false,
"lerg_carrier_name": "T-MOBILE USA, INC.",
"lerg_carrier_ocn": "6529",
"lerg_company_type": "W",
"line_type_nanp": "wireless",
"rate_center": "Belvedere",
"rate_center_region": "CA",
"rate_center_lat": 37.86717,
"rate_center_lon": -122.460023,
"rate_center_v": 8476,
"rate_center_h": 8730,
"lata": "722",
"switch_clli": "OKLECAZV1MD",
"switch_name": null,
"switch_type": "POI",
"ilec_ocn": "9740",
"ilec_name": "PACIFIC BELL",
"exch": "018620",
"lir": null,
"effective_date": null,
"disconnect_date": null,
"lerg_updated": "2026-09-01 00:40:05",
"lerg_status": "ok"
}

Field table with measured fill rates

Measured on the real Apify run described under Measured performance: 64 numbers submitted, 64 rows returned, 62 parsed, 50 US/CA prefixes resolved in LERG. Two fill columns, because a field that only applies to a US landline should not be scored against a German mobile:

FieldTypeWhat it isFill (all 64)Fill (applicable rows)
input_rawstringyour original string, echoed back untouched100%
input_indexint0-based position in the merged input list100%
e164stringthe canonical +CCNNNNNNNNN form97%100% of 62 parsed
is_validboolmatches an assigned range for its country100%
is_possibleboolright length for its country, range not checked100%
validation_reasonstringNOT_A_NUMBER · INVALID_COUNTRY · TOO_SHORT · TOO_LONG · INVALID_LENGTH; null when valid3% (only the 2 rejects)
countrystringISO-3166 alpha-2 of the range, not the dialling country97%100% of parsed
country_namestringEnglish country name97%100% of parsed
country_calling_codestringe.g. 1, 44, 4997%100% of parsed
national_numberstringNSN — the number without the country code97%100% of parsed
number_typestringlibphonenumber type (11 values, see below)97%100% of parsed
format_nationalstring(415) 937-200097%100% of parsed
format_internationalstring+1 415 937 200097%100% of parsed
format_rfc3966stringtel:+14159372000;ext=42 — extension included97%100% of parsed
format_iddstringthe digits to dial from your dialFromCountry97%100% of parsed
tel_uristringbare tel: URI, no extension parameter97%100% of parsed
extensionstringparsed extension, e.g. 422%1 of 1 with an extension
carrier_codestringnational carrier-selection code, where the number carries one0%none in this batch
is_non_geographicbool+800, +870, +882… — a calling code with no country97%100% of parsed
possible_countriesarrayambiguity on shared codes (+1, +7, +44)97%100% of parsed
geo_descriptionstringe.g. San Francisco, CA, London, Beijing89%92% of parsed
carrier_name_intlstringnon-NANP carrier: Optus, T-Mobile, China Mobile, Airtel, KPN…6%6% of parsed — see limits
timezonesarrayIANA zones the range can sit in97%100% of parsed
timezone_primarystringfirst zone, for a flat column97%100% of parsed
digit_countintdigits in the E.164 form, + excluded97%100% of parsed
is_mobilebooltrue / false, or null when genuinely unknowable94%
is_landlineboolas above80%
is_voipboolas above83%
is_toll_freeboolderived from number_type97%100% of parsed
is_premium_rateboolderived from number_type97%100% of parsed
is_shared_costboolderived from number_type97%100% of parsed
is_pagerboolderived from number_type97%100% of parsed
is_uanboolderived from number_type97%100% of parsed
is_voicemailboolderived from number_type97%100% of parsed
is_personal_numberboolderived from number_type97%100% of parsed
is_duplicateboolthis exact endpoint already appeared in your list100%
duplicate_of_indexintinput_index of the first occurrence2% (the 1 duplicate)
is_nanpboolUS / Canada / Caribbean numbering plan100%
npastringarea code81%100% of NANP
nxxstringexchange81%100% of NANP
line_numberstringlast 4 digits81%100% of NANP
block_idstringLERG thousands-block, 09, or A for a whole-NXX assignment78%100% of the 50 resolved
npa_is_ercbooleasily-recognisable code: 800/888/877/866/855/844/833/822/90081%100% of NANP
lerg_carrier_namestringassigned block holder — see limits78%100% of the 50 resolved
lerg_carrier_ocnstringOperating Company Number78%100% of the 50 resolved
lerg_company_typestringraw OCN class: W wireless, I ILEC, C CLEC78%100% of the 50 resolved
line_type_nanpstringwireless · landline-ilec · clec-or-voip78%100% of the 50 resolved
rate_centerstringe.g. New York City Zone 01, Belvedere78%100% of the 50 resolved
rate_center_regionstringUS state / Canadian province78%100% of the 50 resolved
rate_center_latnumberrate-centre latitude78%100% of the 50 resolved
rate_center_lonnumberrate-centre longitude78%100% of the 50 resolved
rate_center_vnumberV coordinate (telco grid)78%100% of the 50 resolved
rate_center_hnumberH coordinate (telco grid)78%100% of the 50 resolved
latastringLocal Access and Transport Area78%100% of the 50 resolved
switch_cllistringserving switch CLLI code78%100% of the 50 resolved
switch_namestringswitch name, where published58%74% of the 50 resolved
switch_typestringe.g. 5E, POI78%100% of the 50 resolved
ilec_ocnstringincumbent carrier OCN for the rate centre78%100% of the 50 resolved
ilec_namestringincumbent carrier name, e.g. PACIFIC BELL78%100% of the 50 resolved
exchstringexchange identifier78%100% of the 50 resolved
lirstringlocal interconnection region, where published6%8% of the 50 resolved
effective_datestringblock effective date — usually blank in the source0%0% of the 50 resolved
disconnect_datestringblock disconnect date — blank unless the block is being returned0%0% of the 50 resolved
lerg_updatedstringwhen the source last refreshed this record78%100% of the 50 resolved
lerg_statusstringok · not_found · not_applicable · unavailable · disabled100%

number_type values, all eleven confirmed against a real range while building this actor: FIXED_LINE, MOBILE, FIXED_LINE_OR_MOBILE, TOLL_FREE, PREMIUM_RATE, SHARED_COST, VOIP, PERSONAL_NUMBER, PAGER, UAN, VOICEMAIL.

lerg_status values: ok a block record was found · not_found the prefix is genuinely not in the geographic LERG tables (toll-free NPAs and the reserved 555 exchange both legitimately return nothing) · not_applicable the row is not a geographic NANP number · unavailable the source could not be reached for this row — the offline fields are unaffected · disabled you switched the enrichment off.


📊 Measured performance

Everything here is from real runs on the Apify platform on 2026-09-02, on build 0.1.3 at the actor's declared 1024 MB. No estimates.

RunInputStatusDurationRowsCharged events
Health-test simulation ×4prefill-only, 12 numbersSUCCEEDED3.59 – 4.77 s12number-validated: 12
Empty input {} ×3none — falls back to the demo batchSUCCEEDED3.45 – 3.63 s12
Real batch ×264 numbers, 32 area codes, 13 countriesSUCCEEDED7.10 s / 11.87 s64number-validated: 64

On the 64-number batch:

  • 64 rows for 64 submitted numbers — nothing is silently dropped; the 2 unparseable strings come back with a validation_reason.
  • 62 of 64 parsed, 62 valid. The 2 rejects were not a phoneNOT_A_NUMBER and +999 12345INVALID_COUNTRY.
  • 52 NANP rows; 50 resolved in LERG. The 2 that did not: +1 800 266 2278 (toll-free, not_applicable — never queried) and 4155552671 (the reserved 555 exchange, not_found).
  • Line-type split actually returned: 34 landline-ilec, 9 clec-or-voip, 7 wireless.
  • 30 HTTP requests to the carrier feed, for 49 distinct prefixes across 32 area codes (2 were already cached from an earlier run). One area code needed 18 different exchanges and was pulled in a single whole-area-code request that cached all 784 exchanges in it. Zero failures.
  • Charged events equalled delivered rows exactly, at both sizes: {"number-validated": 12} against itemCount: 12, and {"number-validated": 64} against itemCount: 64.

Throughput. The offline leg is CPU-bound at roughly 50,000 numbers/minute. The carrier feed is the only latency, and it collapses to about one request per area code once the cache is warm — the cache lives in a named key-value store, so it survives across runs.


💰 Pricing

EventPriceFires
number-validated$0.001 per numberonce for each number delivered to your dataset

$1.00 per 1,000 numbers, flat. The US/Canada carrier and line-type enrichment is included — it is not a second billable event, because it is the point of the actor.

Rows you filter out with Only valid numbers or Skip duplicates are never delivered, so you are never charged for them. Delivery and billing are atomic (Actor.pushData(rows, 'number-validated')), so if you set a spend cap the run stops at it instead of handing you rows you paid nothing for — or billing you for rows you never got.


⚠️ Honest limits — read this before you buy

1. This does not prove the line rings. Everything here is static numbering-plan metadata plus public block-assignment records. It answers "is this well-formed, where does the range live, and what kind of line is it". It never answers "is this number in service right now". There is no HLR dip, no ping, no live network check. If you need "is it connected", this is not that product.

2. lerg_carrier_name is the ASSIGNED carrier, not necessarily today's carrier. Local number portability means a large share of US mobile numbers have moved carrier at least once, and LERG records who the block was allocated to. We do not do a live LNP dip, so we do not claim current carrier. Line type is the field to trust. A wireless-to-wireless port keeps the block's W class, so line_type_nanp survives porting far better than carrier identity does. Use line_type_nanp for compliance and channel decisions; treat lerg_carrier_name as "the carrier this range was issued to".

3. clec-or-voip is genuinely ambiguous, and we say so rather than guessing. LERG's C class covers both competitive landline carriers and VoIP/CPaaS providers (Bandwidth, Twilio, Onvoy and friends). We can tell you it is not a wireless block and not an incumbent landline; we cannot split CLEC from VoIP, so on those rows is_landline and is_voip are null, not false. A null here means "unknown", never "no".

4. carrier_name_intl fills on only 6% of rows, and that is the metadata, not a bug. Google's carrier tables only cover mobile ranges in the countries that publish them (DE, AU, IN, NL, NG, CN, GG and others). It is null for landlines everywhere and for all NANP numbers — for NANP the answer lives in lerg_carrier_name instead.

5. country is the range's assigned region, not the dialling country. +44 7911 123456 returns GG (Guernsey), not GB, because that mobile range is allocated to a Crown dependency. The same surprise exists across +1 Caribbean ranges (BB, JM, TT…). This is metadata-correct and it will still look like a bug the first time you see it — hence this note.

6. effective_date and disconnect_date measured 0% fill. The source leaves both blank for blocks in normal service; they populate only around a pending assignment change. They are included because they are real columns, not because they are usually there.

7. Two data sources, so two failure modes — and only one of them can stop a run. The libphonenumber leg is bundled into the image and cannot fail. The carrier feed is a small public reference site, so this actor is deliberately gentle with it: results are cached in a named key-value store across runs, whole area codes are pulled in one request when a run needs 8+ exchanges from one, concurrency is capped at 4 with a stagger between requests, and any error is fail-soft — the row still ships, marked lerg_status: "unavailable", with every offline field intact. A run never fails because the carrier feed was slow, and a carrier-feed outage is never reported to you as "no results found".


🔍 Where the data comes from

LegSourceCostVerified
Validation, formats, type, geo, timezone, non-NANP carrierGoogle's libphonenumber metadata, bundled into the image (libphonenumber-js/max + libphonenumber-geo-carrier)offline, no network11 of 11 number_type values confirmed on real ranges, 2026-09-02
US/Canada carrier, line class, rate centre, LATA, switch, ILECthe public LERG / NPA-NXX block tables republished by localcallingguide.com as XMLfree, no key, no accountHTTP 200 / 11,478 bytes for a single prefix and HTTP 200 / 3,595,753 bytes with 5,838 records for a whole area code, 2026-09-02

The carrier feed answered identically from a home IP, an Apify datacenter IP and an Apify residential IP when this actor was built — same status, same byte count. There is no anti-bot wall, no CAPTCHA and no user-agent gate on this path, and the two real Apify runs above resolved 50 of 52 prefixes with no proxy at all. Proxy support is exposed anyway, for anyone who wants to spread a very large run.

https://localcallingguide.com/robots.txt reads, in full:

User-agent: *
Disallow: /cgi-bin/

The path this actor reads (/xmlprefix.php) is not under /cgi-bin/ and is not disallowed.


❓ FAQ

Can it tell me whether a US number is a mobile or a landline? Yes — that is the reason this actor exists. line_type_nanp returns wireless, landline-ilec or clec-or-voip, and is_mobile / is_landline are set from it. A libphonenumber-only validator returns FIXED_LINE_OR_MOBILE for every geographic US number and cannot answer this.

Can it tell me the carrier? For US and Canada, it returns the carrier the number's 1,000-block was assigned to, plus their OCN. For mobile numbers in countries that publish carrier ranges (Germany, Australia, India, the Netherlands, Nigeria and others) carrier_name_intl gives the carrier directly. Neither is a live ported-carrier lookup — see limit #2.

Does it check whether the number is active or reachable? No. Nothing here dials, pings or does an HLR lookup. See limit #1 — it is stated plainly rather than buried.

Is it useful for TCPA screening? The W class identifies numbers issued in a wireless block, which is the distinction TCPA rules turn on, and the field is stable across wireless-to-wireless porting. It is one input to a compliance process, not a compliance product, and it carries no litigator or DNC list. Take your legal advice from your lawyer, not from a scraper's README.

What formats can I feed it? Anything libphonenumber accepts: +1 415 937 2000, (415) 937-2000, 415.937.2000, +1-415-937-2000 ext. 42, 4159372000. Numbers without a + are read using defaultCountry.

How do I load 50,000 numbers? Point numbersFileUrl at a public TXT/CSV/JSON file, or set sourceDatasetId to another Apify dataset — the output of a lead scraper, for example. Both stream in and merge with anything typed into the other fields.

Will I be charged for invalid numbers? Only if they are delivered. They are, by default, because a rejection with a reason is usually the useful answer. Switch on Only valid numbers and the invalid ones are dropped before delivery and never billed.

How does duplicate detection work? Numbers are compared after normalisation, so (415) 937-2000 and +14159372000 are the same endpoint. An extension makes a different endpoint: +1 212 736 5000 and +1 212 736 5000 ext. 42 are not duplicates of each other. is_duplicate and duplicate_of_index are always populated; Skip duplicates additionally drops the repeats.

Why did +44 7911 123456 come back as GG and not GB? Because that range really is allocated to Guernsey. See limit #5.

Does it need a proxy? No. Both real Apify runs resolved carrier data with no proxy. A proxy field is there if you want it.

How fresh is the carrier data? The source refreshes daily — a record pulled on 2026-09-02 carried lerg_updated: 2026-09-01 00:40:05. This actor caches each prefix for 30 days by default (lergCacheDays); block assignments move slowly, and the cache is what keeps the load on a small public reference site reasonable.

What happens if the carrier feed goes down mid-run? The run still succeeds and still delivers every row. Affected rows carry lerg_status: "unavailable" and keep all of their offline fields. A blocked or unreachable source is never reported to you as "no results found".

Can I switch the carrier lookup off? Yes — set nanpEnrichment: false for a purely offline run with no outbound requests at all.


⚖️ Fair use

Both data sources are public reference data: Google's libphonenumber metadata is open source (Apache 2.0) and bundled into the image, and the NANP block-assignment tables are public numbering-plan records republished by localcallingguide.com, whose robots.txt is quoted verbatim above. This actor caches aggressively, batches whole area codes into single requests, caps concurrency at 4 and staggers its requests, precisely so that scaling it up does not become someone else's problem.

You are responsible for how you use the output — for consent, for TCPA/GDPR/CASL obligations, and for your own do-not-call and marketing compliance. Validating a number is not permission to call it.