Phone Number Validator & Formatter: E.164, Type, Country avatar

Phone Number Validator & Formatter: E.164, Type, Country

Pricing

from $0.30 / 1,000 phone number validateds

Go to Apify Store
Phone Number Validator & Formatter: E.164, Type, Country

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

Kaizoku Data

Maintained by Community

Actor 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_MOBILE where 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. country comes from the calling code and the number plan only (for example GB, US); type says mobile, fixed line, VoIP and so on, but never which operator or where. Those need data this Actor does not have.

Input

FieldWhat it does
Phone numbersOne 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 URLsDirect 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 filesUpload from your computer to a key-value store (the field offers this) or paste a record URL.
Column nameHeader (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 nameXLSX sheet to read; default the first sheet.
Default countryISO 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 3966Switch each formatted field on or off (all on by default). e164 is always present.
Output invalid numbersOn (default): invalid numbers get a (charged) row with the reason. Off: they are left out and not charged.
Remove duplicatesKeep the first row per E.164 number (020 7946 0958 and +44 20 7946 0958 match). Dropped duplicates are free.
Max numbersStop 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).
  • type is set for valid numbers: MOBILE, FIXED_LINE, FIXED_LINE_OR_MOBILE, TOLL_FREE, PREMIUM_RATE, SHARED_COST, VOIP, PERSONAL_NUMBER, PAGER, UAN, VOICEMAIL, or null when the plan does not say. Numbers in shared plans (for example +44 mobile prefixes of Guernsey or Jersey) report the territory in country.
  • For an invalid number, country is the default country when one applied, or empty.
  • An extension in the input (ext. 12) goes to extension; e164 never contains it.
  • warning and warningMessage appear only on a number read from an Excel cell that holds a number rather than text: see Numbers in Excel files.
  • Rows with valid: null are 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 carry error, errorMessage and a runId (the id of the run that wrote them).
  • inputIndex counts non-blank numbers. The run also saves a RUN_SUMMARY record: 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):

CodeMeaning
NOT_A_NUMBERThe text does not look like a phone number.
INVALID_COUNTRYNo country could be determined: add + and the country code, or set a default country.
INVALID_COUNTRY_CODEThe country calling code after the + does not exist.
TOO_SHORTThe number has too few digits for its country.
TOO_LONGThe number has too many digits for its country (text over 250 characters always gets this).
INVALID_LENGTHThe number has a length that does not exist in its country.
INVALID_NUMBERThe 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):

CodeMeaning
INPUT_TOO_LARGEA 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_REJECTEDThe dataset refused the row (for example because it was too large), so it was left out.
FILE_HTTPThe server did not answer with HTTP 200.
FILE_BLOCKEDThe address is private, local or otherwise refused (also after a redirect).
FILE_PLATFORMA Google, video or social-platform link: upload the file instead.
FILE_TOO_LARGEThe file is over 50 MB.
FILE_TIMEOUTThe download timed out or stalled.
FILE_DNSThe host name could not be resolved.
FILE_NETWORKThe connection failed.
FILE_REDIRECTSMore than 5 redirects.
FILE_INVALID_URLNot a valid http(s) URL.
FILE_DOWNLOADThe file could not be downloaded for another reason.
FILE_NOT_DATAThe 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_FOUNDThe column or field name was not found in the first row.
FILE_COLUMN_REQUIREDThe file has several columns (or JSON objects): set "Column name".
FILE_SHEET_NOT_FOUNDThe sheet name was not found in the workbook.
FILE_BAD_FILEThe file is damaged or could not be read as CSV, JSON or XLSX.
FILE_TOO_LARGE_IN_MEMORYA JSON or XLSX file too big for this run's memory: raise the memory or use CSV.
FILE_SKIPPEDNot 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:

warningMeaning
EXCEL_NUMERIC_CELLThe cell was a number, so a leading zero may have been dropped. Format the column as Text, or add the country code.
EXCEL_PRECISION_LOSTThe 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).

NumbersCost
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_MEMORY otherwise. 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-js 1.13.15, "max" metadata).

Privacy and your responsibilities

In short:

  1. 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.
  2. 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.
  3. Nothing is kept beyond the run: results go to your run's dataset; the run also writes RUN_SUMMARY and PROGRESS records 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_SUMMARY record (counts, no numbers) and a PROGRESS record (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 ApifyClient
client = 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().items
print([(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).