Phone validation that never leaves your servers.

Format, line type, carrier, region and time zone for any number in 240+ countries — checked in microseconds, with no number ever sent to a third party. Bundled free with Acuris address validation.

Try it right now

Paste any phone number below — in E.164 (+…) or national form — and we’ll show the full validation response in <1 second. No signup, no card, public widget endpoint.

Demo is IP rate-limited. For production, grab a 100-credit dev key and call /validate-phone with header X-Acuris-Key.

What each result means

Every field the check above returns, and what it tells you about the phone number you entered.

verdict
Our overall result. valid — the number matches a real, assigned range for its country and line type, safe to use. possible — the length and shape are plausible but the number isn’t in an allocated range yet, so treat as format-only. invalid — it can’t be a real number (too short/long, bad country code, garbage). unknown — input was empty or the validator was unavailable.
score
A 0–10 contactability score. 10 = a valid mobile; 8–9 = valid landline / fixed-or-mobile; 6–7 = valid but lower-trust line types (VoIP, toll-free, premium); 3 = possible only; 0–1 = invalid. Lets your code branch simply, e.g. if score < 8: review.
valid
Boolean — whether the number is valid for its region under the official numbering plan. The single most important field for gating.
e164
The number normalised to E.164 (+<country><national>, e.g. +14155552671) — the canonical form to store and dial. international and national formats are returned too.
line_type
What kind of line it is: mobile, fixed_line, fixed_line_or_mobile, voip, toll_free, premium_rate, pager, and more. Drives whether SMS is even possible.
region
The ISO‑3166 country the number belongs to (e.g. US, DE) — derived from the number itself, not the caller’s guess.
carrier
Best-effort original carrier from the numbering-plan block (e.g. T-Mobile). Offline and block-level, so it’s the assigned carrier, not a live ported lookup — blank when unknown.
location
A human-readable geographic description for the number (e.g. San Francisco, CA or Berlin), where the plan supports it.
timezones
The IANA time zone(s) the number maps to (e.g. Europe/Berlin) — useful for send-time and call-window logic.
can_receive_sms
true when the line can receive SMS (mobile or fixed-or-mobile). Gate your OTP / text sends on this so you never burn a send on a landline.
tel_uri
The number as an RFC 3966 tel: URI (tel:+1-415-555-2671) — drop straight into a click-to-call link.
is_premium_rate / is_toll_free
Cost-class flags — a premium-rate or toll-free line where you might expect a normal mobile. Catches mis-entered or costly numbers.
is_short_number / is_emergency
The input is a short code (112, 999, …) or a known emergency number — usually junk in a contact field, so you can reject it with a clear reason.
national_number / area_code
The national significant number (digits after the country code) and, where the plan has one, the area / destination code — handy for routing and analytics.
is_vanity
The input contained letters (e.g. 1-800-FLOWERS); we convert it to digits before validating.
valid_for_input_region / short_number_cost
If you passed a region, whether the number is actually valid for that country (false = it belongs elsewhere). For short codes, the cost class: toll_free / standard_rate / premium_rate.
dialling
When you pass dial_from, how to dial this number from that country (e.g. a US number from DE → 00 1 415-555-2671).
is_possible
Whether the number is even a plausible length/shape for its region — a cheaper pre-check than full validity.
reasons
Machine-readable codes behind the verdict (e.g. too_short, invalid_country_code, not_in_assigned_range, no_default_region) — for your code to act on programmatically.

What this checks — and what it doesn’t

Honesty first: this is Tier 1 validation — everything is computed offline from the global numbering plan, with no network call and no per-lookup cost. It confirms a number is well-formed and valid; it does not place a call or check whether the handset is switched on right now. Here’s exactly where the line sits.

What you get (offline, Tier 1)What needs a live add-on (Tier 3)
✅ Valid for region & numbering plan ☎️ Live reachability / HLR (is it switched on now)
✅ E.164 + international + national formats 📲 Current carrier after a port (real-time MNP)
✅ Line type (mobile / landline / VoIP / …) 🪪 Caller name (CNAM) lookup
✅ Region, geo description, time zone(s) 🚩 Fraud / SIM-swap / disposable-number scoring
✅ Assigned (block-level) carrier, 240+ countries

Why offline-only? Tier 1 catches the overwhelming majority of bad data at point of entry — typos, wrong country, impossible lengths, landlines typed into a “mobile” field — with zero latency, zero per-call cost, and no PII leaving your stack. That’s what most address-validation buyers actually need, and unlike the big suites we don’t bill it as a separate SKU.

Need live reachability? HLR / active-line lookups require a telecom-data relationship and carry a real per-lookup cost, so they ship as a paid add-on rather than being baked into the free bundle. Talk to us if you need it.

Every check, every number, every time

#️⃣

E.164 normalisation

Any input — spaces, dashes, parentheses, national or international — comes back as a clean +<cc><nsn> string to store and dial.

Numbering-plan validity

Confirms the number is actually assigned for its region, not just plausible — checked against the official global numbering plan.

📱

Line-type detection

mobile / fixed_line / VoIP / toll_free / premium — so you only SMS numbers that can actually receive one.

🏷️

Carrier (offline)

Assigned, block-level carrier from the numbering plan (e.g. T-Mobile) — no network call, no per-lookup fee.

🌍

Region & geo

ISO country plus a human-readable location (San Francisco, CA · Berlin), derived from the number itself.

🕑

Time zones

IANA zone(s) for the number — drive send-time windows and respect quiet hours automatically.

🌐

240+ countries

One API, the whole world. Pass a default region for national-format numbers; E.164 inputs need nothing.

📊

0–10 score

Single numeric contactability score so your code can if score < 8: review. Sortable, comparable.

Where teams put it to work

🛒

Checkout & signup forms

Catch a mistyped or wrong-country number the moment it’s entered, and store it normalised to E.164 — so the “out for delivery” SMS actually reaches the customer instead of bouncing.

🔐

OTP & 2FA send-gating

Before firing a one-time passcode, confirm the line is a mobile. Landlines and many VoIP numbers can’t receive SMS — skip them or fall back to voice instead of burning a send and stalling the login.

🧹

CRM & list hygiene

Bulk-validate an imported contact list: drop impossible numbers, normalise everyone to E.164, and flag landlines sitting in a “mobile” column — before you pay to message any of them.

📞

Compliant outbound dialling

Use each number’s region and time zone to schedule calls inside the contact’s local business hours — respecting quiet-hours rules — and route every lead to the right regional team.

🛡️

Cheap fraud first-filter

Reject obviously bogus numbers at registration — wrong country code, impossible length, a premium-rate line where you expected a mobile — a zero-cost first pass before you pay for a reachability or risk lookup.

💬

WhatsApp & messaging onboarding

Messaging APIs demand clean E.164 mobile numbers. Validate and normalise before adding a contact to a WhatsApp Business, RCS or SMS audience, so the broadcast doesn’t silently drop recipients.

Three lines, any language

A one-line curl, or the same call straight from Node and Python — clean JSON, identical on every tier.

curl -X POST 'https://api.acuris-geo.com/validate-phone' \
  -H 'X-Acuris-Key: YOUR_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"phone": "+1 415 555 2671"}'

# → {
#     "phone": "+1 415 555 2671",
#     "verdict": "valid",
#     "score": 9,
#     "e164": "+14155552671",
#     "line_type": "fixed_line_or_mobile",
#     "region": "US",
#     "location": "San Francisco, CA"
#   }
const res = await fetch("https://api.acuris-geo.com/validate-phone", {
  method: "POST",
  headers: { "X-Acuris-Key": process.env.ACURIS_API_KEY, "Content-Type": "application/json" },
  body: JSON.stringify({ phone: "+1 415 555 2671" }),
});
const r = await res.json();

console.log(r.verdict, r.score, r.e164, r.line_type);
// → "valid" 9 "+14155552671" "fixed_line_or_mobile"
import os, requests

r = requests.post(
    "https://api.acuris-geo.com/validate-phone",
    headers={"X-Acuris-Key": os.environ["ACURIS_API_KEY"]},
    json={"phone": "+1 415 555 2671"},
).json()

print(r["verdict"], r["score"], r["e164"], r["line_type"])
# → valid 9 +14155552671 fixed_line_or_mobile

Bundled free into every Acuris plan

Phone validation has its own credit pool, sized to match your address-validation quota — buy an address plan, get phone validation at no extra charge. 1,000 free on the Dev trial; your monthly address volume, mirrored, on Growth and Pro. The big suites bill this as a separate SKU; we don’t.