Phone Number Validator & Enrichment (bulk, 200+ countries)
Pricing
from $0.80 / 1,000 number validateds
Phone Number Validator & Enrichment (bulk, 200+ countries)
Clean a phone list in one run: validity, formatting in three styles, number type, country, region, carrier and timezones for 200+ countries, plus optional discovery of phone numbers on company websites. No API keys needed. Built for CRM hygiene, lead enrichment and SMS prep.
Pricing
from $0.80 / 1,000 number validateds
Rating
0.0
(0)
Developer
Paul Vasquez
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
Phone Number Validator and Enrichment
Normalize phone lists, inspect numbering-plan validity, and discover telephone
numbers on public homepages and contact pages. This Python 3.12 Apify actor uses
the offline phonenumbers port of libphonenumber. List processing requires no
network, API key, provider account, or paid lookup service. Optional website
discovery uses HTTP requests and HTML parsing without a browser.
What validation means
valid means that a parsed number matches the library's numbering-plan metadata.
possible primarily checks whether its length is plausible. A possible number
can still be invalid. A valid number can be unassigned, disconnected, or unable
to receive messages. This actor performs no HLR lookup and makes no “is active”
claim. It does not call numbers, send SMS, identify subscribers, or verify
ownership. Metadata may lag numbering-plan changes.
Carrier information describes the original allocation where available. Number portability means it may not identify the current carrier. Geographic descriptions refer to numbering areas, not a person's present location. Timezones are candidate zones from metadata, not a device location. Missing enrichment is returned as null or an empty array instead of an invented value.
Input
phoneNumbers accepts up to 10,000 strings. List order and duplicates are
preserved, so each input has a corresponding result unless a charging limit
prevents additional paid output. defaultRegion defaults to US; use an ISO
region such as GB, DE, or IN for national numbers. International numbers
starting with + carry their own country calling code.
websiteUrls optionally accepts up to 100 HTTP(S) URLs. Each URL is normalized
to its origin homepage, followed by the origin's /contact path. Credentials
in URLs are rejected. Repeated normalized sites are scanned once. The actor
follows up to five redirects, but does not discover alternative contact links,
execute JavaScript, solve access challenges, or crawl a site's other pages.
outputFormat is e164, international, or national, defaulting to e164.
It selects the additional formatted field; all three explicit format fields
remain present. includeCarrier defaults to true. includeGeo defaults to true
and controls geographic description and timezones; country and region remain
available. timeoutSecs bounds each complete page attempt, from 1 to 60 seconds.
The supplied daily input uses five seconds and 15 mixed numbers plus Python.org
and Apify, requiring no keys and completing comfortably under two minutes in
the recorded validation.
Results
Number rows contain input, source, valid, possible, e164,
international, national, formatted, countryCode, region, regionName,
numberType, carrier, location, timezones, isMobile, isTollFree,
isVoip, and error. countryCode is numeric; region is a library region
code. Non-geographic numbers may use region 001. Descriptions are English.
Types include mobile, fixed line, fixed-line-or-mobile, VoIP, toll-free, premium
rate, and unknown. isMobile includes the ambiguous fixed-line-or-mobile type;
consult numberType when the distinction matters.
Website number rows additionally include websiteUrl, foundOn, and context.
Context contains the matched text with up to 40 characters on either side.
Scripts, styles, and noscript content are excluded; visible text and telephone
links are matched with PhoneNumberMatcher using POSSIBLE leniency. E.164
deduplication applies within each website across both pages, retaining the first
occurrence. Extensions sharing the same E.164 number collapse together. Numbers
from separate websites or the explicit list remain separate results.
Filter rowType=number when exporting only telephone results. Each attempted
unique website also produces an uncharged website-summary row with fetched-page
count, discovered-number count, failures, and timing. SUMMARY in the key-value
store records aggregate counts and eligible billing events. A successful fetch
with zero matches is an ordinary empty result, not proof the business has no phone.
Pricing and failures
number-validated costs $0.0008 per parseable number row, whether valid or
invalid. Empty and unparseable strings produce uncharged error rows.
website-scanned costs $0.002 once per website with at least one successfully
fetched HTML page, including websites with zero matches. It is not charged per
page. Discovered parseable numbers also incur the number event. Summary rows
themselves are always free; completely failed websites incur no website event.
HTTP 429, server errors, and transport failures receive two retries with one- and two-second backoff. Other HTTP errors are not retried. Responses exceeding two megabytes or non-HTML responses are rejected. Partial website results survive page failures. Charging limits stop further events of that type. Charging occurs before persistence, so storage failures cannot be automatically rolled back.
Running and verification
Create a Python 3.12 virtual environment, install requirements.txt, then run:
apify validate-schema .actor/input_schema.json.venv/Scripts/python.exe -m unittest discover -s tests -vpowershell -NoProfile -ExecutionPolicy Bypass -File validation/run_live.ps1
The live harness creates isolated local storage and records results and timings
under validation/. Local SDK charges are ignored, not actual billing. Before
deployment, configure both custom events from .actor/pay_per_event.json in
Console and disable synthetic charges. See VALIDATION.md for measured evidence
and remaining platform checks. Library behavior is documented in the
upstream project; charging
uses the Apify SDK.
Example output
Recorded local validation output from validation/results.json, the first item in rows. Fields are omitted for brevity; retained values are unchanged. This is a historical example, not a current-source claim.
{"rowType": "number","input": "+1 202-555-0123","source": "list","valid": true,"possible": true,"e164": "+12025550123","region": "US","numberType": "FIXED_LINE_OR_MOBILE","carrier": null,"error": null}
Use cases
- A CRM administrator normalizes imported telephone strings to E.164 while routing unparseable records to a cleanup queue.
- A customer support operations manager separates toll-free, fixed-line, and ambiguous mobile classifications before reviewing contact-routing rules.
- A data quality consultant compares valid and possible flags in a client-supplied list to prioritize manual record correction.
- A business directory editor scans supplied public websites for contact numbers and reviews the retained page context before updating listings.
Pricing example
1,000 parseable number rows and 100 successfully scanned websites cost (1,000 x $0.0008) + (100 x $0.002) = $1.00 in declared events. The 1,000 includes any discovered numbers; extra discovered parseable rows add $0.0008 each. Rates come from .actor/pay_per_event.json. This calculation is an event subtotal, not a measured invoice; local validation does not bill.
Limitations
The saved example comes from the validation input list, not a verified subscriber. A true validity flag establishes neither reachability nor ownership. National-format results depend on the selected region. Website discovery covers only the homepage and /contact; zero matches do not establish absence of a telephone number.