# Bulk Phone Number Validator & Formatter (`uhqxx/phone-validator`) Actor

Bulk phone number validator and formatter for lists of up to 10,000 numbers. Checks each number against its country numbering plan and returns E.164, international and national formats, the country and the line type (mobile, landline, VoIP, toll-free...). Works offline: no number is sent anywhere.

- **URL**: https://apify.com/uhqxx/phone-validator.md
- **Developed by:** [UHQxx](https://apify.com/uhqxx) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.80 / 1,000 phone number validateds

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?

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

## Bulk Phone Number Validator & Formatter

### What is Phone Number Validator?

**Phone Number Validator** checks a list of phone numbers and cleans them up in seconds. For each number it tells you whether it is **valid for its country**, gives you the **E.164, international and national formats**, the **country**, and the **line type** (mobile, landline, VoIP, toll-free, premium-rate...). You can validate up to 10,000 numbers per run and export the result as JSON, CSV or Excel.

**What makes it different:** it works fully offline. No number is called, looked up or sent to a third-party service, nothing is guessed or enriched, and no phone number is written in the logs.

### What can this phone validator do?

- Validate numbers written in any common way: `01 23 45 67 89`, `06.12.34.56.78`, `+33 6 12 34 56 78`, `0033612345678`, `tel:+33612345678`.
- Check each number against the numbering plan of its country (valid length and valid number range).
- Return clean formats ready for a CRM or an SMS tool: **E.164** (`+33612345678`), **international** (`+33 6 12 34 56 78`) and **national** (`06 12 34 56 78`).
- Detect the **line type**: mobile, landline, VoIP, toll-free, premium-rate, shared-cost and more.
- Explain every verdict in plain English (`Number is too short`, `Unknown country calling code`...).
- Remove duplicates, even when the same number is written in different ways.
- Skip the numbers in your exclusion list.

### Who is it for?

- Marketing and sales teams cleaning a contact list before a campaign or a CRM import.
- Developers who need a simple number check inside an import or sign-up workflow, through the Apify API.
- Anyone who wants consistent phone formats across several countries.

### 3 ways to use it

1. **Clean a list before an SMS or call campaign.** Remove numbers that cannot exist and review the `risky` ones (premium-rate, toll-free...).
2. **Standardise formats at import.** Turn `06 12 34 56 78`, `0612345678` and `+33 6 12 34 56 78` into one E.164 value.
3. **Separate mobile from landline numbers.** Use the `numberType` column to route messages to mobiles and calls to landlines.

### Example output

Real output from the Actor (fictional numbers):

```json
[
  {
    "phoneNumber": "01 23 45 67 89",
    "verdict": "ok",
    "reason": "Valid landline number (FR)",
    "isValid": true,
    "e164": "+33123456789",
    "international": "+33 1 23 45 67 89",
    "national": "01 23 45 67 89",
    "country": "FR",
    "countryCallingCode": "33",
    "numberType": "FIXED_LINE",
    "isPossible": true
  },
  {
    "phoneNumber": "0899 12 34 56",
    "verdict": "risky",
    "reason": "Valid premium-rate (surcharged) number (FR): not an ordinary line to reach a person",
    "isValid": true,
    "e164": "+33899123456",
    "international": "+33 899 12 34 56",
    "national": "0 899 12 34 56",
    "country": "FR",
    "countryCallingCode": "33",
    "numberType": "PREMIUM_RATE",
    "isPossible": true
  },
  {
    "phoneNumber": "12345",
    "verdict": "invalid",
    "reason": "Number is too short",
    "isValid": false,
    "e164": null,
    "international": null,
    "national": null,
    "country": null,
    "countryCallingCode": null,
    "numberType": null,
    "isPossible": false
  }
]
```

### How to use it

1. Open the Actor on Apify and paste your numbers in **Phone numbers** (one per line; commas and semicolons also work).
2. Set the **Default country** (FR by default) for the numbers written without a `+` and a country code. Numbers that start with `+` keep their own country.
3. Optional: fill the **Exclusion list** with numbers that must not be processed (see the GDPR section).
4. Click **Start**, then open the **Results** table or export it as JSON, CSV or Excel. A short run summary (totals only) is saved in the `SUMMARY` record of the key-value store.

You can also run it from your own tools through the [Apify API](https://docs.apify.com/api/v2), on a schedule, or from integrations such as Make, Zapier or n8n.

#### Input

| Field | Meaning | Default |
|---|---|---|
| `phoneNumbers` | Numbers to check (required). | none |
| `defaultCountry` | Two-letter country code (FR, DE, GB, US, ES, IT...) applied to numbers without a `+`. | FR |
| `excludedNumbers` | Numbers that must NOT be processed. They are skipped, not saved and not counted. Written the same way as above. | empty |
| `maxNumbers` | Safety limit per run, from 1 to 10,000. Numbers beyond it are not checked (a warning is logged). | 1,000 |

#### Output columns

| Column | Meaning |
|---|---|
| `phoneNumber` | The number as you provided it (trimmed). |
| `verdict` | `ok`, `risky` or `invalid` (see below). |
| `reason` | Why, in plain English. |
| `isValid` | The number matches a known number range of its country. |
| `e164` | International format without spaces, for example `+33612345678` (`null` if invalid). |
| `international` / `national` | The same number formatted with spaces (`null` if invalid). |
| `country` | Two-letter country code (`null` if invalid). Where several countries share a calling code (for example +44), it is the best match: GB, GG, JE or IM. |
| `countryCallingCode` | The country calling code without the `+`. |
| `numberType` | `MOBILE`, `FIXED_LINE`, `FIXED_LINE_OR_MOBILE`, `VOIP`, `TOLL_FREE`, `PREMIUM_RATE`, `SHARED_COST`, `PERSONAL_NUMBER`, `PAGER`, `UAN`, `VOICEMAIL`, or `null` if unknown or invalid. |
| `isPossible` | The length is plausible for the country (weaker than `isValid`). |

#### What the verdicts mean

- **ok**: a valid number on an ordinary line (mobile, landline, VoIP, or a type the numbering data does not specify).
- **risky**: a valid number, but on a special line type that is not an ordinary line to reach a person: premium-rate, toll-free, shared-cost, personal, pager, universal access or voicemail numbers.
- **invalid**: not a valid number for its country (too short, too long, unknown country code, a range that does not exist, or not a number at all).

### FAQ

#### Does it tell me whether a number is active or reachable?

No. The Actor does not call numbers or query any operator, so it cannot know whether a number is in service, who owns it, or whether it can receive SMS. `ok` means "a valid number of an existing range", not "this person can be reached".

#### Where does the validation data come from?

From the open-source library [libphonenumber-js](https://gitlab.com/catamphetamine/libphonenumber-js) (MIT licence), which uses the numbering data of Google's libphonenumber project. Numbering plans change; the data reflects the version of the library bundled in the Actor.

#### What if my numbers have no country code?

Set the **Default country**. A number written without `+` is read as a number of that country: `020 7946 0958` is valid with `GB` and invalid with `FR`. Numbers written with a `+` or `00` prefix always keep their own country.

#### Is it safe for personal data?

Phone numbers are personal data when they identify a person. The Actor makes no network call: numbers are not sent to any service, and no number is written in the logs. You remain responsible for your use: see the GDPR section below.

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

Up to 10,000 (the `maxNumbers` setting, 1,000 by default). In our local test, 10,000 numbers were processed in a few seconds.

### Honest limits

- **It cannot tell whether a number is in service** (see the FAQ).
- The line type comes from the numbering data and can be missing or approximate for some countries. When the data does not give a type, the number is `ok` with the reason "line type unknown".
- Extensions are accepted (`+33 6 12 34 56 78 ext 12`) but are not part of the `e164` value.
- Text around a number is not extracted: `call me on 0612345678` is `invalid`. Give one number per line.
- Numbers with more than 100 characters are rejected as invalid.

### Data and GDPR: please read

**Data processed.** The phone numbers you provide, which are personal data when they identify a person. The output contains the same number plus fields derived from it. Nothing else is collected: no names, addresses, profiles or sensitive data.

**What leaves the Actor.** Nothing. The Actor makes no network call. No number is sent to a third-party service. Logs contain totals only, never numbers.

**What is kept.** The Actor keeps nothing of its own. Your results and your run input (including any exclusion list) are stored by Apify with your run, according to Apify's own retention rules. You can delete them from the Console.

**You are responsible for your use.** You decide why and how the numbers are used, so you are responsible for compliance. In particular:

- Have a valid legal basis for any processing and any contact, and follow the rules of your country on phone and SMS prospecting, which are often stricter than for other channels.
- Inform the people concerned and honour objections and other requests (within one month under the GDPR).
- Make sure your list was obtained lawfully.
- Do not use this Actor to guess or rebuild phone numbers.
- Use the **Exclusion list** for people who objected, so they are never processed again.

This is general information, not legal advice.

### En bref (français)

Contrôlez et nettoyez une liste de numéros de téléphone : validité selon le pays, formats E.164 / international / national, pays, type de ligne (mobile, fixe, VoIP, numéro vert, surtaxé...). Chaque numéro reçoit un verdict (`ok`, `risky`, `invalid`) avec la raison. L'Actor fonctionne **entièrement hors ligne** : il n'appelle aucun numéro, n'interroge aucun opérateur ni service tiers, ne devine aucun numéro et n'écrit aucun numéro dans ses logs. **Limite principale : il ne peut pas savoir si le numéro est en service.** Vous restez responsable de l'usage de vos listes (base légale, information des personnes, droit d'opposition, règles de la prospection téléphonique et par SMS) : voir la section « Data and GDPR ». Une **liste d'exclusion** permet d'écarter les personnes qui se sont opposées.

### Pricing

You pay per number checked. The price is shown in the **Pricing** tab of the Actor.

- **Charged:** each number that was actually checked and got a verdict of `ok`, `risky` or `invalid`.
- **Never charged:** duplicates, numbers in your exclusion list, and numbers beyond your `maxNumbers` limit.
- A small fixed fee per run start may also apply, as shown in the Pricing tab.
- You can cap what a run can spend with the maximum cost setting of the run. The Actor stops cleanly when the cap is reached; results already saved are kept.

### More tools by the same developer

- **Bulk Email Verifier & Validator (No SMTP)**: checks email addresses the same privacy-friendly way (format, domain, mail server, disposable domains, typos), with no probing of mail servers.

# Actor input Schema

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

The numbers to check. One per line. Commas and semicolons also work as separators. Write a number with its country code (+33 6 12 34 56 78) or without it (06 12 34 56 78) and set the default country below. Duplicates are checked once.

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

Two-letter country code (FR, DE, GB, US, ES, IT...) applied to numbers written without a + and a country code. Numbers that start with + keep their own country.

## `excludedNumbers` (type: `array`):

Numbers that must NOT be processed, for example people who objected to being contacted. Excluded numbers are skipped, not saved, and not counted in the results. Write them the same way as above (the default country applies).

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

Safety limit for one run. Numbers beyond this count are not checked.

## Actor input object example

```json
{
  "phoneNumbers": [
    "01 23 45 67 89",
    "+33 6 12 34 56 78",
    "+44 7911 123456",
    "0899 12 34 56",
    "12345"
  ],
  "defaultCountry": "FR",
  "excludedNumbers": [],
  "maxNumbers": 1000
}
```

# 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": [
        "01 23 45 67 89",
        "+33 6 12 34 56 78",
        "+44 7911 123456",
        "0899 12 34 56",
        "12345"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("uhqxx/phone-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": [
        "01 23 45 67 89",
        "+33 6 12 34 56 78",
        "+44 7911 123456",
        "0899 12 34 56",
        "12345",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("uhqxx/phone-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": [
    "01 23 45 67 89",
    "+33 6 12 34 56 78",
    "+44 7911 123456",
    "0899 12 34 56",
    "12345"
  ]
}' |
apify call uhqxx/phone-validator --silent --output-dataset

```

## MCP server setup

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