Phone Number Lookup API — Carrier, Line Type, Validation avatar

Phone Number Lookup API — Carrier, Line Type, Validation

Pricing

from $25.00 / 1,000 phone number looked ups

Go to Apify Store
Phone Number Lookup API — Carrier, Line Type, Validation

Phone Number Lookup API — Carrier, Line Type, Validation

Validate phone numbers in bulk and get E.164 format, country, region, carrier and line type, plus fraud score and owner name where available. Each row shows its lookup status, and rows without carrier data are not billed.

Pricing

from $25.00 / 1,000 phone number looked ups

Rating

1.0

(3)

Developer

Khadin Akbar

Khadin Akbar

Maintained by Community

Actor stats

1

Bookmarked

898

Total users

47

Monthly active users

4 days ago

Last modified

Share

What is Phone Number Lookup API?

Phone Number Lookup API validates a list of exact phone numbers and returns one dataset record per number: E.164 and national formats, country, region, numbering-plan line type, timezones, and carrier plus line type from a managed carrier lookup. Fraud score, DNC status and owner name are added when the supplemental risk lookup returns them. Every row carries a lookupStatus (enriched, format_only, invalid or error) and a charged flag, so you can see which rows were billed.

Price: the phone-looked-up event is charged only for enriched rows (carrier or line type landed). Format-only, invalid and error rows are saved free. Apify platform usage is included. See the Pricing tab for the current event price.

Best fit for this Actor

  • Start with a list of exact phone numbers when you need validation, E.164, country, and carrier/line type in one record per number.
  • Use defaultCountry when local-format numbers need an ISO country fallback.
  • Turn on requireCarrier when a batch is only useful with carrier data, so a carrier outage stops the run with a clear message instead of returning format-only rows.
  • Continue with a listing or CRM export Actor when you first need to collect public phone fields, then pass those values into phoneNumbers.

This Actor is designed as a focused standalone workflow: you already have phone numbers and want structured lookup rather than a people-search query.

Workflow: from a messy CRM phone column to a routing decision

A RevOps operator starts with a CRM column of mixed E.164 and US local numbers. They submit the array with defaultCountry: "US", keep WhatsApp off, and read one dataset row per number. valid and e164 decide whether the row can enter a dialer; lineType and carrier separate mobile from landline; lookupStatus tells them which rows have carrier data; fraudScore is used only when it is present. Format-only rows still give usable numbering-plan data and are not billed.

Quick start input

{
"phoneNumbers": [
"+14155550100",
"(415) 555-0101"
],
"defaultCountry": "US",
"maxConcurrency": 10,
"includeReverseLookupUrls": true,
"includeTelegramCheck": true,
"includeWhatsAppCheck": false,
"requireCarrier": false,
"maxCostUsd": 5
}

phoneNumbers is exact-match lookup, not a search box. maxCostUsd stops billed phone-looked-up events; free rows stay outside that cap.

Input reference

FieldTypeWhat it controls
phoneNumbersarray of stringsExact numbers to look up (required; free Apify plan: first 25 per run; paid plans: existing 10,000 maximum). Prefer E.164.
defaultCountrystringISO 3166-1 alpha-2 fallback for local formats. Default US.
maxConcurrencyintegerParallel lookups, 1–100. Default 20.
includeReverseLookupUrlsbooleanPre-built manual investigation URLs. Default true. No extra event charge.
includeTelegramCheckbooleanPublic Telegram profile via t.me only. Default true.
includeWhatsAppCheckbooleanExperimental wa.me format check, not registration. Default false.
maxCostUsdintegerRun-level cap on billed phone-looked-up events. 0 means no cap.
requireCarrierbooleanStop with a clear status when carrier lookup is unavailable. Default false.

Row status and billing

lookupStatusMeaningcharged
enrichedCarrier or line type landed from the carrier lookuptrue (one phone-looked-up event)
format_onlyValid number, but no carrier data for itfalse
invalidNumber failed libphonenumber validationfalse
errorThe lookup failed for this numberfalse

What data you receive

One dataset item represents one input phone number. Useful fields: valid, e164, countryCode, lineType, carrier, timezones, fraudScore, dncStatus, name, lookupStatus, charged, sources, warnings, lookedUpAt. Example (a subset of a real enriched row):

{
"inputNumber": "+919876543210",
"valid": true,
"e164": "+919876543210",
"national": "098765 43210",
"countryCode": "IN",
"countryName": "India",
"lineType": "mobile",
"isMobile": true,
"carrier": "Bharti Airtel",
"active": true,
"fraudScore": 100,
"dncStatus": "not_on_dnc_registry",
"telegram": {
"registered": false,
"publicName": null,
"publicPhotoUrl": null
},
"lookupStatus": "enriched",
"charged": true,
"sources": [
"libphonenumber",
"carrier_lookup_primary",
"telegram",
"reverse_lookup_urls"
],
"warnings": [],
"lookedUpAt": "2026-10-05T15:37:09.641Z"
}

sources lists what contributed to the row: libphonenumber (validation and formatting), carrier_lookup_primary or carrier_lookup_secondary (carrier and line type), telegram, whatsapp and reverse_lookup_urls. city for US/CA can come from an area-code map even on format-only rows. That is not live HLR.

Use through the API

Replace <APIFY_TOKEN> with your Apify API token. Never paste the token into Actor input.

curl -X POST "https://api.apify.com/v2/acts/khadinakbar~phone-number-lookup-api/runs?waitForFinish=120" \
-H "Authorization: Bearer <APIFY_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"phoneNumbers":["+14155550100"],"defaultCountry":"US","includeWhatsAppCheck":false}'

Read the dataset once the Actor has finished:

curl "https://api.apify.com/v2/datasets/<DATASET_ID>/items" \
-H "Authorization: Bearer <APIFY_TOKEN>"

JavaScript client using the same Actor identity:

import { ApifyClient } from "apify-client";
const client = new ApifyClient({
token: process.env.APIFY_TOKEN
});
const run = await client.actor("khadinakbar/phone-number-lookup-api").call({
phoneNumbers: ["+14155550100"],
defaultCountry: "US",
includeWhatsAppCheck: false
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);

Inspect OUTPUT and RUN_SUMMARY in the default key-value store for outcome, enriched and format-only counts, and charged events.

Use with AI agents through Apify MCP

Look up these exact phone numbers with khadinakbar/phone-number-lookup-api: ["+14155550100"]. Return e164, valid, countryCode, carrier, lineType, lookupStatus, charged and warnings. Keep WhatsApp off. Then read the dataset and the OUTPUT outcome. Treat rows with lookupStatus "format_only" as numbering-plan data without carrier, not as live HLR, CNAM or WhatsApp registration.

Starting information: an array of exact phone numbers and an optional defaultCountry. The Actor is read-only; it does not place calls or send messages. Expected output is one dataset record per number plus OUTPUT/RUN_SUMMARY. Provenance is in sources and lookedUpAt. Cost unit is phone-looked-up, only on enriched rows; cap with maxCostUsd. Configure the hosted MCP connector at https://mcp.apify.com.

Connect the workflow

When you already collected public listing phones, start with Practo Doctor Scraper or Naver Map Scraper — Korean Places, Phone & Menus, then pass the phone field into this Actor as phoneNumbers.

Pricing

This Actor uses Pay per event with Apify platform usage included. Open the Pricing tab on the Actor page for current event prices.

  • phone-looked-up is charged once per enriched row (carrier or line type landed).
  • format_only, invalid and error rows are saved without that event.
  • A small apify-actor-start event applies to each run.

Worked example: 1,000 numbers where 800 come back enriched and 200 format_only means 800 phone-looked-up events.

Free Apify plan

Runs started from an Apify free plan process the first 25 non-empty phone-number inputs per run. Every supported mode and output field remains available with the same lookup quality. Extra inputs are not sent to lookup services and are not charged. The dataset contains the completed rows, and the status message plus OUTPUT and RUN_SUMMARY explain when this limit is reached.

Paid Apify plans keep the existing 10,000-number input limit, concurrency options, spending controls, and event prices. This is a per-run allowance, not a monthly allowance. Format-only, invalid, and error rows still have no phone-looked-up event; the existing startup event applies.

Call over HTTP

For a short lookup, open Endpoints and copy this Actor's server URL. Send POST /lookup with the same JSON input fields used by a normal run and your Apify API token in Authorization: Bearer. Read GET /openapi.json for the endpoint schema. GET / checks readiness without a lookup or lookup event.

# Set ACTOR_STANDBY_URL to the server URL shown in Endpoints.
curl --request POST "$ACTOR_STANDBY_URL/lookup" \
--header "Authorization: Bearer $APIFY_TOKEN" \
--header "Content-Type: application/json" \
--data '{"phoneNumbers":["+447911123456"],"includeTelegramCheck":false}'

The response contains outcome, message, results and summary. Every result has the same fields as a batch row, including lookupStatus and charged. Free Apify callers receive up to 25 non-empty inputs per HTTP request; extra inputs are excluded before lookup. Paid callers retain the existing 10,000-input maximum and options. There is a 1 MiB HTTP body limit, at most 256 characters per input, and a four-minute processing window. Large requests may return PARTIAL; use normal batch runs for long jobs, persistent Datasets and exports. Existing batch inputs and normal saved tasks continue to work.

HTTP results use the existing phone-looked-up price per enriched result, with platform usage included. Format-only, invalid and error rows have no lookup event. maxCostUsd limits the enriched event total for that request. The existing startup event remains part of normal run pricing; it is not added as a new manual HTTP-request charge. Billing is attributed to the authenticated caller before the response. If billing cannot be confirmed, the API returns BILLING_UNCONFIRMED without results: do not automatically retry it, because a timed-out charge can be ambiguous.

Responses are returned directly to the caller. The service does not save callers' phone numbers or results in a shared Dataset or key-value record. Save your response if you need an export or history. Busy responses use HTTP 503 with Retry-After; input errors return 400/413/415, missing authentication returns 401, and required upstream or billing failures return 502. A per-request allowance does not impose a monthly quota or prevent repeated requests.

Best results

  • Provide E.164 numbers when you have them; set defaultCountry only for local formats.
  • Keep maxConcurrency at the default 20 unless you have a reason to go lower.
  • Leave includeWhatsAppCheck false unless you explicitly want an experimental format flag.
  • Set maxCostUsd on bulk runs so billed enrichment stays within your budget.
  • Filter on lookupStatus before treating carrier or fraudScore as present.

Builder's note

I learned while testing live runs that format-only rows were being billed whenever the carrier lookup had no data. The Actor now charges the event only when carrier or line type actually lands, and each row says so in lookupStatus and charged.

FAQ

Does every field fill on every number? No. Validation, E.164 and timezones come from libphonenumber. Carrier and line type come from the carrier lookup when it responds. Fraud score, DNC status, active and owner name appear only when the supplemental risk lookup returns them; otherwise they stay null.

Is this live HLR or CNAM? No. Carrier and line type are numbering-plan data, not a live HLR snapshot or current-carrier portability check.

Why was a valid number not charged? Its lookupStatus is format_only: the number is valid but no carrier data landed, so the row is free. Check warnings on that row for the reason.

What happens when carrier lookup is unavailable? By default the Actor keeps going and saves free format_only rows. With requireCarrier: true it stops early and reports the reason in the run status and OUTPUT.

What does the WhatsApp check actually do? An experimental unauthenticated wa.me format check, default off. It is not WhatsApp registration status.

What does the Telegram check actually do? Public-profile detection via t.me. Private Telegram accounts correctly return registered: false.

Responsible use

This Actor returns public numbering-plan data plus carrier and line type. It is not a substitute for consent, TCPA, or other legal advice. Use it only for lawful purposes, and keep it away from harassment, unlawful outreach, or inferring sensitive personal data. Respect applicable privacy, telecom, and consumer-protection rules in your jurisdiction.