# Phone Number Intelligence API - Carrier, Type and Risk (`nabeelbaghoor/phone-number-intelligence-api`) Actor

Look up any phone number worldwide: carrier, line type, city, region, country, time zone and coordinates, plus a fraud risk score with an allow, flag or block recommendation, SIM swap and porting checks, and whether the number is reachable right now. Pay per result.

- **URL**: https://apify.com/nabeelbaghoor/phone-number-intelligence-api.md
- **Developed by:** [Nabeel Hassan](https://apify.com/nabeelbaghoor) (community)
- **Categories:** Business, Developer tools, Lead generation
- **Stats:** 2 total users, 1 monthly users, 66.7% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$15.00 / 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.

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

## Phone Number Intelligence API - Carrier, Type and Risk

What is this number, who carries it, where is it, should you trust it, and is anyone there right now. Three services over one credential, one flat row per number.

### What this actor does

- **Identifies the line.** Carrier, line type - mobile, landline, prepaid, VoIP, toll free, payphone and the rest - city, county, region, postal code, country, metro code, coordinates and time zone, with the time zone offsets included so a call list can be sorted by local hour rather than by guesswork.
- **Scores fraud risk with an actual recommendation.** A number comes back with a risk level, a score on a zero to one thousand scale, and a plain allow, flag or block, plus the reason codes behind it broken out by category. That is a decision you can route on rather than a number you have to interpret.
- **Uses the lifecycle event to sharpen the score.** The same number carries a very different risk when it is creating an account than when it is signing in to one it has held for two years. Telling the provider which moment this is makes the score meaningfully better, and it is one field.
- **Separates blocklisting from risk.** Some numbers are blocked by policy, meaning the account is not permitted to contact them at all. That is a different decision from a high score, it has different consequences, and it is kept as its own column with its own filter.
- **Returns the number three ways.** As you submitted it, cleansed for calling, and cleansed for messaging. Those are not always the same string, because a number that is valid to dial is not automatically valid to text, and the difference is exactly what a campaign needs to know before it sends.
- **Checks SIM swaps, porting and forwarding.** Twelve identity attributes are available on the lookup modes: the name and address on the line, whether a name you hold matches it, subscriber status, number deactivation, porting history and current porting status, recent SIM swap, active call status, appearance in known breaches, call forwarding, and age verification.
- **Refuses ambiguous numbers instead of guessing.** A number in national format resolves to a different subscriber in every country that shares the pattern, so a run that guesses returns confident answers about the wrong people. Inputs that cannot be a complete international number are refused before the request, cost nothing, and are recorded as refusals rather than silently dropped.
- **Uses the authentication scheme that works everywhere.** This provider offers two, and the simpler one is refused by the risk scoring route with a complaint about the header format rather than about the key, which reads as a broken credential when it is nothing of the sort. The signed scheme is the default here for that reason.
- **Never charges for a miss.** Only rows carrying a resolved number are billed.

### Input

| Field | What it does |
| --- | --- |
| `mode` | Risk scoring, plain lookup, or live reachability. |
| `phones` | Numbers to look up, one per line. |
| `defaultCountryCode` | Calling code to assume for national-format numbers. |
| `addons` | Identity attributes to request alongside the standard answer. |
| `addonOptions` | JSON settings for attributes that take their own parameters. |
| `consentMethod` | How consent to query the number was obtained. |
| `consentTimestamp` | When consent was obtained. |
| `lifecycleEvent` | What the end user is doing. Sharpens the risk score. |
| `originatingIp` | The end user's IP address. Sharpens the risk score. |
| `recommendations` | Keep only allow, flag or block rows. |
| `riskLevels` | Keep only these risk levels. |
| `maxRiskScore` | Keep only rows at or below this score. |
| `phoneTypes` | Keep only these line types. |
| `countries` | Keep only these countries. |
| `excludeBlocklisted` | Drop blocklisted numbers. |
| `excludeProhibited` | Drop numbers you may not contact at all. |
| `requireReachable` | Keep only numbers reachable right now. |
| `authScheme` | Signed, which works everywhere, or basic. |
| `requestsPerMinute` | Pace the run under your plan's rate limit. |
| `skipNotFound` | Leave misses and refusals out of the dataset. |
| `maxResults` | Hard cap on rows, and therefore on spend and run time. |
| `clientId` | Your own customer id. Stored as a secret. |
| `apiKey` | Your own API key. Stored as a secret. |

### Example output

```json
{
  "found": true,
  "mode": "phone-intelligence",
  "query": "+14155552671",
  "referenceId": "0123456789ABCDEF0123456789ABCDEF",
  "phone": "14155552671",
  "phoneAsSubmitted": "+1 (415) 555-2671",
  "countryCallingCode": "1",
  "subscriberNumber": "4155552671",
  "callableNumber": "14155552671",
  "messageableNumber": "14155552671",
  "phoneTypeCode": 2,
  "phoneType": "MOBILE",
  "carrierName": "Verizon Wireless",
  "city": "San Francisco",
  "county": "San Francisco",
  "state": "CA",
  "postalCode": "94103",
  "countryName": "United States",
  "countryIso2": "US",
  "countryIso3": "USA",
  "latitude": 37.7749,
  "longitude": -122.4194,
  "timeZoneName": "America/Los_Angeles",
  "isBlocklisted": false,
  "blockCode": null,
  "isProhibited": false,
  "riskLevel": "low",
  "riskRecommendation": "allow",
  "riskScore": 120,
  "isLive": null,
  "statusCode": 300,
  "statusDescription": "Transaction successfully completed",
  "error": null,
  "raw": { }
}
```

### Frequently asked questions

#### What is the difference between the three modes?

Risk scoring returns everything the plain lookup returns and adds a risk level, a score and a recommendation, along with the reason codes behind them. The plain lookup returns the carrier, line type, location and any identity attributes you enabled, without the risk verdict, and costs less of your provider credits when the verdict is not what you need. Live status answers one narrow question: is this number reachable on the network at this moment, which is a different thing from whether it is valid.

#### What does the risk score actually tell me?

It aggregates phone number intelligence, traffic patterns, machine learning and a shared industry data set into a single number from zero to one thousand, a level from low to high, and a recommendation of allow, flag or block. The recommendation is the part most systems should route on, because it is already calibrated; the raw score is what you tune against when the default thresholds do not fit your own tolerance.

#### Why does the lifecycle event matter?

Because risk is contextual. The same number is unremarkable when it signs in to an account it has held for two years and quite interesting when it creates a new account and immediately transacts. Telling the provider which moment this check belongs to lets its model weigh the signals accordingly. It costs nothing and it is one field, and leaving it out is the most common way to get a score that feels vague.

#### What is the difference between blocklisted and high risk?

A high risk score is an opinion about the number. Blocklisting is a decision that has already been made, sometimes because your account is not permitted to contact that number at all. Treating those as the same thing means either ignoring a contractual restriction or throwing away numbers that are merely uncertain, so they are two columns here, with two filters.

#### Why does the actor return the number three times?

Because the provider does, and the differences matter. There is the number as you submitted it, the number cleansed into a form that can be dialled, and the number cleansed into a form that can be texted. Those are not always the same string, and a campaign that assumes they are will send to a number that cannot receive messages. Each comes with its own cleanse code saying what had to be changed.

#### What are the identity attributes and do I need to do anything to use them?

They are twelve separate products layered on the lookup: the name and address on the line, a match check against a name and address you already hold, subscriber status, number deactivation, porting history and live porting status, recent SIM swap, active call status, breach appearance, call forwarding and age verification. Most must be enabled on your account by the provider before they return anything, and several require you to record how you obtained the subscriber's consent to query the number. The consent record is only sent when you actually supply one, because an invented consent record is worse than none.

#### Why does the actor refuse some of my numbers before sending them?

Because a number written in national format is genuinely ambiguous. The provider addresses numbers in complete international form, and the same digits belong to different subscribers in every country sharing that pattern, so guessing produces confident answers about the wrong people. A number written with a plus or a leading double zero already carries its country code and is taken as given. A number starting with a trunk zero is national format, so without a default calling code there is nothing to resolve it against and it is refused. Anything else is read as already complete, but held to a ten digit minimum, because a seven digit string with no marker is a local number someone forgot to qualify. Refusals cost nothing and are written to the dataset as refusals, so a run that ignored a fifth of its input does not look like a run whose provider found nothing. Set a default calling code if your list is genuinely all from one country.

#### Which authentication scheme should I use?

Signed, which is the default. It builds a request signature from your API key and is accepted by every route. The basic scheme is accepted by the two lookup routes and refused outright by the risk scoring route, which complains that the authorization header is not in the correct format rather than that the key is wrong, and that error sends people hunting for a credential problem that does not exist. The option to switch is there, and the actor warns you if you pick basic for a scoring run.

#### Do I need my own API key?

Yes. This actor does not include data access. You use your own customer id and API key from Telesign, which is the provider whose phone intelligence API this actor calls, from their portal. These are two separate values and both are required: the customer id goes in `clientId` and the API key in `apiKey`, both stored as Apify secrets. Your own plan, credit balance and terms apply, and the identity attributes above are enabled per account rather than by default.

#### What happens when a number returns nothing?

An invalid or unreachable number is a verdict, not a miss: the provider telling you the line does not exist is the question you asked, so that row is written and billed like any other. A miss is a number the provider returned no record for at all, and a refusal is an input that never left this actor. Both are written with `found: false` and never billed. Set `skipNotFound` to true to leave them out of the dataset entirely.

#### How much does a run cost?

Pricing is pay per result: you are charged for each number resolved into the dataset, and never for misses, for locally refused inputs, for rows dropped by the filters, or for duplicates. Apify platform usage is included in the per-result price. Your own API provider's credit balance is separate and billed by them, and note that the identity attributes are charged individually by the provider on top of the base lookup.

### Keyword map

phone number intelligence API, phone number lookup API, carrier lookup, line type detection, mobile or landline check, HLR lookup, phone validation API, phone fraud detection, phone risk score, SMS fraud prevention, SIM swap detection, number porting check, call forwarding detection, number deactivation check, subscriber status, caller name lookup, reverse phone lookup, phone to address, phone geolocation, time zone by phone number, account takeover prevention, bot signup prevention, two factor authentication risk, KYC phone verification, phone reputation API, live phone status

# Actor input Schema

## `mode` (type: `string`):

Risk scoring returns everything the lookup returns plus a score and a recommendation. The plain lookup is cheaper on your provider credits when the risk verdict is not needed. Live status answers one question: is this number reachable on the network right now.

## `phones` (type: `array`):

Numbers to look up, one per line, in complete international form with the country code. A plus sign, a leading double zero, spaces, brackets and dashes are all fine. You can also give a JSON object with any of these fields: phone, externalId, accountId, deviceId, email, originatingIp. Numbers that cannot be a complete international number are refused before the request rather than sent and charged for, and are recorded as refusals.

## `defaultCountryCode` (type: `string`):

The calling code to put in front of numbers written in national format, such as 1 for North America or 44 for the United Kingdom. With this set, a number starting with a trunk zero has the zero replaced by the code. With it empty, a number starting with a zero is refused rather than guessed at, since a national number belongs to a different subscriber in every country that shares the pattern, and a number with no country code and fewer than ten digits is refused as well. Numbers written with a plus or a leading double zero are unaffected either way.

## `addons` (type: `array`):

Extra attributes to request alongside the standard answer, on the two lookup modes. Each one is a separate product with the provider and most must be enabled on your account before they return anything; some also require a record of consent. Leave empty for the standard answer only.

## `addonOptions` (type: `object`):

A JSON object of settings for the attributes that take their own parameters, merged into the request exactly as written. Use this for things like a name and address to match against, or an age threshold, without waiting for a named field. Anything named here is enabled whether or not it is ticked above.

## `consentMethod` (type: `integer`):

The provider's numeric code for how you obtained the subscriber's consent to query this number, from one to four. Some identity attributes will not return anything without it. Left off entirely unless a value is given here, because an invented consent record is worse than none.

## `consentTimestamp` (type: `string`):

The time consent was obtained, as an ISO 8601 timestamp. Sent alongside the consent method.

## `lifecycleEvent` (type: `string`):

The point in the account lifecycle this check belongs to. The risk model reads this: the same number carries a very different risk at account creation than it does at sign-in, and telling the provider which one you are in makes the score meaningfully better.

## `originatingIp` (type: `string`):

The IP address the end user came from, applied to every number that does not carry its own. Improves the risk score, since a number and an IP that disagree about geography is a signal in itself.

## `recommendations` (type: `array`):

Only keep rows the provider recommended this way. This is the fastest way to turn a scoring run into a block list or an allow list. Leave empty to keep every recommendation.

## `riskLevels` (type: `array`):

Only keep rows at these risk levels. Leave empty to keep every level.

## `maxRiskScore` (type: `integer`):

Only keep rows scoring at or below this on the provider's zero to one thousand scale. A finer instrument than the level, and the right one when you are tuning a threshold. Leave at zero to keep every score.

## `phoneTypes` (type: `array`):

Only keep rows whose line type matches one of these, written as the provider names them, such as Mobile or Landline. Useful when a campaign needs mobiles for messaging or landlines for a call list. Leave empty to keep every type.

## `countries` (type: `array`):

Only keep rows whose number resolves to one of these two-letter country codes. Leave empty to keep every country.

## `excludeBlocklisted` (type: `boolean`):

Leave out numbers the provider has blocklisted for any reason.

## `excludeProhibited` (type: `boolean`):

Leave out numbers the provider blocks by policy rather than by risk, meaning your account is not permitted to contact them at all. That is a different decision from a high score and it is kept as its own column.

## `requireReachable` (type: `boolean`):

In the live status mode, drop numbers that are not currently reachable on the network. Has no effect in the other two modes, since only the live status route answers that question and an absent answer is not a negative one.

## `authScheme` (type: `string`):

Signed builds a request signature from your API key and works on every route. Basic sends the credentials directly and is accepted by the lookup routes but refused by the risk scoring route, which complains about the header format rather than about the key. Leave this on signed unless you have a reason not to.

## `requestsPerMinute` (type: `integer`):

Pace the run so it stays under whatever rate your plan allows. Lower this if the provider starts rate limiting; raise it to finish a long list sooner.

## `skipNotFound` (type: `boolean`):

Leave numbers the provider returned nothing for, and inputs refused before they were sent, out of the dataset. Off by default, because knowing which of your numbers were unusable is usually the point. Misses are never charged for either way.

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

Stop after this many rows. This is the cap on both spend and run time.

## `clientId` (type: `string`):

Your own customer id for the phone intelligence API. This is a separate value from the API key and both are required; a run with only one of them will be rejected on every request. Stored as a secret.

## `apiKey` (type: `string`):

Your own API key for the phone intelligence API, which is the long encoded secret issued alongside the customer id. Stored as a secret.

## Actor input object example

```json
{
  "mode": "phone-intelligence",
  "phones": [
    "+14155552671"
  ],
  "addons": [],
  "recommendations": [],
  "riskLevels": [],
  "maxRiskScore": 0,
  "excludeBlocklisted": false,
  "excludeProhibited": false,
  "requireReachable": false,
  "authScheme": "signed",
  "requestsPerMinute": 600,
  "skipNotFound": false,
  "maxResults": 100
}
```

# Actor output Schema

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

One row per number, carrying the carrier, line type, city, region, country, time zone and coordinates, the risk score and recommendation, the blocklisting decision, and any identity attributes the run enabled.

# 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 = {
    "phones": [
        "+14155552671"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("nabeelbaghoor/phone-number-intelligence-api").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 = { "phones": ["+14155552671"] }

# Run the Actor and wait for it to finish
run = client.actor("nabeelbaghoor/phone-number-intelligence-api").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 '{
  "phones": [
    "+14155552671"
  ]
}' |
apify call nabeelbaghoor/phone-number-intelligence-api --silent --output-dataset

```

## MCP server setup

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

```

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/nhrchAc5S25TW3R9W/builds/G1v5DIbOPGeXdwkpl/openapi.json
