Confidence levels

Every API response includes a confidence field summarizing how reliable the validation is. Pick one threshold and apply it consistently in your code — that's the whole point.

The 5 labels

Label Score Meaning
verified 0.95 Mobile or fixed line in a regulated market with known carrier metadata. Highest trust.
likely 0.80 Valid format, plausible line type, no negative signal. Most common outcome.
uncertain 0.55 Ambiguous line type (premium, shared cost, UAN…) or country with low-quality numbering data.
low 0.20 VoIP / disposable / burner carrier, or matched a flagged prefix.
invalid 0.00 Format/parse failed or number not possible.

The numeric score is derived from the label — use whichever feels more natural in your code (confidence === "verified" reads better than score >= 0.95).

How we compute it

The pipeline applies signals in order, each one can lower the confidence (never raise it):

  1. line_type baseline

    • mobile / fixed_lineverified
    • fixed_line_or_mobile / toll_freelikely
    • voip / pager / voicemaillow
    • everything else → uncertain
  2. Carrier profile (DB lookup)

    • Carriers seeded as known disposable VoIP (Google Voice, TextNow, Hushed, Pinger, Burner, Bandwidth, Twilio, Plivo, Vonage, …) → forces low
    • Carriers we've manually overridden in admin (e.g. observed fraud) → uses the override label
    • Plain observed carriers without explicit signal → no effect (we don't penalize unknowns)
  3. Country cap

    • Some countries have notoriously inconsistent numbering plans → confidence is capped to likely even if line_type would say verified.
  4. Disposable prefix match

    • If the E.164 number starts with a flagged prefix in our disposable_phone_prefix table → forces low, sets is_disposable=true.

High-trust signups (banking, KYC-light)

if result["confidence"] != "verified" or result["is_disposable"]:
    reject("Need a verified mobile or landline number.")

Catches ~85% of fraud signals. Some legitimate users with fixed_line_or_mobile (US, BR…) will be likely and rejected — worth it for high-stakes contexts.

Standard signups (SaaS, e-commerce)

if result["confidence"] == "low" or result["is_disposable"]:
    flag_for_review(user)

Filters out throw-away VoIP / disposable numbers without rejecting legit US/CA fixed_line_or_mobile users.

Lead enrichment / contact cleaning

if result["confidence"] in {"verified", "likely"}:
    keep()
elif result["confidence"] == "uncertain":
    keep_with_warning()
else:
    drop()

Works for cleaning a CRM list — drop the obvious junk, keep everything plausible.

Diagnostics field

For debugging or audit logs, the response includes a diagnostics object showing why we landed on a given confidence:

"diagnostics": {
  "format":     { "parsed": true, "is_possible": true, "is_valid": true },
  "disposable": { "is_disposable": false, "reason": null, "matched_prefix": null },
  "confidence": {
    "line_type_baseline": "verified",
    "carrier_profile":    { "matched": true, "label": "verified", "applied": false, "samples": 12 }
  }
}

Read this when a user complains "why is my number flagged low?" — the answer is in there.

Don't trust marketing accuracy claims

Some phone validation APIs claim "99% accuracy". That's marketing — at format-only level (no HLR), the upper bound on accuracy is dictated by libphonenumber's metadata, which is open-source and the same for everyone. Anything above ~90-92% is implausible without true HLR queries against the network. We tell you what we know, mark what we don't, and let you decide.