Trial abuse is not usually a thousand people taking one trial each. It is a handful of people taking a thousand trials, which means the whole problem is linkage: recognising that the twelfth signup is the same person as the first. That is a different job from scoring a single signup in isolation.
An abuser varies whatever is cheap and reuses whatever is expensive. Email addresses are free, so those change every time. Device fingerprints, IP ranges and phone numbers cost money or effort, so those repeat. Look for repetition in the expensive things.
Gmail ignores dots and anything after a plus sign, so user+1@gmail.com through user+400@gmail.com are all one mailbox. Normalising the address collapses a hundred trials into one visible account, and it costs nothing to implement.
curl "https://www.layercall.com/v1/verify/email?email=user%2Btrial7@gmail.com" \ -H "X-Api-Key: YOUR_KEY" # normalized_email collapses the alias back to the real mailbox
A browser fingerprint survives a new email address and a fresh incognito window, which is exactly what a repeat trialist changes between attempts. It is not perfect and it should not be your only check, but it links accounts that otherwise look unrelated.
Requiring a card from everyone stops abuse and also stops your growth. Requiring a phone number or a card only from signups that already look linked keeps the front door open for the majority who are exactly who they say they are.
const trust = await lc.scoreUser({ ip, email, device_id }); if (trust.verdict === "block") return requireCard(); if (trust.verdict === "review") return requirePhoneVerification(); return startTrial();
Every check here has a cost in real customers. These are the ones that actually bite — several because we shipped them ourselves.
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.