# Bulk Phone Number Validator — E.164, Carrier & Line Type (`haketa/bulk-phone-validator`) Actor

Validate and format thousands of phone numbers at once: validity, E.164 / international / national format, line type (mobile, landline, VoIP, toll-free), country, carrier, timezone and area. Fast, offline, keyless phone-list cleaning for CRM, SMS and leads.

- **URL**: https://apify.com/haketa/bulk-phone-validator.md
- **Developed by:** [Haketa](https://apify.com/haketa) (community)
- **Categories:** Lead generation, Developer tools
- **Features:** Limited permissions
- **Stats:** 13 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.60 / 1,000 results

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

## Bulk Phone Number Validator — E.164 Formatter, Line Type, Carrier, Country & Timezone Lookup

[![Apify Actor](https://img.shields.io/badge/Apify-Actor-00C853?style=flat-square\&logo=apify)](https://apify.com/haketa/bulk-phone-validator)
[![E.164 Formatter](https://img.shields.io/badge/📞-E.164_·_International_·_National-2da44e?style=flat-square)]()
[![Line Type & SMS](https://img.shields.io/badge/📱-Mobile_vs_Landline_&_VoIP-1f6feb?style=flat-square)]()
[![Carrier & Timezone](https://img.shields.io/badge/🌍-Carrier_·_Country_·_Timezone-6f42c1?style=flat-square)]()
[![1,000+ Bulk Scale](https://img.shields.io/badge/⚡-1,000%2B_to_Millions_Keyless-orange?style=flat-square)]()
[![Export](https://img.shields.io/badge/⬇️-JSON_·_CSV_·_Excel_·_API-24292f?style=flat-square)]()

**Validate, clean, format, and enrich thousands of phone numbers in seconds.** This **bulk phone number validator** takes any raw phone list—in E.164, international, or local national format—and returns a structured record for every number: **validity (`isValid`), possibility (`isPossible`), canonical E.164 format (`e164`), international and national formatting, `tel:` URI, extension, line type (`MOBILE`, `FIXED_LINE`, `VOIP`, `TOLL_FREE`, etc.), SMS-readiness (`isMobile`), ISO country & calling code, original network carrier, geographic area, and IANA timezone.**

Fast, **keyless, and rate-limit-free** — no third-party per-lookup validation API fees, no external API keys, and no throttling. Bulk export your cleaned phone list to **JSON, CSV, Excel (XLSX), HTML, or via the Apify API**.

> 🎯 **Built for** B2B sales, growth, and RevOps teams doing **CRM phone hygiene**, SMS & WhatsApp marketers protecting **campaign deliverability**, lead-generation agencies **filtering scraped leads**, call centers scheduling **timezone-aware dialing**, and developers who need a high-throughput **phone number validation API**.

***

### ⚡ At a glance

| Feature | Details |
| :--- | :--- |
| 🎯 **Core capability** | Bulk phone validation, E.164 normalization, line-type classification, and carrier/timezone/location enrichment |
| 🌍 **Global coverage** | 240+ countries and territories (US, UK, DE, FR, ES, IT, NL, SE, PL, TR, AE, SA, IN, CN, JP, BR, MX, AU, CA & more) |
| 📱 **Line-type detection** | Distinguishes `MOBILE`, `FIXED_LINE`, `FIXED_LINE_OR_MOBILE`, `VOIP`, `TOLL_FREE`, `PREMIUM_RATE`, `SHARED_COST`, `PERSONAL_NUMBER`, `PAGER`, `UAN`, `VOICEMAIL` |
| 💬 **SMS & WhatsApp flag** | Derived `isMobile` flag (`"true"` / `"false"`) so you never waste SMS budget texting landlines |
| 🔁 **Scale & throughput** | Processes **100 to 1,000,000+ phone numbers** per run (`maxItems: 0` = unlimited) in parallel (`maxConcurrency: 50`) |
| 🔓 **Keyless & zero network lag** | Powered by embedded international numbering plan metadata — **0 external API calls, 0 rate limits, 100% deterministic** |
| ⬇️ **Export & API** | **JSON, CSV, Excel, HTML**, plus **Key-Value Store `OUTPUT` summary** for synchronous API / webhook pipelines |

***

### 🔎 Why validate and normalize phone numbers in bulk?

Raw phone lists from web scrapers, lead forms, CRM exports, and event signups are notoriously messy: numbers arrive with spaces, dashes, parentheses, extensions, missing `+` signs, local trunk prefixes (`0`), typos, and fake digits—with landlines and VoIP numbers mixed right alongside mobile numbers.

Dialing or texting an uncleaned list costs money and damages sender reputation:

- 💬 **Stop wasting SMS & WhatsApp spend on landlines** — filter to `isValid = "true"` and `isMobile = "true"` before launching any Twilio, WhatsApp Business, or SMS broadcast.
- 🧹 **Eliminate CRM duplicates with canonical E.164 keys** — `+1 (415) 555-2671`, `415.555.2671`, and `0014155552671` all normalize to `+14155552671` (`e164`), letting you deduplicate HubSpot, Salesforce, or Pipedrive contacts instantly.
- ⏰ **Call prospects during local business hours** — use the enriched `timezone` (`America/New_York`, `Europe/Berlin`, `Asia/Dubai`) and `countryName` fields to route outbound calls to the right regional sales pod at the right hour.
- 🛡️ **Catch bad leads at capture** — flag impossible or invalid numbers (`isValid = "false"`, with a clear `error` reason) from ad forms and B2B lead lists before they pollute your database.
- 💰 **Replace expensive per-lookup APIs** — commercial lookup APIs charge $0.005–$0.01 per number ($5–$10 per 1,000 numbers). This Actor validates **thousands of numbers in a single run** for fractions of a cent in compute.

***

### ✨ What this bulk phone validator does

- ✅ **Unlimited 1,000+ bulk processing (`maxItems: 0`)** — paste 100, 1,000, 50,000, or 500,000+ phone numbers; by default (`maxItems: 0`), the Actor processes your **entire list** without truncating.
- ✅ **100-record instant demo on empty runs** — leave `phoneNumbers` empty and click **Start** to immediately validate a built-in sample of **100 diverse international phone numbers** across 25+ countries in ~2 seconds.
- ✅ **Accepts any input format** — parses E.164 (`+442079460100`), international (`+44 20 7946 0100`), national (`01512 3456789` with `defaultCountry: "DE"`), and numbers with extensions (`+1 415 555 2671 ext. 42`).
- ✅ **4 ready-to-use output formats per number** — `e164` (`+4915123456700`), `international` (`+49 1512 3456700`), `national` (`01512 3456700`), and clickable `uri` (`tel:+4915123456700`).
- ✅ **Carrier, geographic area & timezone enrichment** — enriches valid numbers with original mobile `carrier` (e.g., `T-Mobile`, `Vodafone`, `Orange`, `China Mobile`), landline `location` (state/city), and IANA `timezone`.
- ✅ **1-to-1 row preservation** — invalid or unparseable inputs are never silently dropped; they are returned in order with `isValid: "false"` and a clear explanation in `error` so your spreadsheet row alignment stays intact.
- ✅ **Key-Value Store `OUTPUT` summary** — writes run totals (`totalValidated`, `validCount`, `invalidCount`, `mobileCount`) to `OUTPUT` for instant inspection by automated pipelines.

***

### 🎯 Use cases & B2B data hygiene workflows

| Team / Role | Workflow & Value Delivered |
| :--- | :--- |
| 📈 **B2B Lead Generation & Scraping Teams** | Pipe phone numbers extracted from Google Maps, Justdial, Yellow Pages, or company websites directly into this Actor. Standardize every lead to `e164` and tag mobile vs. fixed-line numbers before delivering the dataset to clients. |
| 💬 **SMS, WhatsApp & Mobile Marketing** | Upload campaign contact lists, filter for `isValid == "true"` and `isMobile == "true"`, and segment by `country` and `carrier` to maximize delivery rates and avoid carrier filtering. |
| 🏢 **RevOps & CRM Administrators** | Run quarterly hygiene sweeps across Salesforce, HubSpot, or Zoho exports. Use `e164` as the unique merge key to collapse duplicate contact records written in different punctuation styles. |
| 📞 **Outbound Call Centers & SDR Teams** | Enrich raw prospect lists with `timezone`, `countryName`, `location`, and `national` format so dialers automatically schedule calls within legal local calling windows. |
| ⚙️ **Backend & Automation Engineers** | Trigger the Actor via Apify API or Make/Zapier/n8n webhooks whenever a new batch of leads lands in Google Sheets, Airtable, or Postgres. |

***

### 📥 Input configuration

All fields are optional (`"required": []`). Paste your phone numbers one per line, or leave `phoneNumbers` empty to run the 100-number international sample.

| Field | Type | Default / Prefill | Description |
| :--- | :--- | :--- | :--- |
| `phoneNumbers` | `array<string>` | `[]` (runs 100-item sample) | Phone numbers to validate, one per line. Accepts E.164, international, or national formats. |
| `defaultCountry` | `string` | `""` (Auto-detect) | Two-letter ISO country code (`US`, `GB`, `DE`, `FR`, `TR`, `IN`, `AE`, `BR`, etc.) used to interpret numbers written in national format without a leading `+`. |
| `includeCarrierData` | `boolean` | `true` | Enriches valid numbers with `carrier`, `location`, and `timezone`. Disable only if you want raw validation speed on multi-million-row lists. |
| `maxItems` | `integer` | `0` (Unlimited) | Maximum number of records to output (`0` = process the entire list without limits). Also supports `maxResults` / `maxRecords`. |
| `maxConcurrency` | `integer` | `50` | Number of phone numbers enriched concurrently (`1`–`200`). |

#### Example input — validate an international phone list (1,000+ ready)

```json
{
  "phoneNumbers": [
    "+14155552671",
    "+447700900200",
    "+4915123456700",
    "+33612345678",
    "+971501234567",
    "+919820112300"
  ],
  "defaultCountry": "",
  "includeCarrierData": true,
  "maxItems": 0,
  "maxConcurrency": 50
}
```

#### Example input — validate national-format numbers with a default country

```json
{
  "phoneNumbers": [
    "0170 1234567",
    "030 12345678",
    "0151 22334455 ext. 19"
  ],
  "defaultCountry": "DE",
  "includeCarrierData": true
}
```

***

### 📤 Output schema & field reference

The Actor writes one structured record per phone number. Boolean flags (`isValid`, `isPossible`, `isMobile`) are emitted as `"true"` / `"false"` strings so they export cleanly across CSV, Excel, and schema-strict pipelines.

#### Example output — valid US number with geographic area & timezone

```json
{
  "input": "+14155552600",
  "isValid": "true",
  "isPossible": "true",
  "e164": "+14155552600",
  "international": "+1 415 555 2600",
  "national": "(415) 555-2600",
  "uri": "tel:+14155552600",
  "extension": "",
  "countryCode": "1",
  "country": "US",
  "countryName": "United States",
  "numberType": "FIXED_LINE_OR_MOBILE",
  "isMobile": "true",
  "location": "San Francisco, CA",
  "carrier": "",
  "timezone": "America/Los_Angeles",
  "error": "",
  "scrapedAt": "2026-11-09T15:10:00.000Z"
}
```

#### Example output — valid German mobile number with carrier & timezone

```json
{
  "input": "+4915123456700",
  "isValid": "true",
  "isPossible": "true",
  "e164": "+4915123456700",
  "international": "+49 1512 3456700",
  "national": "01512 3456700",
  "uri": "tel:+4915123456700",
  "extension": "",
  "countryCode": "49",
  "country": "DE",
  "countryName": "Germany",
  "numberType": "MOBILE",
  "isMobile": "true",
  "location": "",
  "carrier": "T-Mobile",
  "timezone": "Europe/Berlin",
  "error": "",
  "scrapedAt": "2026-11-09T15:10:00.000Z"
}
```

#### ✅ Validation & Formatting Fields

| Field | Type | Coverage | Description |
| :--- | :--- | :--- | :--- |
| `input` | `string` | 🟢 High (100%) | Exact raw phone number string as supplied in the input. |
| `isValid` | `string` | 🟢 High (100%) | `"true"` if the number passes full length, prefix, and country numbering plan rules; `"false"` otherwise. |
| `isPossible` | `string` | 🟢 High (100%) | `"true"` if the digit length is possible for the detected country. |
| `e164` | `string` | 🟢 High (100% of valid) | Canonical **E.164** format (e.g., `+4915123456700`) — ideal primary key for CRM deduplication. |
| `international` | `string` | 🟢 High (100% of valid) | Human-readable international format (e.g., `+49 1512 3456700`). |
| `national` | `string` | 🟢 High (100% of valid) | Local dialing format within the country (e.g., `01512 3456700`). |
| `uri` | `string` | 🟢 High (100% of valid) | RFC 3966 clickable dialer URI (`tel:+4915123456700`). |
| `extension` | `string` | 🟡 Conditional | Extension digits parsed from inputs like `ext. 123` or `#123` (empty string when none). |
| `error` | `string` | 🟡 Conditional | Diagnostic message when `isValid` is `"false"` (e.g., `"number is not valid for its country"`). |

#### 📱 Line Type, Country & Enrichment Fields

| Field | Type | Coverage | Description |
| :--- | :--- | :--- | :--- |
| `numberType` | `string` | 🟢 High (~99% of valid) | Line classification: `MOBILE`, `FIXED_LINE`, `FIXED_LINE_OR_MOBILE`, `VOIP`, `TOLL_FREE`, `PREMIUM_RATE`, `SHARED_COST`, `PERSONAL_NUMBER`, `PAGER`, `UAN`, `VOICEMAIL`. |
| `isMobile` | `string` | 🟢 High (100%) | `"true"` when `numberType` is `MOBILE` or `FIXED_LINE_OR_MOBILE` (capable of receiving SMS). |
| `countryCode` | `string` | 🟢 High (100% of valid) | International country calling prefix without `+` (e.g., `1`, `44`, `49`, `90`, `91`). |
| `country` | `string` | 🟢 High (100% of valid) | Two-letter ISO 3166-1 alpha-2 country code (e.g., `US`, `GB`, `DE`, `FR`, `IN`). |
| `countryName` | `string` | 🟢 High (100% of valid) | Full English country name (e.g., `United States`, `United Kingdom`, `Germany`). |
| `carrier` | `string` | 🟡 Conditional (~80% mobile) | Original mobile network operator assigned to the prefix (e.g., `T-Mobile`, `Vodafone`, `Orange`). Empty on landlines. |
| `location` | `string` | 🟡 Conditional (Geographic) | City, state, or region for geographic/fixed-line numbers (e.g., `San Francisco, CA`, `London`). |
| `timezone` | `string` | 🟢 High (~98% of valid) | Comma-separated IANA timezone IDs for the number's region (e.g., `Europe/Berlin`, `America/New_York`). |
| `scrapedAt` | `string` | 🟢 High (100%) | ISO 8601 UTC timestamp of when the validation run executed. |

***

### 🔁 Scaling to 1,000+ and 100,000+ phone numbers

Unlike web scrapers that paginate across external web pages, this Actor processes your entire input array locally in memory using an asynchronous worker pool (`maxConcurrency: 50`) and flushes validated records to the Apify Dataset in 500-item batches:

- **100 numbers (default sample run):** finishes in **~2–4 seconds**.
- **1,000+ numbers:** finishes in **~5–10 seconds** with zero external network bottlenecks.
- **10,000–100,000+ numbers:** streams smoothly in batches of 500 records without hitting rate limits or proxy costs.
- **Want to test 1,000+ scale without uploading a file?** Leave `phoneNumbers` empty and set `maxItems: 1000`—the Actor's built-in international sample generator will automatically synthesize and validate **1,000 unique international phone numbers** across 25 countries!

***

### ⚙️ How to use & integrate via API

1. **Open the Bulk Phone Number Validator** in Apify Console.
2. **Paste your phone numbers** into `phoneNumbers` (or leave it empty to test with the 100-number sample).
3. **Select `defaultCountry`** if your list contains local national numbers without a `+` prefix.
4. **Click Start** and download your validated table as **CSV, Excel, or JSON**.

#### API example (cURL)

```bash
curl -X POST "https://api.apify.com/v2/acts/haketa~bulk-phone-validator/runs?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "phoneNumbers": ["+14155552671", "+447700900200", "+4915123456700"],
    "includeCarrierData": true
  }'
```

#### API example (Python)

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")

run = client.actor("haketa/bulk-phone-validator").call(run_input={
    "phoneNumbers": ["+14155552671", "01512 3456700", "+971501234567"],
    "defaultCountry": "DE",
    "includeCarrierData": True,
})

for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(f"{row['input']} -> valid={row['isValid']} e164={row['e164']} type={row['numberType']} carrier={row['carrier']}")
```

#### API example (Node.js)

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });

const run = await client.actor('haketa/bulk-phone-validator').call({
    phoneNumbers: ['+14155552671', '+447700900200', '+33612345678'],
    includeCarrierData: true,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(`Validated ${items.length} phone numbers.`);
```

***

### 💡 Pro tips for phone list cleaning

- 🌍 **Always keep `+` country codes when scraping:** If your numbers already start with `+`, leave `defaultCountry` on **Auto-detect (`""`)**—the validator will automatically detect all 240+ countries in the same mixed list.
- 📱 **Why US & Canadian numbers show `FIXED_LINE_OR_MOBILE`:** In the North American Numbering Plan (`+1`), mobile and landline numbers share the same area-code prefixes. The validator accurately reports `FIXED_LINE_OR_MOBILE` and sets `isMobile: "true"` so North American numbers are included in SMS-eligible segments.
- 🔄 **Original carrier vs. ported numbers:** Prefix-based carrier resolution identifies the **original network operator** assigned to a number block. Because of mobile number portability (MNP), treat `carrier` as an initial network signal rather than a live HLR dip.

***

### ⚠️ Honest limitations

- **Offline numbering plan validation (not a live HLR/ping check):** This Actor validates syntax, country prefix, digit length, and numbering-plan assignment offline. It does not place a live call or HLR network ping to check whether a handset is currently powered on.
- **Carrier vs. Location mutual exclusivity:** In most countries, mobile prefixes map to a `carrier` (e.g., `Vodafone`) without a fixed city, whereas landline prefixes map to a geographic `location` (e.g., `London`, `New Jersey`) without a single carrier.

***

### ❓ Frequently asked questions (FAQ)

**How many phone numbers can I validate in a single run?**
There is no hard limit (`maxItems: 0` by default). You can validate 100, 1,000, 50,000, or 1,000,000+ phone numbers in a single job.

**Does it require any external API key or Twilio/Numverify subscription?**
No. Everything runs self-contained inside the Actor with zero external API keys, zero per-number lookup fees, and zero rate limits.

**Does it dial or send an SMS to the phone numbers?**
No. Validation is 100% passive and offline—the phone owner is never contacted or alerted.

**What happens if I run the Actor with empty input?**
It automatically generates and validates a diverse sample of **100 international phone numbers** across 25 countries (or `N` numbers if you set `maxItems: N`) so you can test downstream integrations immediately.

**Do I need proxies for this Actor?**
No proxies are needed because no external websites or third-party APIs are queried.

***

### 🔌 CRM & marketing automation integrations

Drop this **Bulk Phone Number Validator** directly into your data-ingestion and outbound workflows:

- 🏢 **HubSpot, Salesforce & Pipedrive:** Trigger via **Make, Zapier, or n8n** whenever new leads arrive. Write back the normalized `e164` number, `isMobile` status, `countryName`, and `timezone` so SDRs always dial clean numbers during local business hours.
- 💬 **Twilio, WhatsApp Business, Klaviyo & Braze:** Pre-filter every contact list to `isValid == "true"` and `isMobile == "true"` before importing into SMS/WhatsApp platforms—eliminating landline bounce fees and protecting sender reputation.
- 📊 **Google Sheets, Airtable & Supabase:** Clean entire spreadsheet columns in bulk on a schedule or via webhook and export ready-to-join tables keyed on `input` or `e164`.
- 🔗 **Chained Apify Lead Pipelines:** Pair this Actor directly after **Google Maps Scraper**, **Justdial Scraper**, or **Email & Contact Extractor** via an Actor-to-Actor webhook to automatically validate every scraped business phone number.

***

### 📊 Downstream deduplication & SMS segmentation recipe (Python / Pandas)

Once your validation run completes, you can split your list into SMS-ready mobiles, callable landlines, and invalid leads in just a few lines of Python:

```python
import pandas as pd

## Load validated phone dataset from Apify
df = pd.read_json("validated_phones.json")

## 1. Keep only valid numbers and deduplicate on canonical E.164
valid_df = df[df["isValid"] == "true"].drop_duplicates(subset=["e164"])

## 2. Segment into SMS-capable mobile numbers vs. fixed-line / landline numbers
sms_ready = valid_df[valid_df["isMobile"] == "true"]
landlines = valid_df[valid_df["numberType"] == "FIXED_LINE"]
invalid_rows = df[df["isValid"] == "false"]

print(f"Unique valid numbers: {len(valid_df)}")
print(f"SMS/WhatsApp mobile numbers: {len(sms_ready)}")
print(f"Fixed-line landlines: {len(landlines)}")
print(f"Invalid / rejected inputs: {len(invalid_rows)}")

## 3. Group valid mobile leads by country and timezone for campaign scheduling
by_tz = (
    sms_ready.groupby(["country", "countryName", "timezone"])
    .size()
    .reset_index(name="mobile_leads")
    .sort_values("mobile_leads", ascending=False)
)
print(by_tz.head(10))
```

#### Equivalent SQL query for CRM deduplication

```sql
SELECT
    e164,
    ANY_VALUE(international) AS international_format,
    ANY_VALUE(national)      AS national_format,
    ANY_VALUE(country)       AS country_iso,
    ANY_VALUE(countryName)   AS country_name,
    ANY_VALUE(numberType)    AS line_type,
    ANY_VALUE(isMobile)      AS is_mobile,
    ANY_VALUE(carrier)       AS carrier,
    ANY_VALUE(timezone)      AS timezone
FROM validated_phone_numbers
WHERE isValid = 'true'
  AND e164 <> ''
GROUP BY e164;
```

***

### 🔗 Related B2B contact & verification actors

- [Bulk Email Verifier — MX, Disposable & Deliverability](https://apify.com/haketa/bulk-email-verifier)
- [Email & Contact Extractor — Emails, Phones, Socials](https://apify.com/haketa/email-extractor)
- [Email Finder & Verifier — B2B Email Lookup API](https://apify.com/haketa/email-finder-verifier)
- [Email Pattern Finder — Name + Domain to Business Email](https://apify.com/haketa/email-pattern-finder)
- [Bulk Company Name to Domain Finder — Website, Logo & Match](https://apify.com/haketa/bulk-company-domain)

***

### 📋 Changelog

- **v1.2 (2026-11)** — Unlocked **unlimited 1,000+ bulk phone validation** (`maxItems` default & prefill set to `0` so user lists are never truncated at 100), upgraded empty/default sample runs to output **100 realistic international phone records** across 25 countries (scalable to `1,000+` via `maxItems`), and added Key-Value Store `OUTPUT` summary statistics (`totalValidated`, `validCount`, `invalidCount`, `mobileCount`).
- **v1.1 (2026-10)** — Serialized batch dataset flushing (`flushChain`) to guarantee zero dropped records on fast runs.
- **v1.0 (2026-09)** — Initial release with E.164 normalization, line-type detection, and carrier/timezone/location lookup.

***

▶️ **Ready to clean and standardize your phone list?** Paste your numbers, click **Start**, and export validated E.164 records in seconds!

# Changelog

This Actor's version history is a separate document: https://apify.com/haketa/bulk-phone-validator/changelog.md

# Actor input Schema

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

Paste the phone numbers you want to validate — one per line. Any format works (E.164, international, or national). Handles anything from a handful to millions. Leave this empty to run a 100-number international sample and see what the results look like. For numbers written without a country code, pick a Default country below.

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

Pick the country used to read numbers written in national format (without a leading +). Leave on Auto-detect if every number already starts with a country code.

## `includeCarrierData` (type: `boolean`):

Look up carrier, timezone and geographic area for valid numbers. Turn off for the fastest, validation-only run on huge lists.

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

Cap the number of results. Set to 0 for unlimited (process the whole list).

## `maxConcurrency` (type: `integer`):

How many numbers to process in parallel.

## Actor input object example

```json
{
  "phoneNumbers": [
    "+14155552671",
    "+442071838750",
    "+491701234567",
    "+33612345678",
    "+8613800138000",
    "+551123456789"
  ],
  "defaultCountry": "",
  "includeCarrierData": true,
  "maxItems": 0,
  "maxConcurrency": 50
}
```

# Actor output Schema

## `input` (type: `string`):

The number as supplied

## `isValid` (type: `string`):

Whether the number is valid

## `isPossible` (type: `string`):

Whether the number is possible (length/prefix)

## `e164` (type: `string`):

E.164 format

## `international` (type: `string`):

International format

## `national` (type: `string`):

National format

## `uri` (type: `string`):

tel: URI

## `extension` (type: `string`):

Phone extension, when the input includes one (e.g. ext. 123)

## `countryCode` (type: `string`):

Country calling code

## `country` (type: `string`):

ISO country code

## `countryName` (type: `string`):

Country name

## `numberType` (type: `string`):

Mobile, landline, VoIP, toll-free…

## `isMobile` (type: `string`):

Whether the line can receive SMS

## `location` (type: `string`):

Geographic area

## `carrier` (type: `string`):

Carrier / network

## `timezone` (type: `string`):

Timezone(s)

## `error` (type: `string`):

Error, if any

## `scrapedAt` (type: `string`):

Timestamp

# 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": [],
    "includeCarrierData": true,
    "maxItems": 0,
    "maxConcurrency": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("haketa/bulk-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": [],
    "includeCarrierData": True,
    "maxItems": 0,
    "maxConcurrency": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("haketa/bulk-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": [],
  "includeCarrierData": true,
  "maxItems": 0,
  "maxConcurrency": 50
}' |
apify call haketa/bulk-phone-validator --silent --output-dataset

```

## MCP server setup

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