# Phone Number Validator & Formatter (Bulk, Offline) (`maliksaid/phone-number-validator`) Actor

Bulk phone number validator and formatter for all countries. Returns valid and possible flags, E.164, international and national formats, country and line type (mobile, fixed line, toll-free, VoIP). Offline libphonenumber checks. Arabic digits, per-row country and duplicate removal.

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

## Pricing

from $0.32 / 1,000 phone 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.
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 & Formatter (Bulk, Offline)

This phone number validator checks and formats phone numbers from any country in bulk. Paste a list of numbers. For each number you get a valid flag, a possible flag, the E.164 format, the international and national formats, the country and the line type (mobile, fixed line, toll-free, VoIP and more). The checks run offline with the open-source libphonenumber metadata. The Actor never calls a carrier, an HLR service or any other lookup.

### What the phone number validator does

- Validates each number against the numbering plan of its country.
- Formats valid numbers as E.164 (`+447400123456`), international (`+44 7400 123456`) and national (`07400 123456`).
- Finds the country, the country calling code and the line type.
- Reads national numbers with a default country, or with a country per row.
- Reads Arabic-Indic (٠١٢٣) and Persian (۰۱۲۳) digits.
- Reads a leading `00` as an international prefix.
- Keeps extensions such as `ext. 7`.
- Removes duplicates, even when the same number is written in two ways.
- Keeps your own row ID (`ref`), so you can join the results back to your CRM.

### Phone number data you get

| Field | Example |
|---|---|
| `input` | `٠٥١٢٣٤٥٦٧٨` |
| `isValid` | `true` |
| `isPossible` | `true` |
| `validationError` | `null`, or `TOO_SHORT`, `TOO_LONG`, `INVALID_LENGTH`, `INVALID_COUNTRY`, `INVALID_NUMBER`, `NOT_A_NUMBER` |
| `e164` | `+966512345678` |
| `international` | `+966 51 234 5678` |
| `national` | `051 234 5678` |
| `rfc3966` | `tel:+966512345678` |
| `countryCode` / `countryName` | `SA` / `Saudi Arabia` |
| `callingCode` | `+966` |
| `numberType` | `MOBILE` |
| `isDuplicate` | `false` |

`numberType` comes from the numbering plan. The values are MOBILE, FIXED_LINE, FIXED_LINE_OR_MOBILE, TOLL_FREE, PREMIUM_RATE, SHARED_COST, VOIP, PERSONAL_NUMBER, PAGER, UAN, VOICEMAIL and UNKNOWN. Some countries, such as the US and Canada, use the same ranges for mobile and fixed lines. Those numbers show FIXED_LINE_OR_MOBILE.

The offline metadata has no carrier, city or time zone data. So the Actor does not return a carrier, a region inside the country or a time zone. It also cannot tell whether a number is switched on or in use today. For that you need a live HLR or carrier lookup, which this Actor does not do.

### How to validate phone numbers in bulk

1. Open the Actor and paste your numbers into **Phone numbers**, one per line.
2. Set **Default country** (for example `US`, `GB`, `SA` or `AE`) if your list has national numbers such as `050 123 4567`.
3. If each row has its own country, use **Rows with their own country** instead.
4. Set **Maximum numbers** to `0` to check the whole list.
5. Click **Start**. Download the results as CSV, Excel or JSON.

### Input example

```json
{
    "phoneNumbers": ["+44 7400 123456", "12345"],
    "rows": [
        { "phone": "050 123 4567", "country": "AE", "ref": "crm-1001" },
        { "phone": "٠٥١٢٣٤٥٦٧٨", "country": "SA", "ref": "crm-1002" }
    ],
    "defaultCountry": "US",
    "removeDuplicates": true,
    "onlyValid": false,
    "maxItems": 0
}
```

### Output example

This record came from the input above.

```json
{
    "id": "row-4",
    "inputIndex": 4,
    "input": "٠٥١٢٣٤٥٦٧٨",
    "ref": "crm-1002",
    "defaultCountry": "SA",
    "checkedAt": "2026-10-22T08:03:47.520Z",
    "isValid": true,
    "isPossible": true,
    "validationError": null,
    "e164": "+966512345678",
    "international": "+966 51 234 5678",
    "national": "051 234 5678",
    "rfc3966": "tel:+966512345678",
    "extension": null,
    "countryCode": "SA",
    "countryName": "Saudi Arabia",
    "callingCode": "+966",
    "nationalNumber": "512345678",
    "numberType": "MOBILE",
    "isDuplicate": false
}
```

The number `12345` with default country `US` came back with `isValid: false` and `validationError: "TOO_SHORT"`.

### Phone number validator pricing

You pay per event.

- $0.02 when the run starts.
- $0.0004 per number saved to the dataset ($0.40 per 1,000 numbers).

A run of 2,000 numbers costs $0.82. Duplicates that the Actor removes are not charged. With **Only valid numbers** on, invalid numbers are not saved and not charged. Apify's free plan credit covers small lists.

### Privacy and lawful use

Phone numbers are personal data under the GDPR, Saudi PDPL, UAE PDPL and similar laws. You must have a lawful basis to process every number you upload, such as consent or a contract. This Actor only checks the numbers you supply. It never adds names, addresses or any other personal data. It sends the numbers nowhere outside your Apify run. The log and status message show counts only. The results stay in your own Apify dataset until you delete them.

### FAQ

#### Does it check whether a number is active?

No. It checks the number against the official numbering plan. A valid number can still be unassigned or switched off.

#### How many numbers can I check in one run?

We tested 100,000 numbers in one local run. It took about 20 seconds. For larger lists, split them across runs.

#### Why is my national number marked INVALID_COUNTRY?

A number without a `+` country code needs a country. Set **Default country**, or give each row a `country`.

#### Which library does it use?

It uses libphonenumber-js (MIT licence) with the "max" metadata, which comes from Google's libphonenumber (Apache 2.0 licence).

### Other Actors by the same developer

- [Technical SEO Audit & Broken Link Checker](https://apify.com/maliksaid/technical-seo-audit)
- [US Building Permits Scraper: Construction Leads](https://apify.com/maliksaid/us-building-permits)

### Support

Found a number that the Actor reads wrongly? Open an issue on the Actor's **Issues** tab. Include the number format (with the digits changed) and the country.

# Actor input Schema

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

Enter one phone number per line, in any format. Numbers without a + country code use the default country below. Arabic-Indic digits are fine.

## `rows` (type: `array`):

Add numbers as JSON objects when each row needs its own country. Use the keys phone, country (two-letter code) and ref (your own ID, copied to the output).

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

Enter a two-letter country code, such as US, GB, SA or AE. It reads numbers written without a + country code. Leave empty to accept only international numbers.

## `removeDuplicates` (type: `boolean`):

Save each number once, even when it is written in different ways. Turn off to keep every row and mark repeats with isDuplicate.

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

Save only numbers that are valid. Invalid numbers are dropped and not charged.

## `maxItems` (type: `integer`):

Stop after saving this many numbers. The default keeps test runs short and cheap. Set 0 for no limit.

## Actor input object example

```json
{
  "phoneNumbers": [
    "+1 201-555-0123",
    "+44 7400 123456",
    "+966 51 234 5678",
    "+971 50 123 4567",
    "+49 1512 3456789",
    "+91 81234 56789",
    "12345"
  ],
  "rows": [
    {
      "phone": "050 123 4567",
      "country": "AE",
      "ref": "crm-1001"
    },
    {
      "phone": "٠٥١٢٣٤٥٦٧٨",
      "country": "SA",
      "ref": "crm-1002"
    }
  ],
  "defaultCountry": "",
  "removeDuplicates": true,
  "onlyValid": false,
  "maxItems": 100
}
```

# Actor output Schema

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

No description

# 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": [
        "+1 201-555-0123",
        "+44 7400 123456",
        "+966 51 234 5678",
        "+971 50 123 4567",
        "+49 1512 3456789",
        "+91 81234 56789",
        "12345"
    ],
    "rows": [
        {
            "phone": "050 123 4567",
            "country": "AE",
            "ref": "crm-1001"
        },
        {
            "phone": "٠٥١٢٣٤٥٦٧٨",
            "country": "SA",
            "ref": "crm-1002"
        }
    ],
    "maxItems": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("maliksaid/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": [
        "+1 201-555-0123",
        "+44 7400 123456",
        "+966 51 234 5678",
        "+971 50 123 4567",
        "+49 1512 3456789",
        "+91 81234 56789",
        "12345",
    ],
    "rows": [
        {
            "phone": "050 123 4567",
            "country": "AE",
            "ref": "crm-1001",
        },
        {
            "phone": "٠٥١٢٣٤٥٦٧٨",
            "country": "SA",
            "ref": "crm-1002",
        },
    ],
    "maxItems": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("maliksaid/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": [
    "+1 201-555-0123",
    "+44 7400 123456",
    "+966 51 234 5678",
    "+971 50 123 4567",
    "+49 1512 3456789",
    "+91 81234 56789",
    "12345"
  ],
  "rows": [
    {
      "phone": "050 123 4567",
      "country": "AE",
      "ref": "crm-1001"
    },
    {
      "phone": "٠٥١٢٣٤٥٦٧٨",
      "country": "SA",
      "ref": "crm-1002"
    }
  ],
  "maxItems": 100
}' |
apify call maliksaid/phone-number-validator --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,maliksaid/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/iN8EqZI1sgCmvGnCs/builds/Wzjb1dH8HXWwAuO2S/openapi.json
