Bulk Phone Number Validator & Formatter (Offline)
Pricing
from $0.56 / 1,000 number checkeds
Bulk Phone Number Validator & Formatter (Offline)
Validate, format (E.164 / international / national) and classify phone numbers in bulk with Google libphonenumber. Offline, no data leaves Apify.
Pricing
from $0.56 / 1,000 number checkeds
Rating
0.0
(0)
Developer
James Brown
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
Phone Number Validator & Formatter (Bulk, Offline)
This phone number validator checks, formats and classifies phone numbers in bulk. Paste a list or point it at a CSV and get E.164, international and national formats, the number type (mobile, fixed line, VoIP, toll-free, premium rate…), region, timezones and carrier name (where Google's data has it) for every number, straight from Google's libphonenumber rules.
Fully offline. No third-party API, no lookups, no scraping. The numbers you submit are checked by a local copy of the libphonenumber rules, so they never leave your Apify run. That makes it fast, cheap and private: $0.80 per 1,000 numbers.
Why use this phone number validator
| This actor | Lookup / enrichment actors | |
|---|---|---|
| Data source | Local libphonenumber rules (Apache-2.0) | Third-party carrier / HLR / web lookups |
| Your numbers sent to a third party | No | Often yes |
| Speed | Thousands of numbers per second | Limited by remote lookups |
| Cost | $0.005 start + $0.0008 per number | Varies: compare their per-number price |
| Live line status (is the phone switched on?) | No | Sometimes |
If you need to know whether a line is currently live (HLR lookup), this is not that tool. If you need to clean, standardise and classify a list (CRM import, SMS list hygiene, dialler prep, dedupe), this is the cheap and private way to do it. It is an offline alternative to lookup-based actors such as memo23/phone-enrichment and automation-lab/phone-number-validator.
What the phone number formatter returns
original, valid, possible, possibleReason, e164, international, national, countryCallingCode, regionCode, location, numberType, carrier, timezones, extension, duplicateOf, and, only on rows re-written after a restart, recoveredAfterRestart.
Input
| Field | Description |
|---|---|
numbers | List of numbers in any common format |
text | Free-text blob, one number per line (or ; separated) |
csvUrl + csvColumn | Public CSV URL, read as a stream (up to 200 MB). Column by header name or 1-based index; auto-detects headers like "phone" / "mobile" / "tel" |
defaultCountry | ISO code (e.g. GB, US, DE) used for numbers written without + country code. Default GB |
dedupe | Flag repeats (compared by E.164) with duplicateOf; duplicates are free. Default on |
Run it with no input and you get a 10-number sample.
Example input
{"numbers": ["+44 20 7946 0958", "07911 123456", "+1 (650) 253-0000", "not a number"],"defaultCountry": "GB","dedupe": true}
Example output
{"original": "+44 20 7946 0958","valid": true,"possible": true,"possibleReason": "IS_POSSIBLE","e164": "+442079460958","international": "+44 20 7946 0958","national": "020 7946 0958","countryCallingCode": 44,"regionCode": "GB","location": "London","numberType": "FIXED_LINE","carrier": null,"timezones": ["Europe/London"],"extension": null,"duplicateOf": null}
Invalid inputs are still returned, with valid: false and the reason in possibleReason (NOT_A_NUMBER, TOO_SHORT, INVALID_COUNTRY_CODE…), so you can match results back to your source rows.
Pricing (pay per event)
| Event | Price | When it is charged |
|---|---|---|
| Actor start | $0.005 | Once per run |
| Number checked | $0.0008 | Per phone number checked, valid or not (duplicates free) |
| Numbers | Approximate cost |
|---|---|
| 1,000 | $0.805 |
| 10,000 | $8.005 |
| 100,000 | $80.005 |
- Actor start: $0.005 per run (Apify's
apify-actor-startevent). Maximum run memory is capped at 1,024 MB so the start event is never multiplied. - Number checked: $0.0008 per number (
number-checkedevent). Charged for every input that is checked, valid or not. - Duplicates flagged by
dedupeare not charged.
Example: 100,000 numbers ≈ $80.00 + $0.005. The run stops cleanly if you set a maximum cost per run.
Performance
Numbers are processed as a stream in batches of 1,000, and progress is saved to the key-value store (PROGRESS) so a restart or migration continues where it left off. Results are never lost and never double-charged. In rare cases a platform restart leaves one batch whose outcome is unknown; that batch is written again without charge, so you may see duplicate rows, and every such row carries recoveredAfterRestart: true so you can de-duplicate (drop the flagged row if the number also appears unflagged). Measured end to end on a Windows dev machine (Python 3.11, local file storage, no pricing configured): 20,000 numbers took 66 s with a peak of about 182 MB resident memory (5,000 numbers: 21 s, 180 MB, so memory is dominated by the interpreter and libphonenumber metadata, not by input size). Validation alone runs at roughly 3,400 numbers/s; the rest is dataset writes. A 100,000-number run is projected at about 5-6 minutes and under 256 MB; this projection has not been run on the Apify platform, where dataset write speed will differ.
Data source, licence and compliance
- Source: the
phonenumbersPython port of Google's libphonenumber. Both are licensed under Apache-2.0 (verified 2026-10-21 against theLICENSEfile in the python-phonenumbers repository). Apache-2.0 permits commercial use and redistribution with attribution. The bundled metadata (formats, carrier prefixes, timezones, geocoding) is part of the same project. - ToS / robots.txt: not applicable to the validation itself. The actor makes no network requests to any data provider, and nothing is scraped. The only network access is when you supply a
csvUrl: the actor then fetches that file, identifies itself with a User-Agent, and honours the host'srobots.txt(it refuses to fetch if disallowed, and also if robots.txt cannot be fetched or returns a 5xx error; every redirect target is checked again, private/internal addresses are blocked, and requests are spaced about 1 second apart; this cannot be switched off). - Credentials in URLs (
user:pass@host) are not sent: the actor never makes authenticated requests, so use a URL that needs no login. - Attribution: powered by Google libphonenumber via python-phonenumbers (Apache-2.0).
- Personal data: phone numbers are supplied by you. The actor does not store them beyond the run's dataset and key-value store (which you control and can delete), makes no external calls with them, and logs no numbers, CSV row contents or full URLs. You are responsible for having a lawful basis (e.g. UK GDPR) for processing the numbers you submit.
- Monitoring / "changed since" mode: not applicable, as there is no external source that changes. Output is deterministic for a given library version.
Limitations
- Checks numbers against numbering plans and formats, not whether a line is live or who owns it. "Valid" means the number is allocated within a valid range for its country.
- Carrier is the original allocation holder and is only available for some countries; it is empty for many (including GB, where number portability makes prefix data unreliable). It is never a live lookup.
- Number type can be
FIXED_LINE_OR_MOBILEorUNKNOWNwhere the plan does not distinguish them. - Numbers must be one per row/line; commas are not used as separators in
text(use new lines or;). - Rules follow the bundled libphonenumber release and change as new versions are published.
- CSV input: quoted multi-line fields are parsed as one record.
csvColumntakes a header name or a 1-based number;csvHeader(auto/yes/no) says whether the first row is a header, so a headerless file withcsvColumn: 2keeps its first row. ThePROGRESSkey-value record holds only counters, never numbers.
FAQ
How do I validate phone numbers in bulk?
Add them to numbers, paste them into text, or give a public csvUrl. Set defaultCountry for numbers written without a + prefix. Each number becomes one result row.
Can it format phone numbers to E.164?
Yes. Every row has e164, international and national formats. Invalid numbers keep valid: false and a reason in possibleReason.
Does it tell mobile from landline and VoIP numbers?
It returns numberType (mobile, fixed line, VoIP, toll-free, premium rate and more) from the libphonenumber numbering plans. It does not check whether a line is currently switched on.
Are my numbers sent to a third party? No. Validation runs offline inside your Apify run.
Related Actors
- Email Validator & List Cleaner: clean an email list alongside your phone list (MX, disposable domains, typos).
- PDF to Markdown for RAG: turn PDF, DOCX, PPTX and HTML into Markdown for LLMs.
- Domain Health Checker: SPF, DMARC, DKIM and SSL expiry checks for any domain.