# Phone Number Validator & Formatter: Bulk, E.164, Mobile Check (`charlesmarkette/phone-number-validator-formatter-bulk-e164`) Actor

Validate and format phone numbers in bulk or enrich any dataset: valid/invalid, E.164, international and national format, mobile/landline/VoIP type, country, location, carrier and time zones. Finds numbers in text, removes duplicates. Offline, no API key.

- **URL**: https://apify.com/charlesmarkette/phone-number-validator-formatter-bulk-e164.md
- **Developed by:** [Charles Markette](https://apify.com/charlesmarkette) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.55 / 1,000 checked phone 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 & Formatter** checks phone numbers in bulk and tells you which are **valid**, then formats each one in **E.164**, international and national style. It also reports the **number type** (mobile, landline, VoIP, toll-free…), country, location, original carrier and time zones. Paste a list, or point it at any Apify dataset (for example leads or contact details from a scraper): the phone columns are added next to each item's original fields. It runs fully offline on Google's open-source **libphonenumber** metadata, so you need no API key and no third-party lookup service, and the numbers you check never leave the run.

### What it does

- **Validates** numbers for 240+ countries and regions: right length, real number range, possible vs valid.
- **Formats** them as E.164 (`+14155552671`), international (`+1 415-555-2671`), national (`(415) 555-2671`) and an RFC 3966 `tel:` link.
- **Classifies** each one: MOBILE, FIXED_LINE, FIXED_LINE_OR_MOBILE, TOLL_FREE, PREMIUM_RATE, VOIP, SHARED_COST and more.
- **Adds context**: country code, location (city or region where the data has it), the carrier the number range was assigned to, and time zones. These are handy for routing calls and SMS at a sensible local hour.
- **Keeps extensions** ("ext. 12", "x12") in their own column.
- **Enriches datasets**: reads phone fields from any dataset, including lists and comma-separated strings, nested fields (`contact.phone`) and a per-item country field.
- **Finds numbers inside text**: turn on *Find phone numbers inside text* to pull every valid number out of page text, descriptions or email signatures.
- **Cleans lists**: remove duplicates (compared in E.164 plus extension, so `(415) 555-2671` and `+1 415 555 2671` count as one), or keep only valid numbers. Removed rows are free.

### Use cases

- **Clean lead lists before a CRM import or an SMS/calling campaign:** drop invalid numbers, get E.164 for Twilio and other dialers, and tell mobile from landline numbers.
- **Enrich scraper output:** run it after a contact-details, Google Maps or directory scraper with `{{resource.defaultDatasetId}}` in an integration.
- **Standardise mixed formats** from forms and spreadsheets into one format.
- **Route by time zone or country** with the time-zone and region columns.

### How to use it

1. Paste numbers into **Phone numbers**, and/or pick a **Dataset to enrich**.
2. Set **Default country** (2-letter code, such as US, GB, DE or IN) for numbers written without a `+` country code. If your items carry their own country, set **Country field**.
3. Optional: change **Phone fields** (default: phone, phones, phoneNumber, phone_number, telephone, mobile), and turn on *Remove duplicate numbers*, *Output only valid numbers* or *Find phone numbers inside text*.
4. Run it. Each checked number becomes one output row. Export as CSV, Excel or JSON, or send it on with an integration.

**Input example**

```json
{
  "phoneNumbers": ["+1 650-253-0000", "(415) 555-2671", "+44 20 7946 0018 ext 5"],
  "defaultCountry": "US",
  "dedupe": true
}
```

**Output example**

```json
{
  "phoneInput": "(415) 555-2671",
  "phoneValid": true,
  "phonePossible": true,
  "phoneE164": "+14155552671",
  "phoneInternational": "+1 415-555-2671",
  "phoneNational": "(415) 555-2671",
  "phoneRfc3966": "tel:+1-415-555-2671",
  "phoneCountryCode": 1,
  "phoneRegion": "US",
  "phoneType": "FIXED_LINE_OR_MOBILE",
  "phoneCarrier": null,
  "phoneLocation": "San Francisco, CA",
  "phoneTimeZones": ["America/Los_Angeles"],
  "phoneExtension": null,
  "phoneError": null
}
```

For a number that is not valid, the formatted columns are empty and `phoneError` says why, so a bad number can't slip into your dialer as if it were real. When you enrich a dataset, each row also keeps the item's own fields and adds `phoneSourceField`, the field the number came from. The Overview table shows the phone columns; CSV, Excel and JSON exports include every field. A run summary (counts of valid, invalid and duplicate numbers) is saved as `OUTPUT` in the run's key-value store.

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

Pay per event: **$0.001 per checked phone number** (one output row), plus a **$0.005** run start fee (one start event per GB of run memory; the default 256 MB run is charged one). Platform usage is included. Numbers you leave out with *Output only valid numbers* or *Remove duplicate numbers* are free.

**Apify Store discounts:** the price above is for Apify's Free plan. Paid plans pay less per 1,000 validated numbers: $0.87 on Starter, $0.70 on Scale, $0.55 on Business. The "from" price at the top of the Store page is the Business price.

**Worked examples:**

*Examples use the Free-plan rate. Where an example doesn't add it, each run also has its run-start charge, counted per GB of Actor memory (minimum one; one at the default memory); Apify Proxy (where used) is billed separately.*

- 1,000 scraped leads with one phone each: 1,000 × $0.001 + $0.005 = **$1.005**.
- A daily check of 50 new sign-ups: $0.055 per run, about **$1.65 per month**.

If you set a maximum cost per run, the Actor stops cleanly at that limit.

### FAQ

#### Does it call the number or send an SMS?

No. It checks the number against the official numbering plans (Google's libphonenumber metadata). It can't tell whether a valid number is switched on or who owns it, and it never contacts the number.

#### Why is the carrier empty for US numbers?

The carrier column comes from the original range allocation. Many countries (including the US and Canada) don't publish it in this data, and numbers can be ported to another carrier later. Treat it as a hint.

#### My numbers have no country code.

Set **Default country**, or a **Country field** if each item has its own country. Without either, numbers without a `+` are reported as "No country" (and in *Find phone numbers inside text* mode they can't be recognised, so they are skipped).

#### "FIXED_LINE_OR_MOBILE"?

In some countries (the US, for example) landline and mobile numbers share the same ranges, so they can't be told apart from the number alone.

#### Is my data private?

Everything runs inside your Actor run. The numbers are not sent to any outside service.

### Other tools by the same author

- [Domain WHOIS & DNS Lookup](https://apify.com/charlesmarkette/domain-whois-dns-lookup-bulk-rdap): registration date, expiry, email provider, SPF and DMARC for the companies in the same lead list.
- [Merge & Dedup Datasets: Remove Duplicates, Only New Items](https://apify.com/charlesmarkette/merge-deduplicate-datasets-only-new-items)
- [Google Sheets Import & Export: Sync, Upsert, Dedupe](https://apify.com/charlesmarkette/google-sheets-sync)
- [Send Email Report & Slack, Discord, Teams, Telegram Alerts](https://apify.com/charlesmarkette/send-email-report-slack-discord-teams-telegram-alerts)
- [Website Contact Info Scraper](https://apify.com/charlesmarkette/website-contact-info-scraper-emails-phones-socials): get the phone numbers, emails and social profiles each company website publishes, then validate the phones here.
- [Website Tech Stack Detector](https://apify.com/charlesmarkette/website-tech-stack-detector-shopify-cms-analytics): see which platform and tools each lead's website runs on.

Phone number metadata: [libphonenumber](https://github.com/google/libphonenumber) (Apache License 2.0), via the [phonenumbers](https://github.com/daviddrysdale/python-phonenumbers) Python package.

# Actor input Schema

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

Phone numbers to check, one per line, in any format (spaces, dashes, brackets, +country code, 'ext. 12'). Leave empty if you only enrich a dataset.

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

2-letter country code (US, GB, DE, IN…) used for numbers written without a + country code. Numbers with +country code ignore it.

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

Optional. A dataset from a scraper run (for example contact details or leads). Each item's phone field is checked and the result columns are added next to the original fields. Use {{resource.defaultDatasetId}} in an integration.

## `phoneFields` (type: `array`):

Which item fields hold phone numbers. Dotted paths work (contact.phone). A field may hold one number, a list, or a comma-separated string; each number becomes its own output row. Default: phone, phones, phoneNumber, phone_number, telephone, mobile.

## `countryField` (type: `string`):

Optional item field with a 2-letter country code (such as countryCode). Used instead of the default country for that item.

## `keepItemFields` (type: `boolean`):

On: each output row is the original item plus the phone columns. Off: only the phone columns.

## `findInText` (type: `boolean`):

Treat the inputs and fields as free text (page text, descriptions, email signatures) and pull out every valid phone number in it (up to 200 per value). Numbers without a + country code are only found when a default or per-item country is set.

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

Leave out numbers that are not valid. Left-out numbers are free.

## `dedupe` (type: `boolean`):

Output each number once (compared in E.164 form plus extension, so +1 415 555 2671 and (415) 555-2671 are the same). Duplicates are free. When enriching a dataset, a later item whose only number was already output is left out; within one item the same number is always counted once.

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

Language code for the location and carrier text, such as en, de, fr, es, ja. Falls back to English.

## `maxResults` (type: `integer`):

Optional cap on output rows (each row is one checked number).

## Actor input object example

```json
{
  "phoneNumbers": [
    "+1 650-253-0000",
    "(415) 555-2671",
    "+44 20 7946 0018"
  ],
  "defaultCountry": "US",
  "keepItemFields": true,
  "findInText": false,
  "onlyValid": false,
  "dedupe": false,
  "language": "en"
}
```

# Actor output Schema

## `items` (type: `string`):

No description

## `summary` (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 650-253-0000",
        "(415) 555-2671",
        "+44 20 7946 0018"
    ],
    "defaultCountry": "US"
};

// Run the Actor and wait for it to finish
const run = await client.actor("charlesmarkette/phone-number-validator-formatter-bulk-e164").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 650-253-0000",
        "(415) 555-2671",
        "+44 20 7946 0018",
    ],
    "defaultCountry": "US",
}

# Run the Actor and wait for it to finish
run = client.actor("charlesmarkette/phone-number-validator-formatter-bulk-e164").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 650-253-0000",
    "(415) 555-2671",
    "+44 20 7946 0018"
  ],
  "defaultCountry": "US"
}' |
apify call charlesmarkette/phone-number-validator-formatter-bulk-e164 --silent --output-dataset

```

## MCP server setup

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

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/w4SiwqFyds4PnkOPI/builds/6ZaidvAcz4IlwzNe3/openapi.json
