# Phone Number Validator - Bulk Format, Type & Carrier (`dami_studio/phone-number-validator`) Actor

Check phone numbers in bulk: valid or not and why, mobile or landline, country, and the number in E.164, international and national form. Valid numbers also get region, original carrier and time zones. Paste a list or check a field of your dataset. Offline: nobody is called.

- **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
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.26 / 1,000 number checkeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Phone Number Validator

Paste phone numbers, or point it at a dataset of leads, and get one row for each number. The row tells you whether the number is valid and, if not, why. It gives the number in E.164, international and national form, the country, and what kind of line it is: mobile, landline, toll-free, VoIP and so on. Valid numbers also get the place their number range belongs to, the carrier the range was first given to, and the time zones it covers.

Everything happens inside the run. No number is called, texted or looked up on a phone network, and nobody on your list notices anything.

### What it doesn't do

- **It can't tell you whether a number is switched on right now.** Valid means the number fits a range the country actually uses. It may still be disconnected, or never handed out. Only a live lookup through a phone network can tell that, and this doesn't do one.
- **It doesn't say who owns a number.** No names, no addresses.
- **The carrier is the original one.** People keep their number when they change carrier, so for a moved number the carrier column shows the old one.
- **In the US and Canada, mobiles and landlines share the same ranges,** so numbers there come back as "landline or mobile". Most other countries keep the two apart.
- **No WhatsApp or Telegram check.**

### Input

**Phone numbers.** One per line, written any way you like: `+44 20 7946 0018`, `(201) 555-0123`, `0033 6 12 34 56 78`, `tel:+81-3-1234-5678`. An extension such as `ext. 12` goes into its own column.

**Default country.** A local number, one without `+` or `00` in front, only means something with a country. `(201) 555-0123` checks out as a US number when this is set to United States, and fails with most other countries. Pick the country your list mostly comes from. Numbers that start with `+` or `00` carry their own country and ignore this setting.

**Dataset and field.** To check the results of another actor, a business-listing scraper say, pick its dataset (or pass the ID as `datasetId` through the API) and give the name of the field that holds the number (`phone` unless you change it). The actor can read only the dataset you pick, and it never writes to it. Nested fields work with dots, such as `contact.phone`, and a field holding a list checks every number in it. A dataset and a pasted list can go in the same run.

**Add region, carrier and time zones.** On by default. Switch it off if you only want the check and the formats.

**Language for region and carrier names.** English by default, with 32 other languages. A name that doesn't exist in the chosen language comes in English.

**Filters.** Three optional ones. *Only valid numbers* drops the ones that fail the check. *Only these line types* keeps, say, just mobiles; it leaves out invalid numbers too, since those have no type. *Only these countries* keeps numbers from the countries you pick. A number the filters leave out isn't returned and isn't charged. In the US and Canada most numbers come back as "landline or mobile", so when you filter for mobiles there, pick that type as well.

**Maximum numbers.** 10,000 unless you change it, and up to 500,000 in one run. Only distinct, non-blank numbers that pass your filters count.

### Output

One row per distinct number, in the order they came in.

| Field | Example | Notes |
|---|---|---|
| `input` | `(201) 555-0123` | as you wrote it |
| `valid` | `true` | |
| `possible` | `true` | the length is right for the country, even when the number itself isn't in use |
| `reason` | `wrong length for France` | why it isn't valid; empty when it is |
| `e164` | `+12015550123` | the form most systems want |
| `international` | `+1 201 555 0123` | |
| `national` | `(201) 555-0123` | how people dial it inside the country |
| `rfc3966` | `tel:+12015550123` | the form links and contact cards use |
| `extension` | `12` | when the entry had one |
| `countryCode` | `US` | two-letter code |
| `country` | `United States` | |
| `callingCode` | `+1` | |
| `numberType` | `landline or mobile` | one of: mobile, landline, landline or mobile, toll-free, premium rate, shared cost, VoIP, personal number, pager, UAN, voicemail |
| `smsCapable` | `true` | `true` for mobiles, `false` for landlines and special numbers, empty when the numbering plan can't tell (landline or mobile, VoIP) |
| `region` | `Mountain View, CA` | where the number range belongs |
| `carrier` | `Three` | the carrier the range was first given to |
| `timeZones` | `["America/Los_Angeles"]` | |
| `source`, `sourceIndex` | `list`, `2` | the line of your list, or the position of the item in your dataset |

For a landline, `region` is usually a town or city. Many countries don't tie mobile ranges to a place, so for mobiles it is often empty. A number that isn't tied to one place, such as a US toll-free number, lists every time zone in its country.

When a number isn't valid, `reason` says why in plain words: `too short`, `wrong length for France`, `not a number in use in Germany`, `unknown country code` or `no country code: start it with + or set a default country`. The formatted columns stay empty for those, so an invalid number can't slip into a column you import somewhere else.

The run also writes a report, `RUN_REPORT` in the key-value store, with counts by verdict, reason, type and country, and a note of what was skipped.

### What you pay

Each distinct number checked is one charge, valid or not, because the verdict is what you asked for. Blank entries and repeats of a number already checked in the same run are skipped and never charged. `020 7946 0018` with United Kingdom set and `+44 20 7946 0018` count as the same number. If you set a maximum charge for the run, it stops when that is reached, and every row you get has been paid for. The price itself is on the Pricing tab.

### Where the rules come from

Validity, line types and formats come from libphonenumber-js, which follows Google's libphonenumber numbering plans; this build uses version 1.13.13, released in September 2026. Region, carrier and time-zone names come from the tables in libphonenumber-geo-carrier 2.0.0, built from the same Google data. Countries open new number ranges from time to time, so a range that went live after this build can show up as "not a number in use" until the actor is updated.

### Questions

**Why is a number I know works marked "not a number in use"?** Either its range is newer than the data, or it was read with the wrong country. Check the default country, or write the number with `+` and its country code.

**Does it change my dataset?** No. It only reads the field you name, and writes its own results to its own dataset.

**Can it tell me which numbers take text messages?** As far as the numbering plan goes, yes: `smsCapable` is `true` for mobiles and `false` for landlines and special numbers. It's empty where the plan can't tell, as with "landline or mobile" numbers in the US and Canada and with VoIP. Whether a mobile is switched on right now needs the live lookup this actor doesn't do.

# 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/pIVE0QX4up7v5jAGV/openapi.json
