Phone Number Validator & Formatter: E.164, Type, Country
Pricing
from $0.30 / 1,000 phone number validateds
Phone Number Validator & Formatter: E.164, Type, Country
Offline phone validator: lists or CSV/JSON/XLSX to E.164, country, type, reason. $0.30 per 1,000. Format check only: no carrier, region or time zone lookup.
Pricing
from $0.30 / 1,000 phone number validateds
Rating
0.0
(0)
Developer
Kaizoku Data
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Share
Check and clean phone numbers you supply (a list, or a CSV / JSON / XLSX file): validity, E.164, international and national formats, country, number type (mobile, fixed line, VoIP, toll-free, premium rate, ...) and the reason when a number is invalid. Runs offline with Google's libphonenumber rules (via libphonenumber-js): no browser, no proxy, no API key.
Price: $0.30 per 1,000 numbers ($0.0003 per number). One row per number, valid or invalid. Blank lines, dropped duplicates and file-error rows are free. No start fee of our own on top of Apify's default.
- Normalise: any common format in, E.164 out (
+442079460958), plus international (+44 20 7946 0958), national (020 7946 0958) and RFC 3966 (tel:+442079460958) forms. - Classify: country (ISO code and name), calling code and number type from the national number plan, including
FIXED_LINE_OR_MOBILEwhere the plan cannot tell. - Explain: every invalid number gets a reason code (
TOO_SHORT,TOO_LONG,INVALID_COUNTRY,NOT_A_NUMBER,INVALID_NUMBER, ...): see Reason codes. - Bulk: up to 1,000,000 numbers per run, from a typed list or up to 20 files (CSV, TSV, JSON, NDJSON, XLSX; 50 MB each), with optional de-duplication on E.164.
- Quick start: press Start. The prefilled input validates 12 fictional or reserved numbers (UK, US and Australian ranges set aside for fiction) in about a second for $0.0036.
What "valid" means here. This Actor checks format and number-plan ranges only: whether a number has a plausible length and falls inside a range that the country's numbering plan has allocated. It makes no network lookups, no carrier or HLR queries and no owner or reverse lookups, so it cannot tell you whether a line is connected, reachable, ported or who owns it. A valid number may be disconnected; a number in a recently allocated range may show as invalid until the library's rules are updated.
What it does not do. There is no carrier lookup, no region or city lookup (no geocoding) and no time-zone lookup.
countrycomes from the calling code and the number plan only (for exampleGB,US);typesays mobile, fixed line, VoIP and so on, but never which operator or where. Those need data this Actor does not have.
Input
| Field | What it does |
|---|---|
| Phone numbers | One number per line, any common format. Prefilled with fictional numbers. Blank lines are skipped free, and so is any cell longer than 1,000 characters (see INPUT_TOO_LARGE). |
| File URLs | Direct http(s) links to a CSV/TSV, JSON, NDJSON or XLSX file (up to 20, 50 MB each). Private and local addresses are blocked, as are Google and social-platform links (upload the file instead). A link that returns a web page (a "share" link from Dropbox, OneDrive and similar sites), a PDF or an image is refused as FILE_NOT_DATA, free. |
| Uploaded files | Upload from your computer to a key-value store (the field offers this) or paste a record URL. |
| Column name | Header (CSV, XLSX) or field (JSON objects) with the numbers, case-insensitive; 2 means the second column. Empty is fine for one-column files and JSON arrays of strings. |
| Sheet name | XLSX sheet to read; default the first sheet. |
| Default country | ISO code (GB, US, DE, ...) for numbers without +. Without it, such numbers return INVALID_COUNTRY. Numbers with + ignore it. Prefilled with GB; it is not applied unless you send it. |
| International / National / RFC 3966 | Switch each formatted field on or off (all on by default). e164 is always present. |
| Output invalid numbers | On (default): invalid numbers get a (charged) row with the reason. Off: they are left out and not charged. |
| Remove duplicates | Keep the first row per E.164 number (020 7946 0958 and +44 20 7946 0958 match). Dropped duplicates are free. |
| Max numbers | Stop after N non-blank numbers. Hard limit 1,000,000 per run (larger values are lowered with a warning). |
Example inputs
1. A pasted list, UK default:
{"phoneNumbers": ["020 7946 0958", "+1 202-555-0143", "07700 900123", "12345"],"defaultCountry": "GB"}
2. A CSV export, de-duplicated, valid numbers only:
{"fileUrls": ["https://example.com/contacts.csv"],"column": "Mobile","defaultCountry": "US","dedupe": true,"outputInvalid": false}
3. An XLSX sheet, E.164 only:
{"fileUrls": ["https://example.com/leads.xlsx"],"sheetName": "Leads","column": "phone","includeInternational": false,"includeNational": false,"includeRfc3966": false}
Output
One dataset row per number, in input order (typed list first, then each file):
{"inputIndex": 0,"source": "list","input": "+44 20 7946 0958","valid": true,"possible": true,"e164": "+442079460958","international": "+44 20 7946 0958","national": "020 7946 0958","rfc3966": "tel:+442079460958","country": "GB","countryName": "United Kingdom","callingCode": "44","type": "FIXED_LINE","extension": null,"error": null,"errorMessage": null}
An invalid number keeps whatever could be parsed:
{"inputIndex": 4,"source": "list","input": "12345","valid": false,"possible": false,"e164": "+4412345","country": "GB","type": null,"error": "TOO_SHORT","errorMessage": "The number has too few digits for its country."}
valid= inside an allocated number-plan range.possible= only the length is plausible (weaker).typeis set for valid numbers:MOBILE,FIXED_LINE,FIXED_LINE_OR_MOBILE,TOLL_FREE,PREMIUM_RATE,SHARED_COST,VOIP,PERSONAL_NUMBER,PAGER,UAN,VOICEMAIL, ornullwhen the plan does not say. Numbers in shared plans (for example +44 mobile prefixes of Guernsey or Jersey) report the territory incountry.- For an invalid number,
countryis the default country when one applied, or empty. - An extension in the input (
ext. 12) goes toextension;e164never contains it. warningandwarningMessageappear only on a number read from an Excel cell that holds a number rather than text: see Numbers in Excel files.- Rows with
valid: nullare free rows (never charged): a file that could not be read, a cell that is too large to be a number, or a row the dataset refused. They carryerror,errorMessageand arunId(the id of the run that wrote them). inputIndexcounts non-blank numbers. The run also saves aRUN_SUMMARYrecord: counts of valid and invalid numbers, reasons, duplicates dropped, files read and failed, rows charged and why the run stopped.
Reason codes
error holds one of these codes whenever a row is not a valid number. The same codes are listed, with an enum, in the dataset schema.
Number reasons (the row is charged: you got an answer):
| Code | Meaning |
|---|---|
NOT_A_NUMBER | The text does not look like a phone number. |
INVALID_COUNTRY | No country could be determined: add + and the country code, or set a default country. |
INVALID_COUNTRY_CODE | The country calling code after the + does not exist. |
TOO_SHORT | The number has too few digits for its country. |
TOO_LONG | The number has too many digits for its country (text over 250 characters always gets this). |
INVALID_LENGTH | The number has a length that does not exist in its country. |
INVALID_NUMBER | The length fits but no number-plan range of the country matches. |
Free rows (valid: null, never charged; input is null, so nothing you supplied is echoed back):
| Code | Meaning |
|---|---|
INPUT_TOO_LARGE | A cell longer than 1,000 characters. It cannot be a phone number and is not read. inputIndex and source tell you where it was. After 1,000 of these the run stops (stoppedBy: "unpaid-work-cap"): check the column name. |
ROW_REJECTED | The dataset refused the row (for example because it was too large), so it was left out. |
FILE_HTTP | The server did not answer with HTTP 200. |
FILE_BLOCKED | The address is private, local or otherwise refused (also after a redirect). |
FILE_PLATFORM | A Google, video or social-platform link: upload the file instead. |
FILE_TOO_LARGE | The file is over 50 MB. |
FILE_TIMEOUT | The download timed out or stalled. |
FILE_DNS | The host name could not be resolved. |
FILE_NETWORK | The connection failed. |
FILE_REDIRECTS | More than 5 redirects. |
FILE_INVALID_URL | Not a valid http(s) URL. |
FILE_DOWNLOAD | The file could not be downloaded for another reason. |
FILE_NOT_DATA | The link returned a web page (HTML), a PDF, an image or another file that is not CSV, JSON or XLSX, for example a Google Sheets, Dropbox or OneDrive share link. Use a direct download link, or upload the file. |
FILE_COLUMN_NOT_FOUND | The column or field name was not found in the first row. |
FILE_COLUMN_REQUIRED | The file has several columns (or JSON objects): set "Column name". |
FILE_SHEET_NOT_FOUND | The sheet name was not found in the workbook. |
FILE_BAD_FILE | The file is damaged or could not be read as CSV, JSON or XLSX. |
FILE_TOO_LARGE_IN_MEMORY | A JSON or XLSX file too big for this run's memory: raise the memory or use CSV. |
FILE_SKIPPED | Not read, because five earlier files in the run had already failed. |
If every file fails and there are no typed numbers, the run fails with a message.
Numbers in Excel files
Excel stores a number typed into a cell as a number, not as text: it drops a leading zero (020 7946 0958 typed without a + becomes 2079460958) and keeps only 15 significant digits. The Actor reads the cell's raw value, writes exponent forms such as 4.42079460958E+11 out as plain digits, and restores leading zeros when the cell's number format pads them (a format such as 00000000000). When it cannot know whether something was lost, the row carries a warning:
warning | Meaning |
|---|---|
EXCEL_NUMERIC_CELL | The cell was a number, so a leading zero may have been dropped. Format the column as Text, or add the country code. |
EXCEL_PRECISION_LOST | The cell holds more than 15 significant digits, which Excel rounds: the last digits are not reliable. |
The number is still validated and charged as usual; the warning is information. Text cells never carry one. RUN_SUMMARY.excelWarnings counts them.
Restarts and migrations
Apify may move a run to another server. The Actor saves rows in batches of 500 (and at least every 30 seconds, and when it is told to migrate or abort), charges after each batch is saved, and then records its progress. A restarted run reads that record and the dataset, skips what is already saved and never saves or charges a row twice. It reads the input again from the start to find its place (files are downloaded again, not charged), so a very large file can add a little time after a migration. If an input file has changed or become unreachable since the first attempt, the run stops with a message instead of guessing, and says nothing was charged twice.
Pricing
Pay per event: number-validated = $0.0003 per output row (valid or invalid).
| Numbers | Cost |
|---|---|
| 100 | $0.03 |
| 1,000 | $0.30 |
| 10,000 | $3.00 |
| 100,000 | $30.00 |
| 1,000,000 | $300.00 |
Not charged: blank lines, duplicates removed by "Remove duplicates", invalid numbers when "Output invalid numbers" is off, and every free row (file problems and refused web pages, cells over 1,000 characters, rows the dataset refused). Set Apify's Max total charge to cap a run; the Actor stops cleanly when it is reached (stoppedBy: "max-charge" in RUN_SUMMARY). Standard Apify platform costs for the run itself are covered by this price.
Limits
- 1,000,000 numbers per run; 20 files per run, 50 MB each; downloads time out after 120 s by default (up to 600 s).
- CSV files are streamed. JSON and XLSX files are held in memory, so very large ones need more memory: at the default 512 MB, JSON up to about 30 MB and XLSX up to about 70 MB of sheet data are read; the run reports
FILE_TOO_LARGE_IN_MEMORYotherwise. Raise the run memory or use CSV. - Runs need at least 256 MB. For a million numbers with de-duplication, or files over about 20 MB, use 1,024 MB.
- After five failed files the remaining files are skipped, and after 1,000 cells over 1,000 characters the run stops: both are free but not unlimited.
- A CSV record (one row) may be up to 16 MB; an Excel row up to 16,384 columns.
- Numbers must be one per cell or line. Text around a number ("call 020 7946 0958 please") is not searched for numbers.
- Numbering plans change. Results reflect the rules in the bundled library version (
libphonenumber-js1.13.15, "max" metadata).
Privacy and your responsibilities
In short:
- Only your own input is processed: the numbers you type in, or a file you point the Actor to. It never searches for or collects numbers anywhere else.
- No lookups about any number: validation runs offline (number-plan ranges and format). There is no carrier, HLR, owner, region or time-zone lookup, and no number is ever sent over the network.
- Nothing is kept beyond the run: results go to your run's dataset; the run also writes
RUN_SUMMARYandPROGRESSrecords with counts only. Numbers are never logged.
- Phone numbers are personal data. You supply them and you are the data controller under UK GDPR / GDPR; you are responsible for having a lawful basis, for telling the people concerned, and for lawful use of the results, for example PECR (UK) and TCPA (US) rules for calling or texting people, and any do-not-call registers. Validation does not give you consent to contact anyone.
- The Actor never logs phone numbers (logs contain counts only), makes no network requests about them (the only network access is optionally downloading a file you point it to), and stores nothing beyond this run's dataset, the
RUN_SUMMARYrecord (counts, no numbers) and aPROGRESSrecord (counts, a hash of the input and a one-line outcome per file, no numbers) that lets a restarted run resume. Files are read once in memory and not kept. You control retention of the dataset through your Apify settings. - File URLs are fetched with an SSRF guard: only
http(s)to public addresses, re-checked on every redirect (at most 5), size and time capped. The Actor does not download from Google, YouTube or social platforms: upload those files instead. You are responsible for having the right to use your files. - Key-value-store record links (
api.apify.com/v2/key-value-stores/…, e.g. uploads) are read with the run's own token. The token is only ever sent to Apify's API, never to any other host.
Use it via API, n8n or Make
cURL (synchronous, returns the rows):
curl -X POST "https://api.apify.com/v2/acts/ataism~phone-validator/run-sync-get-dataset-items?token=$APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"phoneNumbers": ["020 7946 0958", "+1 202-555-0143"], "defaultCountry": "GB"}'
JavaScript (apify-client):
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('ataism/phone-validator').call({phoneNumbers: ['020 7946 0958', '+1 202-555-0143'],defaultCountry: 'GB',});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items.map((i) => [i.e164, i.valid, i.type]));
Python (apify-client):
from apify_client import ApifyClientclient = ApifyClient(token="APIFY_TOKEN")run = client.actor("ataism/phone-validator").call(run_input={"phoneNumbers": ["020 7946 0958", "+1 202-555-0143"],"defaultCountry": "GB",})items = client.dataset(run["defaultDatasetId"]).list_items().itemsprint([(i["e164"], i["valid"], i["type"]) for i in items])
n8n: add the Apify node, choose Run an Actor and get dataset, set the Actor to ataism/phone-validator and the input JSON as above.
Make: use the Apify modules Run an Actor (Actor ataism/phone-validator, input JSON as above), then Get Dataset Items with the run's default dataset ID.
FAQ
Does it tell me if a number is live or who owns it? No. It validates format and number-plan ranges offline. For connectivity you need a carrier/HLR service, which this Actor does not call.
Why is +44 7700 900123 invalid? It sits in a range that Ofcom reserves for TV and radio drama, which is deliberately not a real allocation. It has a possible length (possible: true) but no matching range (INVALID_NUMBER).
Why does type say FIXED_LINE_OR_MOBILE? In some plans (the US and Canada, for example) the number plan does not distinguish landlines from mobiles.
Open-source components
libphonenumber-js (MIT; its metadata derives from Google's libphonenumber, Apache-2.0), csv-parse (MIT), fflate (MIT), Apify SDK (Apache-2.0).