Request a tool
All toolsAutomationsGuidesMCP serverRequest a toolPlatformsCategories
Phone Number Validator icon

Phone Number Validator

Validate phone numbers in bulk, offline. Get E.164 and national formats, country, mobile or landline, region, original carrier and time zones for each number.

81 runs on Apify $0.00026 per number checked ($0.26 / 1,000)
Run this in the cloudRun on Apify →

Developer & Research Tools

How it works

  1. 1
    Open it on Apify

    Hit Run on Apify — it opens the tool in the cloud, no install.

  2. 2
    Set the inputs

    Adjust phoneNumbers, defaultCountry, datasetId (sensible defaults are pre-filled).

  3. 3
    Click Run

    The tool runs on Apify’s cloud and collects the data for you.

  4. 4
    Export the results

    Download as JSON, CSV or Excel, or pipe straight into your app, Google Sheets, or an AI agent.

Pricing

$0.00026 per number checked = $0.26 per 1,000

You are charged forWhenPrice
Number checkedOne distinct phone number checked and returned in the dataset, valid or not. Blank entries and repeats in the same run are never charged.$0.00026

Pay-per-event pricing: you are billed per result, not per subscription. Billing is handled by Apify on your own account. These are the live Apify store prices, in effect since 2026-09-14, and they are what you are actually charged.

Inputs

FieldWhat it doesType
phoneNumbersOne number per line, written any way: +44 20 7946 0018, (201) 555-0123, 0033 6 12 34 56 78 or tel:+81-3-1234-5678. Numbers without + or 00 in front need the default country below. Leave this empty to check a dataset instead.array
defaultCountryThe country for numbers written without + or 00, the way people write local numbers. (201) 555-0123 checks out as a US number with United States here and fails with most other countries. Numbers that start with + or 00 carry their own country and ignore this.string
datasetIdCheck the numbers in one of your Apify datasets, for example the results of a business-listing or lead scraper. Pick it here, or pass its ID as datasetId through the API. Works together with the list above.string
datasetFieldThe field in each dataset item that holds the number: phone unless you change it. Use dots for a nested field (contact.phone). A field holding a list checks every number in it.string
addRegionAndCarrierFor valid numbers, add the place the number range belongs to (for landlines usually a town or city), the carrier the range was first given to, and its time zones. Switch it off for just the check and the formats.boolean
languageNames that don't exist in the chosen language come in English.string
onlyValidReturn only the numbers that pass the check. The others are left out and aren't charged.boolean
numberTypesReturn only valid numbers of these types, mobile for example. Leave it empty for every type. Numbers left out aren't charged. In the US and Canada most numbers come back as "landline or mobile", so pick that too for those.array
countriesReturn only numbers that belong to these countries. Leave it empty for every country. Numbers left out aren't charged.array
maxNumbersThe most numbers checked in one run. Only distinct, non-blank entries count, and each one checked is charged.integer

What you get

A structured dataset — each result includes fields like:

inputvalidreasone164nationalcountrynumberTypesmsCapableregioncarriertimeZones

Export every run as JSON, CSV or Excel, or send it to your app, a database, Google Sheets, or an AI agent.

Related tools in Developer & Research Tools

Other ready-to-run tools in the same category — all pay-per-use on the Apify cloud.

GitHub Scraper iconDeveloper & Research Tools

GitHub Scraper

Search GitHub repos and users: stars, forks, language, topics, licence, plus user bio, company and followers. No token needed. $0.90 per 1,000 rows.

18 use cases

Stack Overflow / Stack Exchange Scraper iconDeveloper & Research Tools

Stack Overflow / Stack Exchange Scraper

Search Stack Overflow and Stack Exchange by keyword or tag. Score, answer count, views, reputation and body text. $2 per 1,000 questions.

2 use cases

Package Registry Scraper (npm + PyPI) iconDeveloper & Research Tools

Package Registry Scraper (npm + PyPI)

Get npm and PyPI package metadata as JSON. Version, license, author, repo, keywords and npm monthly downloads. $2 per 1,000 packages.

2 use cases

arXiv Scraper iconDeveloper & Research Tools

arXiv Scraper

Search arXiv papers by title, author, abstract or category. Get full abstracts, authors, categories, DOI, dates and PDF links. $2 per 1,000 papers.

2 use cases

OpenAlex Scholarly Works Scraper iconDeveloper & Research Tools

OpenAlex Scholarly Works Scraper

Search 250M+ OpenAlex papers with no API key. Get titles, authors, venue, year, citations, DOI, OA links and full abstracts. $2.00 per 1,000 papers.

2 use cases

Crossref Scholarly Works Scraper iconDeveloper & Research Tools

Crossref Scholarly Works Scraper

Search 150M+ papers on Crossref: DOI, title, authors, journal, publisher, date, citations and abstract. No API key. $1.00 per 1,000 works.

2 use cases

See all Developer & Research Tools →

Phone Number Validator: bulk check, format, line type and carrier, offline

Paste a list of numbers, or point it at a field in one of your own Apify datasets. Every distinct number comes back as a row: valid or not and why not, the E.164, international, national and tel: forms, the country, the calling code, and whether it is a mobile or a landline.

Nobody is called and nothing is texted. This is a check against the world's numbering plans, so it tells you whether a number can be right at all, not whether it is switched on today.

InputA list of numbers, a dataset field, or both
OutputOne row per distinct number
Ceiling500,000 numbers per run
Account neededNone
Price$0.26 per 1,000 numbers checked, flat on every plan

📞 What Phone Number Validator does

Write the numbers however you have them. +44 20 7946 0018, (201) 555-0123, 0033 6 12 34 56 78 and tel:+81-3-1234-5678 all parse. A leading 00 becomes +, a tel: prefix is dropped, and spaces, brackets and dashes do not matter.

A number without + or 00 has no country in it, so the run uses your Default country to read it. (201) 555-0123 is a valid United States number and is nonsense as a French one, which is the whole reason that field exists.

For the numbers that pass, you also get the region the range belongs to, which for a landline is usually a town or city, the carrier the range was first handed to, and the time zones it covers. Region and carrier names come back in any of 33 languages.

The datasetId field is the part that saves real work. Point it at the output of a business-listing or lead scraper, name the field that holds the number, and the two run back to back without you exporting anything.

📥 What you give it

{
  "phoneNumbers": ["+44 20 7946 0018", "(201) 555-0123", "06 12 34 56 78"],
  "defaultCountry": "US",
  "onlyValid": true,
  "numberTypes": ["mobile"],
  "maxNumbers": 5000
}
FieldDefaultWhat it is
phoneNumbersnoneOne per line, up to 500,000. Leave it empty to check a dataset instead.
defaultCountrynoneTwo-letter code, from a list of 245. Used only for numbers written without + or 00.
datasetIdnoneOne of your own datasets, picked from a list in the form or passed as an ID through the API. Works alongside the list.
datasetFieldphoneWhich field holds the number. Dots reach a nested one, like contact.phone. A field holding a list checks every number in it.
addRegionAndCarriertrueAdds region, carrier and time zones to valid numbers. Off leaves those three empty.
languageenFor region and carrier names, from 33 languages. Names missing in your language come back in English.
onlyValidfalseLeave out numbers that fail the check.
numberTypesnoneKeep only these line types: mobile, landline, landline or mobile, toll-free, VoIP and six more.
countriesnoneKeep only numbers belonging to these countries.
maxNumbers100001 to 500,000. Only distinct numbers that get past your filters count towards it.

Anything a filter leaves out is not charged, and neither are blanks or repeats. So onlyValid: true on a list that is half rubbish costs you roughly half.

In the United States and Canada, pick landline or mobile too if you are filtering by line type. Those two countries share number ranges between mobiles and landlines, so most numbers there honestly cannot be told apart and come back under that label.

📤 What you get back

A real row, from run fV91ws4Xze6pNDOfZ:

{
  "input": "+44 20 7946 0018",
  "valid": true,
  "possible": true,
  "reason": null,
  "e164": "+442079460018",
  "international": "+44 20 7946 0018",
  "national": "020 7946 0018",
  "rfc3966": "tel:+442079460018",
  "extension": null,
  "countryCode": "GB",
  "country": "United Kingdom",
  "callingCode": "+44",
  "numberType": "landline",
  "smsCapable": false,
  "region": "London",
  "carrier": null,
  "timeZones": ["Europe/London"],
  "source": "list",
  "sourceIndex": 1
}

carrier is empty on that row because it is a landline: carrier data covers mobile ranges.

FieldWhat it is
validThe number fits its country's numbering plan. This is the one to filter on.
possibleA length check only, and it is set for anything that parses at all. A number can be possible and not valid.
reasonWhy a number failed, in plain words: "too short", "wrong length for France", "not a number in use in Germany", "unknown country code", "no country code: start it with + or set a default country". null when it passed.
e164The clean +441234567890 form. Store this one.
smsCapabletrue for mobiles, false for landlines and other fixed types, and null where the plan genuinely cannot say, which includes "landline or mobile" and VoIP.
region, carrier, timeZonesValid numbers only, and only with addRegionAndCarrier on.
source, sourceIndexlist with the line number, or dataset with the item's offset, so you can join the row back to where it came from.

Formats, region, carrier and time zones are filled in only for numbers that pass. An invalid number gets its verdict, its reason and nothing invented around it.

🧾 Reading the output

One row per distinct number, and that is the whole dataset. No refusal rows and no blank lines.

A sample row carries _sample: true and turns up only when you run with nothing to check.

Everything else about the run is in a key-value record called RUN_REPORT:

In the reportWhat it tells you
Counts by verdict, reason, type and countryThe shape of your list in one place.
Entries skippedHow many were blank, how many were repeats, how many your filters removed.
The filters usedWritten out, including any value that was not recognised and dropped.
The dataset readItems seen, items with no number in the named field, and any error reading it.
Stop reason and seconds takenWhether it finished the list or hit maxNumbers, the deadline or your budget.

A run whose filters match nothing finishes normally and says so in the status message. A run with nothing to check at all fails.

▶️ How to run it

1. Open Phone Number Validator and click Try for free. 2. Paste numbers into Phone numbers, or pick a dataset under Dataset and name the field. 3. Set Default country if your list has local numbers in it. 4. Add filters under Only valid numbers, Only these line types or Only these countries. 5. Click Start, then download the dataset as JSON, CSV or Excel, or read it from the Apify API.

💰 How much does it cost?

$0.26 per 1,000 numbers checked. The same rate on every Apify plan, with no volume tiers.

One charge per row delivered. Blanks, repeats of a number already in the run, and anything your filters remove are not charged, and neither is the sample row. Region, carrier and time zones cost nothing extra.

💡 What people use it for

  • Cleaning a scraped lead list down to the numbers that can actually be dialled.
  • Splitting a list into mobiles and landlines before a texting campaign.
  • Normalising a CRM export to E.164 so two systems finally agree on a format.
  • Working out which countries a list is really from, by counting countryCode.
  • Cutting a scraper's output straight into a clean sheet, using datasetId instead of a CSV.

🚧 What it does not do

  • It does not call or text anything. No answer test, no ring test, no HLR lookup. Nothing tells

you whether a number is live, disconnected or answered today.

  • No names, no owners, no WhatsApp check.
  • carrier is the carrier the range was first given to. A ported number shows the old one. Take

it as a hint about the range, not as fact about the handset.

  • United States and Canada numbers come back as "landline or mobile" most of the time, because

the ranges are genuinely shared. smsCapable is null for those.

  • The numbering data has a date. A range that opened after the last update reads as not in use.
  • A number not tied to one place lists every time zone of its country. A US toll-free number

names 42 of them.

  • rfc3966 has no separators, so tel:+442079460018. That is how the standard form is written.
  • 500,000 numbers in a run, and a large run wants the memory to match.

🧭 Which list-cleaning tool do you need?

If you wantUse
Phone numbers checked and formattedThis one
Email addresses checkedEmail Verification
Phones and emails pulled off a list of websitesContact Details Scraper
Business listings with phone numbers to cleanGoogle Maps Scraper
Someone's social profiles from a name or domainSocial Profile Finder

❓ Questions people ask

Does it ring the number? No. Everything is worked out from the numbering plans, so nobody's phone lights up and nothing leaves the run.

What is the difference between valid and possible? possible says the length could be right. valid says the whole number matches a range that country actually uses. Filter on valid.

Why did a number I know is fine come back invalid? Nearly always a missing country. Set Default country to match where the local numbers were written, or put them in + form.

Can I check the numbers from another actor's run? Yes. Pick that run's dataset under Dataset and set the field name. Nothing needs exporting.

Is the carrier current? No. It is the carrier the range was originally allocated to. Porting is invisible to an offline check.

Do I pay for numbers my filters removed? No. Only rows that reach the dataset are charged.

🆘 If something breaks

Open the Issues tab on the actor page. Send the run ID and a number that came back wrong. The RUN_REPORT record in the run's key-value store carries the counts, the filters and the stop reason, which usually explains a short result on its own.