# Phone Number Validator (`dami_studio/phone-number-validator`) Actor

Phone number validator for bulk lists: valid or not and why, line type (mobile or landline), country, and E.164, international and national forms. Valid numbers also get region, original carrier and time zones. Paste a list or check a dataset field. Nobody is called. $0.26 per 1,000 numbers.

- **URL**: https://apify.com/dami_studio/phone-number-validator.md
- **Developed by:** [Dami's Studio](https://apify.com/dami_studio) (community)
- **Categories:** Lead generation, Developer tools, Automation
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.25 / 1,000 numbers

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

**Phone number validator for bulk lists, with valid or not, line type, carrier and clean E.164 format**: 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.

| | |
|---|---|
| **Input** | A list of numbers, a field in one of your datasets, or both |
| **Output** | One row per distinct number: verdict and reason, E.164 and other formats, country, line type, region, carrier, time zones |
| **Ceiling** | 500,000 numbers per run |
| **Speed** | A few seconds for a short list; 100,000 numbers took 93 to 139 seconds on measured runs |
| **Account needed** | None |
| **Price** | $0.26 per 1,000 numbers checked on the Free plan, a little less on paid plans. The free plan's $5 a month covers about 19,000 |

### 🔍 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
scraper, name the field that holds the number, and the two run back to back without you
exporting anything.

### 📋 What data you get from each phone number

| What you get | Field |
|---|---|
| Whether it passes, and why not when it does not | `valid`, `possible`, `reason` |
| The number in E.164, international, national and `tel:` form, plus any extension | `e164`, `international`, `national`, `rfc3966`, `extension` |
| The country, its code and the calling code | `country`, `countryCode`, `callingCode` |
| Mobile, landline and the other line types, and whether it takes texts | `numberType`, `smsCapable` |
| The place the range belongs to, its original carrier and its time zones | `region`, `carrier`, `timeZones` |
| What you sent, and where it came from in your list or dataset | `input`, `source`, `sourceIndex` |

### ▶️ How to validate phone numbers in bulk

1. Open [Phone Number Validator](https://apify.com/dami_studio/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.

Start it with nothing to check and you get one free sample row showing the output shape.

### 💰 How much does it cost to validate phone numbers?

**$0.26 per 1,000 numbers checked** on the Free plan. Paid plans pay a little less: $0.258 on Starter, $0.256 on Scale and $0.254 on Business. On the
free plan, the $5 Apify gives you each month covers about 19,000 numbers.

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 you give it

```json
{
  "phoneNumbers": ["+44 20 7946 0018", "(201) 555-0123", "06 12 34 56 78"],
  "defaultCountry": "US",
  "onlyValid": true,
  "numberTypes": ["mobile"],
  "maxNumbers": 5000
}
```

| Field | Default | What it is |
|---|---|---|
| `phoneNumbers` | none | One per line, up to 500,000. Leave it empty to check a dataset instead. The form starts with four examples, which are a prefill. |
| `defaultCountry` | none | Two-letter code, from a list of 245. Used only for numbers written without `+` or `00`. The form starts on `US`, which is a prefill, not a default. |
| `datasetId` | none | One of your own datasets, picked from a list in the form or passed as an ID through the API. Works alongside the list. |
| `datasetField` | `phone` | Which field holds the number. Dots reach a nested one, like `contact.phone`. A field holding a list checks every number in it. |
| `addRegionAndCarrier` | `true` | Adds region, carrier and time zones to valid numbers. Off leaves those three empty. |
| `language` | `en` | For region and carrier names, from 33 languages. Names missing in your language come back in English. |
| `onlyValid` | `false` | Leave out numbers that fail the check. |
| `numberTypes` | none | Keep only these line types: `mobile`, `landline`, `landline or mobile`, `toll-free`, `VoIP` and six more. |
| `countries` | none | Keep only numbers belonging to these countries. |
| `maxNumbers` | `10000` | 1 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. Numbers there can come back under that label.

### 📤 What you get back

Two real rows from a run on 3 October 2026 with the form's own example numbers: a London landline,
which gets a region, and a French mobile, which gets a carrier.

```json
[
  {
    "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
  },
  {
    "input": "+33 6 12 34 56 78",
    "valid": true,
    "possible": true,
    "reason": null,
    "e164": "+33612345678",
    "international": "+33 6 12 34 56 78",
    "national": "06 12 34 56 78",
    "rfc3966": "tel:+33612345678",
    "extension": null,
    "countryCode": "FR",
    "country": "France",
    "callingCode": "+33",
    "numberType": "mobile",
    "smsCapable": true,
    "region": null,
    "carrier": "SFR",
    "timeZones": ["Europe/Paris"],
    "source": "list",
    "sourceIndex": 3
  }
]
```

| Field | How to read it |
|---|---|
| `valid` | The number fits its country's numbering plan. This is the one to filter on. |
| `possible` | A length check only, set for anything that parses at all. A number can be `possible` and not `valid`. |
| `reason` | Why 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. |
| `carrier` | Filled for mobile ranges. A landline has a `region` instead. |
| `smsCapable` | `true` for mobiles, `false` for landlines and other fixed types, `null` where the plan cannot say, which includes "landline or mobile" and VoIP. |
| `sourceIndex` | The line number in your list, or the item's offset in your dataset, so you can join the row back. |

Formats, region, carrier and time zones are filled in **only for numbers that pass**. In the same
run, `06 12 34 56 78` read with the US default came back `valid: false` with "not a number in use in
United States", and nothing invented around it.

### 🧾 Reading the output

One row per distinct number, 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 report | What it tells you |
|---|---|
| Counts by verdict, reason, type and country | The shape of your list in one place. |
| Entries skipped | How many were blank, how many were repeats, how many your filters removed. |
| The filters used | Written out, including any value that was not recognised and dropped. |
| The dataset read | Items seen, items with no number in the named field, and any error reading it. |
| Stop reason and seconds taken | Whether 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. So does a
run with nothing to check: you get the one sample row, not charged.

| Note row (not charged) | What it means |
|---|---|
| `COST_LIMIT` | The maximum cost you set for the run does not cover a single result. Raise it and run again. The run still ends as succeeded. |
| `NOT_SERVED` | No numbers came back. The row's error says why, for example a dataset ID that could not be opened, or no numbers in the field you named. The run still ends as succeeded. |

### 💡 What people use it for

- Cleaning a list of numbers down to the ones that can actually be dialled.
- Splitting a list into mobiles and landlines.
- 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`.

From business listings to a checked list of numbers, in three runs:

1. Run [Yellow Pages US Scraper](https://apify.com/dami_studio/yellow-pages-us-scraper) for the
   businesses you want.
2. Here, pick that run's dataset under **Dataset**, leave the field as `phone`, set **Default
   country** to United States and tick **Only valid numbers**.
3. Join the rows back to the listings on `sourceIndex`, which is each listing's position in that
   dataset.

### 🚧 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 can come back as "landline or mobile".** `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.
- **500,000 numbers in a run**, and a large run wants the memory to match.
- It is not an official tool of any platform or company named on this page. Dami's Studio is independent and is not affiliated with or endorsed by any of them.

### 🧭 Which list-cleaning tool do you need?

| If you want | Use |
|---|---|
| Phone numbers checked and formatted | This one |
| Email addresses checked | [Email Verifier](https://apify.com/dami_studio/email-verification) |
| Phones and emails pulled off a list of websites | [Contact Details Scraper](https://apify.com/dami_studio/contact-details-scraper) |
| Business listings with phone numbers to clean | [Google Maps Scraper](https://apify.com/dami_studio/google-maps-scraper) |
| US business listings with phone numbers | [Yellow Pages US Scraper](https://apify.com/dami_studio/yellow-pages-us-scraper) |
| Social profiles from a name or domain | [Social Profile Finder](https://apify.com/dami_studio/social-profile-finder) |

### ❓ Questions people ask

#### Does it ring the phone number?

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

#### 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 call it from code or connect it to an AI assistant?

The [API tab](https://apify.com/dami_studio/phone-number-validator/api/python) has code for Python, JavaScript and the
CLI. MCP clients like Claude and ChatGPT use
`https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/phone-number-validator`; coding assistants can load [our
skill file](https://api.apify.com/v2/key-value-stores/BTBHU3DdiuoF4JOsK/records/apify-web-checks-monitors-SKILL.md).
All three bill your Apify account at the same price.

#### Is checking phone numbers legal?

Nothing is called or texted. A phone
number is still personal data under GDPR and similar laws, so have a reason for holding the list.
Apify's [write-up on scraping and the law](https://blog.apify.com/is-web-scraping-legal/) is a good
start, and we are not lawyers.

### 🆘 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.

# Actor input Schema

## `phoneNumbers` (type: `array`):

One 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.

## `defaultCountry` (type: `string`):

The 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.

## `datasetId` (type: `string`):

Check 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.

## `datasetField` (type: `string`):

The 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.

## `addRegionAndCarrier` (type: `boolean`):

For 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.

## `language` (type: `string`):

Names that don't exist in the chosen language come in English.

## `onlyValid` (type: `boolean`):

Return only the numbers that pass the check. The others are left out and aren't charged.

## `numberTypes` (type: `array`):

Return 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.

## `countries` (type: `array`):

Return only numbers that belong to these countries. Leave it empty for every country. Numbers left out aren't charged.

## `maxNumbers` (type: `integer`):

The most numbers checked in one run. Only distinct, non-blank entries count, and each one checked is charged.

## Actor input object example

```json
{
  "phoneNumbers": [
    "+44 20 7946 0018",
    "(201) 555-0123",
    "+33 6 12 34 56 78",
    "06 12 34 56 78"
  ],
  "defaultCountry": "US",
  "datasetField": "phone",
  "addRegionAndCarrier": true,
  "language": "en",
  "onlyValid": false,
  "maxNumbers": 10000
}
```

# Actor output Schema

## `results` (type: `string`):

One row per distinct number: valid or not and why, E.164, international and national formats, country, calling code and line type, and for valid numbers the region, original carrier and time zones.

## `report` (type: `string`):

Counts by verdict, reason, line type and country, what was skipped (blank entries, repeats) and why the run stopped.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "phoneNumbers": [
        "+44 20 7946 0018",
        "(201) 555-0123",
        "+33 6 12 34 56 78",
        "06 12 34 56 78"
    ],
    "defaultCountry": "US"
};

// Run the Actor and wait for it to finish
const run = await client.actor("dami_studio/phone-number-validator").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "phoneNumbers": [
        "+44 20 7946 0018",
        "(201) 555-0123",
        "+33 6 12 34 56 78",
        "06 12 34 56 78",
    ],
    "defaultCountry": "US",
}

# Run the Actor and wait for it to finish
run = client.actor("dami_studio/phone-number-validator").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "phoneNumbers": [
    "+44 20 7946 0018",
    "(201) 555-0123",
    "+33 6 12 34 56 78",
    "06 12 34 56 78"
  ],
  "defaultCountry": "US"
}' |
apify call dami_studio/phone-number-validator --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/phone-number-validator"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/byNY8AaLnWrmipxkG/builds/7DRXqiAVtRbd7QS9a/openapi.json
