Use case

Account security and takeover risk

Check the phone number or e-mail address a user adds or changes on an account, and step up verification when the signals suggest takeover risk.

Last updated

A phone shows masked results next to cards reading encrypted at rest, masked in logs, no reverse lookups and data retention 30 days.
Only a result, never a profile: encrypted at rest, masked in logs, no reverse lookups.

Account takeovers often begin with a change of contact details: the attacker adds their own 2FA number or swaps the recovery e-mail. Checking the new number and address at that moment gives your security logic facts to weigh: line type, live reachability, whether the mailbox exists and, where enabled, spam reputation. Risky changes get an extra step.

Where does contact-detail risk show up?

Attackers who get into an account usually try to keep it. That means changing where codes and reset links go. Warning signs include:

  • A new 2FA number that is a VoIP line. Legitimate users have VoIP numbers too, but attackers often use them because they're cheap and easy to get. The carrier lookup reports line_type. See VoIP number.
  • A new number that isn't assigned or can't be reached. The HLR lookup asks the home network whether the number is reachable, unreachable or invalid (not assigned). A 2FA number that can't receive codes is a problem whoever added it.
  • A recovery e-mail with no mailbox behind it. An address at a major webmail provider that doesn't exist can't receive recovery mail. That points to a typo or a throwaway. The e-mail mailbox check answers this.
  • A number with fraud or spam reports. Where spam reputation is enabled (limited access), reasons such as reason_unassigned or reason_regulator are worth weighing.
  • A change of country. The carrier lookup's country can differ from the account's usual country.

Each signal is weak on its own. A combination, together with your own session data such as a new device, a new IP range or a recent password reset, is what should trigger a step-up.

Why do contact changes deserve their own checks?

Most login defences look at the session: device, location, behaviour. A contact change is different because it has lasting effects. Once an attacker controls the 2FA number or recovery address, they can survive a password reset by the real owner. That is why guidance such as NIST SP 800-63B treats phone-based out-of-band authentication as a restricted method and expects verifiers to consider the risk of a number being changed or ported. The checks on this page don't replace stronger authenticators such as passkeys. They make the change itself harder to abuse.

We don't offer SIM-swap detection. For what SIM swaps look like and which signals exist elsewhere, see SIM swap.

How does the workflow look?

  1. The user submits a new phone number or e-mail address.
  2. Call POST /v1/lookup with the new identifiers and checks: ["carrier", "spam", "email"]. Leave out spam if it isn't enabled for your account, and add hlr for 2FA numbers.
  3. Combine the results with your session risk and apply the table below.
  4. For a step-up, confirm through the old number or e-mail, or hold the change for a cooling-off period.
  5. Notify the old contact details that a change was made, whatever the outcome.
  6. Log the decision with checked_at. Keep the raw response only as long as you need it.

The request is small (one number, one address), so it fits in the synchronous part of a settings change. Use a short wait, such as 5 seconds; anything still pending counts as unknown for the decision.

Which checks answer which question?

CheckWhat it answersWhen to run itLink
carrierMobile, VoIP or fixed line? Which country?New or changed phone numberCarrier lookup
hlrIs the new mobile number assigned and reachable? Was it ported?New 2FA numberHLR lookup
emailDoes the new mailbox exist at a major webmail provider?New or changed recovery e-mailE-mail mailbox check
spam (limited access)Is the number in spam or fraud reports?New phone number, where enabledSpam reputation

What should you do with each result?

SignalsSuggested action
Mobile line, mailbox exists, no reportsAllow the change
line_type: voip and the session is otherwise normalAllow, and notify the old contact details
line_type: voip and a new device or locationStep up: confirm through the old number or e-mail
HLR status: invalid for a new 2FA numberRefuse the number; it can't receive codes
Mailbox registered: falseAsk the user to check the address; don't make it the recovery e-mail
Spam risk_level: high or reason_unassigned: trueStep up, and hold the change for review
Checks unknownDecide on your other signals; unknown is not negative and is not charged

What does a request look like?

Test mode, with a number that has no carrier data and no reports, and an address with an existing mailbox:

curl https://api.mobilevalidate.com/v1/lookup \
  -H "Authorization: Bearer mv_test_publicSandboxn9ZgneuhR1B9CRfKG3fulym" \
  -H "Content-Type: application/json" \
  -d '{"numbers":["+447700900002"],"emails":["registered@test.mobilevalidate.com"],"checks":["carrier","spam","email"]}'

Runs as pasted with the public sandbox key, which answers the test values only. In the SDKs, omit the key to use MOBILEVALIDATE_API_KEY.

Response (excerpt, test mode: the checks of the phone row, then of the e-mail row):

JSON
[
  {
    "network.carrier": {
      "service": "network.carrier", "status": "unknown", "registered": null, "attributes": null,
      "confidence": null, "confidence_score": null, "checked_at": null,
      "cached": false, "age_seconds": null, "billed": false, "reason": "NO_DATA", "poll_after_ms": null
    },
    "number.spam": {
      "service": "number.spam", "status": "completed", "registered": true,
      "attributes": {
        "risk_level": "no_reports", "risk_score": 0, "reason_regulator": false, "reason_government": false,
        "reason_community": false, "reason_unassigned": false, "voip_range": false, "sources": 0
      },
      "confidence": "high", "confidence_score": 0.99, "checked_at": "2026-09-25T14:28:26.024Z",
      "cached": false, "age_seconds": 0, "billed": false, "reason": null, "poll_after_ms": null
    }
  },
  {
    "email.valid": {
      "service": "email.valid", "status": "completed", "registered": true, "attributes": null,
      "confidence": "high", "confidence_score": 0.99, "checked_at": "2026-09-25T14:28:26.024Z",
      "cached": false, "age_seconds": 0, "billed": false, "reason": null, "poll_after_ms": null
    }
  }
]

The carrier answer is unknown (NO_DATA) and free. The rest of the decision rests on the spam level, the mailbox result and your own session signals.

How much does it cost?

Each check on each identifier is billed only when the answer is conclusive. You're not charged for inconclusive results (unknown, unsupported country, timeout, invalid, duplicate). Contact-detail changes are rare events, so this use case usually costs very little: a few real-time checks per change. The HLR lookup is billed per conclusive answer. Spam reputation bills every level, including no_reports, and is free for numbers from sanctioned countries and regions, which are never checked. See pricing for current rates.

What limits and rules apply?

The usual lookup limits apply: up to 100 identifiers and 20 checks per request, rate limits per key and a daily cap per account. Requests that look like sequential number ranges or generated e-mail lists are rejected. E-mail checks return only yes, no or unknown, never names or profiles.

Check only the contact details your users give you for their own accounts, and say in your privacy notice that you do so for security. Results support a step-up decision. They are not identity proof, and on their own they shouldn't lock people out of their accounts. Give every flagged user a way through, such as support review. Keep what you store small: the decision, the signals and checked_at. People can object through the opt-out form.

What are the common mistakes?

  • Locking accounts on one signal. A VoIP number or a new country is common among genuine users. Step up instead.
  • Checking only at sign-up. Takeovers happen later. The contact-change moment is when checks matter most.
  • Not notifying the old contact. Telling the previous number or address about a change is the cheapest takeover alarm there is.
  • Treating no_reports as safe. It means we hold no reports, not that the number is trustworthy.
  • Treating unknown as negative. It is free and neutral; decide on your other signals.

Frequently asked questions

When should I run these checks?

At moments when attackers change contact details: a new phone number for two-factor authentication, a changed recovery e-mail, a password reset to a new destination or a first login from a new device.

Should I lock the account when a signal looks risky?

Usually not. Ask for an extra verification step instead, such as confirming on the old number or e-mail, a delay before the change takes effect, or a support review. Locking hurts genuine users who just changed phones.

Does the mailbox check read or send e-mail?

No. It only answers whether the mailbox exists, for major webmail providers. Nothing is sent to the address and no mailbox content is accessed.

Can I use these checks as identity verification?

No. They are risk signals about a number or an address, not proof of who someone is, and must not be used as KYC or for credit, employment, housing or insurance decisions.

Is spam reputation part of this?

It can be, where it is enabled. Spam reputation is in limited access (approved customers only) and accepts numbers from all countries except sanctioned ones.

Do you detect SIM swaps?

No. We don't offer SIM-swap detection. The HLR lookup tells you whether a new 2FA number is assigned and reachable and whether it was ported, which is a different question.

What does an unknown answer mean for a contact change?

No conclusive answer, for example after a timeout. It is free and neutral: decide on your other session signals rather than blocking the change.

Should I store the lookup response with the account?

Store the decision, the signals that drove it and checked_at. Keep the raw response only as long as you need it for review, and say in your privacy notice that you check contact changes for security.

All articles

Know before you send.

Create a free account: approved automatically, usually within 10–15 minutes.

Free to start: check 20 phone numbers and 10 e-mail addresses for real on your account, plus an unlimited test mode. You pay only for conclusive answers, from $20.