Phone Number Lookup API — Carrier, Line Type, Validation
Pricing
from $25.00 / 1,000 phone number looked ups
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
Maintained by CommunityActor stats
1
Bookmarked
898
Total users
47
Monthly active users
4 days ago
Last modified
Categories
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
defaultCountrywhen local-format numbers need an ISO country fallback. - Turn on
requireCarrierwhen 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
| Field | Type | What it controls |
|---|---|---|
phoneNumbers | array of strings | Exact numbers to look up (required; free Apify plan: first 25 per run; paid plans: existing 10,000 maximum). Prefer E.164. |
defaultCountry | string | ISO 3166-1 alpha-2 fallback for local formats. Default US. |
maxConcurrency | integer | Parallel lookups, 1–100. Default 20. |
includeReverseLookupUrls | boolean | Pre-built manual investigation URLs. Default true. No extra event charge. |
includeTelegramCheck | boolean | Public Telegram profile via t.me only. Default true. |
includeWhatsAppCheck | boolean | Experimental wa.me format check, not registration. Default false. |
maxCostUsd | integer | Run-level cap on billed phone-looked-up events. 0 means no cap. |
requireCarrier | boolean | Stop with a clear status when carrier lookup is unavailable. Default false. |
Row status and billing
lookupStatus | Meaning | charged |
|---|---|---|
enriched | Carrier or line type landed from the carrier lookup | true (one phone-looked-up event) |
format_only | Valid number, but no carrier data for it | false |
invalid | Number failed libphonenumber validation | false |
error | The lookup failed for this number | false |
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-upis charged once perenrichedrow (carrier or line type landed).format_only,invalidanderrorrows are saved without that event.- A small
apify-actor-startevent 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
defaultCountryonly for local formats. - Keep
maxConcurrencyat the default 20 unless you have a reason to go lower. - Leave
includeWhatsAppCheckfalse unless you explicitly want an experimental format flag. - Set
maxCostUsdon bulk runs so billed enrichment stays within your budget. - Filter on
lookupStatusbefore treatingcarrierorfraudScoreas 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.