# Bulk Phone Number Lookup — Phone Validator & Reverse Phone (`ntriqpro/phone-number-validator-bulk`) Actor

Bulk phone number validator and reverse phone lookup. One rich row per number: valid/invalid verdict, line type (mobile, VoIP, toll-free, premium rate), country, carrier, region, time zone, 4 formats, SMS-capability, risk flags, plus a batch summary. Offline, no API keys.

- **URL**: https://apify.com/ntriqpro/phone-number-validator-bulk.md
- **Developed by:** [daehwan kim](https://apify.com/ntriqpro) (community)
- **Categories:** Business, Lead generation
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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

## Bulk Phone Number Lookup — Phone Validator & Reverse Phone

**Paste a list of phone numbers, get a full verdict on every one.** This
**bulk phone number lookup** and **phone validator** turns a messy column of
numbers — `(415) 555-2671`, `001-415-555-2671`, `+82 10-1234-5678` — into clean,
decision-ready rows: valid or invalid, what kind of line it is, which country and
carrier range it belongs to, what time zone the owner is in, all four standard
formats, whether you can send it an SMS, and what to watch out for.

Powered by **[libphonenumber](https://github.com/google/libphonenumber)**, the
same numbering-plan library that ships inside Android — running **fully offline**
on the Apify platform, with API access, scheduling, integrations and monitoring
built in.

> **Unofficial.** This Actor uses the open-source libphonenumber metadata
> (Apache-2.0, via the `phonenumbers` Python port) and is **not affiliated with,
> endorsed by, or sponsored by** Google or any carrier. See `LICENSE-NOTICE.md`.

### Why use Bulk Phone Number Lookup?

- **Bulk, not one-at-a-time** — hand it a whole list; every number comes back as
  its own row, plus one batch summary row at the end.
- **A verdict, not a boolean.** Most validators answer `true` / `false`. This one
  answers in a sentence you can paste into a ticket:
  `+82 10-1234-5678 — valid South Korea mobile, Asia/Seoul, safe to SMS`
- **Line type, so you know what you are calling** — mobile, fixed line, VoIP,
  toll-free, premium rate, shared cost, pager, UAN, voicemail.
- **Risk flags** — `premium-rate`, `voip`, `shared-cost`, `toll-free`,
  `not-possible`, `invalid-for-region`, `country-mismatch`, `unparseable`,
  rolled into a single `riskLevel` of none / low / medium / high. Premium-rate
  numbers in a lead list are a cost trap; this is the field that catches them.
- **SMS-capability** — `smsCapable` is `true` for mobile lines, `false` for
  fixed lines and service numbers, and `null` when the plan genuinely cannot
  tell (VoIP, personal numbers). Filter your SMS campaign on one field.
- **Reverse phone data** — country, country name, international dialling code,
  geographic region (`San Francisco, CA`, `London`), carrier range
  (`Vodafone`, `T-Mobile`) and IANA time zones, all from public numbering-plan
  metadata.
- **Four formats at once** — E.164 (`+14155552671`), international
  (`+1 415-555-2671`), national (`(415) 555-2671`) and RFC3966
  (`tel:+1-415-555-2671`), so the output drops straight into your CRM, dialler
  or SMS gateway.
- **Duplicate collapsing** — `415-555-2671` and `+1 415 555 2671` are the same
  line; with `dedupe` on (the default) you get one row, not two.
- **Offline and private** — no API keys, no third-party service, no lookup
  request leaves the run. Your list is never sent anywhere.

### How to use Bulk Phone Number Lookup

1. Paste your numbers into **Phone numbers**.
2. Set a **Default region** (e.g. `US`, `GB`, `KR`) if some numbers have no
   country code.
3. Click **Start** and read the results in the **Output** tab, or pull them
   through the Apify API.

### Input

| Field | Type | Description |
|---|---|---|
| `phoneNumbers` | array | The numbers to check. Any format. |
| `phoneNumber` | string | Single-number alternative. |
| `defaultRegion` | string | ISO 3166-1 alpha-2 code used for numbers with no country code (e.g. `US`). |
| `dedupe` | boolean | Collapse numbers that normalise to the same E.164 value. Default `true`. |

```json
{
  "phoneNumbers": [
    "+1 415-555-2671",
    "+82 10-1234-5678",
    "+44 20 7183 8750",
    "+49 1522 3433333",
    "+1 900 555 0199"
  ],
  "defaultRegion": "US",
  "dedupe": true
}
```

### Output

One dataset item per phone number. Rows that carry a `noticeCode` are **status
rows, not results** (`summary`, `no-input`, `targets-truncated`,
`charge-limit-reached`, `free-plan-cap`, `billing-unavailable`) — skip them when
parsing programmatically.

Invalid input ends the run as SUCCEEDED with a notice row.

```json
{
  "input": "+49 1522 3433333",
  "valid": true,
  "possible": true,
  "e164": "+4915223433333",
  "international": "+49 1522 3433333",
  "national": "01522 3433333",
  "rfc3966": "tel:+49-1522-3433333",
  "numberType": "MOBILE",
  "numberTypeLabel": "mobile",
  "country": "DE",
  "countryName": "Germany",
  "countryCallingCode": "+49",
  "region": "Germany",
  "carrier": "Vodafone",
  "timezones": ["Europe/Berlin"],
  "smsCapable": true,
  "riskFlags": [],
  "riskLevel": "none",
  "invalidReason": null,
  "normalization": {
    "changed": true,
    "strippedCharacters": ["+", " "],
    "inputLength": 16
  },
  "headline": "+49 1522 3433333 — valid Germany mobile (Vodafone), Europe/Berlin, safe to SMS",
  "scannedAt": "2026-08-26T00:00:00.000Z"
}
```

The batch also gets one unbilled `summary` row with `numbersReceived`,
`numbersAnalysed`, `duplicatesRemoved`, `validCount`, `invalidCount`,
`smsCapableCount`, and the full `countryDistribution`, `typeDistribution` and
`riskFlagDistribution`.

You can download the dataset in various formats such as JSON, HTML, CSV, or Excel.

### Data table

| Field | Description |
|---|---|
| `input` | The number exactly as you supplied it |
| `headline` | One-line verdict for this number |
| `valid` | Whether the number is valid under its country's numbering plan |
| `possible` | Whether the length is plausible for that plan |
| `e164` / `international` / `national` / `rfc3966` | The four standard formats |
| `numberType` / `numberTypeLabel` | Line type code and human label |
| `country` / `countryName` / `countryCallingCode` | ISO code, country name, dialling code |
| `region` | Geographic area the number range belongs to |
| `carrier` | Carrier that holds the number range, where the plan records one |
| `timezones` | IANA time zones for the number range |
| `smsCapable` | `true` / `false` / `null` when the plan cannot tell |
| `riskFlags` / `riskLevel` | What to watch out for, and how serious |
| `invalidReason` | Why a number failed, in plain English |
| `normalization` | What changed between your input and E.164 |
| `scannedAt` | UTC timestamp of the check |

### Pricing

Pay-per-event: **$0.005 per run** plus **$0.03 per number resolved**. A list of
100 numbers costs $3.005 for that run. Every number written to the dataset is
charged, whether the verdict is valid or invalid — an "invalid" verdict is the
answer you asked for, and it is the one that saves you a wasted SMS. Batch
summary and status rows are not charged. Apify platform compute is billed
separately per your plan.

**Free-plan runs return up to 25 numbers**; upgrade your Apify plan to receive
the full result set in one run.

### Tips

- Set `defaultRegion` to the country your list mostly comes from — numbers
  without a country code are unparseable otherwise.
- Filter on `smsCapable == true` before an SMS campaign, and on
  `riskLevel in ("medium", "high")` before a dialler campaign.
- `country-mismatch` catches numbers that claim one country but belong to
  another — a common signal in fraud screening.
- Schedule a recurring run against an exported CRM segment to keep contact data
  clean over time.

### FAQ, disclaimers, and support

**Does it tell me who owns the number?** No. This Actor returns numbering-plan
facts — validity, line type, country, region, carrier range, time zone — not
subscriber identity. No personal data is looked up or returned.

**Does it tell me whether the number is currently in service?** No. Offline
validation says the number is *well-formed and issuable* under the public
numbering plan. Live line-status checks require a paid HLR lookup, which this
Actor deliberately does not perform.

**Why is `carrier` empty for some numbers?** Carrier data exists only for
mobile ranges in countries that publish it, and number portability means the
range holder is not always the current carrier. Treat it as a strong hint, not
a guarantee.

**Privacy & data responsibility.** **You are the data controller** for every
number you submit; ntriqpro is a **data processor** acting solely on your
instructions. Validation runs offline inside your own run and **we do not store
your results** — output is written only to your own run's dataset. You are
responsible for a lawful basis and for complying with GDPR, PIPA, CCPA, TCPA and
other applicable laws.

**Open-source licence.** Built on **libphonenumber** (Apache-2.0) by Google, via
the `phonenumbers` Python port by David Drysdale. It is an independent
integration, not an official distribution. See `LICENSE-NOTICE.md`.

**Found a bug or want a feature?** Use the **Issues** tab. Custom solutions
available.

*Used this Actor? A sentence in the **Reviews** tab about what you used it for
helps the next person decide.*

### Works well with

- **[phoneinfoga-osint](https://apify.com/ntriqpro/phoneinfoga-osint)** — Take any number that passed validation into a deep OSINT lookup.
- **[theharvester-osint](https://apify.com/ntriqpro/theharvester-osint)** — Harvest more numbers and emails from a company domain to feed this validator.

Every run of this Actor also returns these suggestions in the `nextSteps` field of its summary row.

# Actor input Schema

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

One or more phone numbers to validate and look up. Any format works — +1 415-555-2671, (415) 555 2671, 001-415-555-2671. Numbers without a country code are read using the Default region below.

## `phoneNumber` (type: `string`):

Alternative to the list above — a single number to validate.

## `defaultRegion` (type: `string`):

Two-letter country code used to read numbers that have no country code, e.g. US, GB, KR, DE. Leave empty to require international format.

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

Collapse numbers that normalise to the same E.164 value (e.g. 415-555-2671 and +14155552671) so you are not charged twice for the same line. On by default.

## Actor input object example

```json
{
  "phoneNumbers": [
    "+1 415-555-2671",
    "+82 10-1234-5678",
    "+44 20 7183 8750",
    "+49 1522 3433333",
    "+1 900 555 0199"
  ],
  "defaultRegion": "US",
  "dedupe": true
}
```

# 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 415-555-2671",
        "+82 10-1234-5678",
        "+44 20 7183 8750",
        "+49 1522 3433333",
        "+1 900 555 0199"
    ],
    "defaultRegion": "US",
    "dedupe": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("ntriqpro/phone-number-validator-bulk").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 415-555-2671",
        "+82 10-1234-5678",
        "+44 20 7183 8750",
        "+49 1522 3433333",
        "+1 900 555 0199",
    ],
    "defaultRegion": "US",
    "dedupe": True,
}

# Run the Actor and wait for it to finish
run = client.actor("ntriqpro/phone-number-validator-bulk").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 415-555-2671",
    "+82 10-1234-5678",
    "+44 20 7183 8750",
    "+49 1522 3433333",
    "+1 900 555 0199"
  ],
  "defaultRegion": "US",
  "dedupe": true
}' |
apify call ntriqpro/phone-number-validator-bulk --silent --output-dataset

```

## MCP server setup

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

```

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/FMWKOQZ7tdo7NuYMU/builds/vBAsz6cxA9AeM0Yfx/openapi.json
