← All guides

How to stop fake signups

If fake accounts appeared overnight, you are almost certainly being hit by one script rather than many people. That is good news: scripted signups reuse infrastructure, and infrastructure is measurable. Here is the order to check things in, cheapest first.

  1. 1

    Check the email domain before you check anything else

    Disposable-address providers are the cheapest thing an attacker uses and the cheapest thing for you to detect. There are roughly 8,000 known throwaway domains, and a scripted signup run will lean on a handful of them. This single check typically removes most of the volume, and it never touches a real customer — nobody signs up for a product they intend to pay for using a ten-minute mailbox.

    curl "https://www.layercall.com/v1/verify/email?email=test@mailinator.com" \
      -H "X-Api-Key: YOUR_KEY"
  2. 2

    Then look at where the request came from

    A datacenter IP address means the request did not come from a phone or a home broadband connection — it came from a rented server. Real customers occasionally arrive via VPNs, so treat this as a signal rather than a verdict. But a burst of signups from one hosting provider inside an hour is not a coincidence.

    curl "https://www.layercall.com/v1/score/ip?ip=1.2.3.4" \
      -H "X-Api-Key: YOUR_KEY"
  3. 3

    Check how old the email's domain is

    Attackers who move past disposable providers register their own domains, and they use them immediately. A domain registered three days ago that is already signing up for your product is worth a second look. Be careful here: real founders launch on week-old domains too, which is why this should raise a challenge rather than a block.

  4. 4

    Score the whole signup, not each field separately

    The signals matter together. A disposable email is worth a challenge. A disposable email from a datacenter IP on a domain registered yesterday is not ambiguous. One call scores every field and returns a single verdict, so you write one branch instead of five.

    const trust = await fetch("https://www.layercall.com/v1/score/user", {
      method: "POST",
      headers: { "X-Api-Key": process.env.LAYERCALL_API_KEY, "Content-Type": "application/json" },
      body: JSON.stringify({ ip, email, phone }),
    }).then(r => r.json());
    
    if (trust.verdict === "block")  return reject();
    if (trust.verdict === "review") return sendOneTimeCode();
    return createAccount();
  5. 5

    Send a code — do not slam the door

    This is the part most teams get wrong under pressure. Blocking outright turns every false positive into a lost customer who never tells you why they left. A one-time code costs a real person eight seconds and costs a script the entire attack, because the script has no mailbox to receive it. Reserve outright rejection for the cases where several strong signals agree.

What this gets wrong

Every check here has a cost in real customers. These are the ones that actually bite — several because we shipped them ourselves.

  • Blocking every VPN user. Plenty of privacy-conscious people, remote workers on corporate VPNs, and entire countries browse this way. VPN use is a reason to verify, not to refuse.
  • Treating an unmeasured signal as a negative. If a domain's registration date could not be determined, that is unknown — not "old and therefore fine". We shipped this bug ourselves and wrote it up.
  • Blocking shared IP addresses. University networks, mobile carriers and offices put thousands of unrelated people behind one address. Punishing that address punishes all of them.
  • Scoring the wrong device. If you fingerprint the browser, make sure you are reading the end user's user agent and not your own server's HTTP client — we made exactly this mistake and every request looked identical.

Try it before you integrate

Paste any IP, email, phone or domain into the playground — no signup. Or get a free API key for 1,000 lookups a month, no card.