All articles Bot detection

Waitlist and Beta Signup Bots: Keeping Launch Lists Real

A waitlist looks like the least interesting form on the internet. Email in, “you’re on the list” out. Then you add a referral leaderboard (“move up 10 spots for every friend who joins”), the launch gets some attention, and the top of your list fills with people who apparently have hundreds of friends, all of whom signed up within minutes of each other, many with addresses at the same obscure domain.

Waitlist gaming wastes more than a database table. It skews the demand signal you’re using to plan the launch, hands your earliest invites to people who won’t use the product, and turns a referral program into a bot leaderboard. This post covers how it happens and how to keep the list real.

Why waitlists get gamed

  • Referral ranking. If position depends on referrals, every fake signup credited to you is a step up. Scripts that submit the form with your referral code are trivial to write.
  • Scarce access. For a hyped beta, early invites can be resold or used to claim usernames, handles or limited perks.
  • Launch rewards. “First 1,000 get lifetime pricing” turns the list into a race.
  • Vanity. Some people just want to be number one.

The work is cheap. The form has no password, no email verification at submit time, and usually no rate limit.

What gaming looks like in the data

Most fake waitlist entries fall into two groups.

Scripted floods. A loop posts the form with generated or catch-all emails and the attacker’s referral code. Tells: submissions seconds apart, headless browsers or plain HTTP clients, datacenter IPs, identical form-fill timing, and often no JavaScript execution at all.

Manual self-referral. One person opens a private window, signs up with a new address using their own code, and repeats. Slower and harder to spot by volume, but every entry comes from the same browser.

The second group is why IP and email checks aren’t enough. The same laptop through a phone hotspot and a VPN produces many IPs and as many addresses as you like. What stays constant is the device.

Dedupe by device at submit time

Identify on the waitlist page and send the requestId with the form. On the server, fetch the event and attach a device identity to the entry:

<script src="https://api.pryntid.com/cdn/prynt.umd.js"></script>
<script>
  const ready = Prynt.load({ apiKey: 'pk_live_…' })
    .then((p) => p.identify({ tag: { action: 'waitlist' } }))
    .catch(() => null);

  document.querySelector('#waitlist').addEventListener('submit', async (e) => {
    e.preventDefault();
    const ident = await ready;
    e.target.querySelector('[name=requestId]').value = ident?.requestId ?? '';
    e.target.submit();
  });
</script>
// server
const event = await prynt.getEvent(body.requestId);   // @prynt/node
const entry = {
  email: body.email,
  referredBy: body.ref,
  visitorId: event.visitorId,
  decision: event.decision,
  reasons: event.risk?.reasons ?? [],
};

const referrer = await db.waitlist.findByCode(body.ref);
entry.referralCounts = !!referrer
  && referrer.visitorId !== event.visitorId          // not your own device
  && event.decision === 'allow';

await db.waitlist.insert(entry);
await prynt.updateEvent(body.requestId, { linkedId: entry.email });

Two details make this work:

  • A referral only counts if it comes from a different device than the referrer’s. That kills manual self-referral immediately, without blocking the signup itself. The person still gets their spot; their code just doesn’t get credit.
  • Store the visitorId on every entry. Even if you don’t act on it at submit time, it lets you audit the list later.

The updateEvent call links the entry to the device, so later events from the same browser show it in accountsOnDevice. If you’d rather not send the email to Prynt, use your own entry id as the linkedId.

Challenge the risky traffic, not everyone

A waitlist form converts best when it’s one field and a button. Don’t put a puzzle in front of every visitor. Instead, let the decision pick who sees friction:

  • allow: accept silently.
  • challenge: run prynt.challenge(), a proof-of-work step that costs a real browser a moment and a script farm a lot of CPU. It returns { passed, passToken }; verify the token server-side with /v1/challenge/validate before accepting. Pass tokens are single-use and tied to your account.
  • block: accept the email into a separate holding table, don’t credit any referral, and don’t show an error. Silent holding gives the script author nothing to debug.

For the form itself, protectForm(form) adds honeypot and timing signals, which catch the scripts that fill every field instantly. See form-fill cadence signals for what those look like.

Audit before you invite

Whatever you do at submit time, review the list before the first invite batch goes out. With visitorId stored, a few queries catch most of the remaining junk:

  1. Group by device. Any visitorId with more than two or three entries is a self-referrer or a shared machine. Keep the first entry, drop the rest from ranking.
  2. Recount referrals. Recalculate leaderboard positions using only referrals from distinct devices with an allow decision.
  3. Drop automation. Remove entries whose reasons include bot, automation, or datacenter signals.
  4. Look at email domains. A cluster of entries at one unknown domain, all referred by the same code, is usually a catch-all domain run by one person.

Publish your rules (“referrals from the same device don’t count”) before launch. It discourages the manual gamers and makes the cleanup defensible when someone asks why they dropped from 12th to 400th.

Invite in waves and watch activation

Send invites in batches and track who actually activates. If a batch has unusually low activation, look at what those entries had in common: a referral code, a domain, a device cluster. Feed what you find back into the next batch’s filter.

Waitlists are a small version of every signup-abuse problem: cheap identities, a reward for volume, and a form designed for zero friction. The same techniques apply as bot signup detection and referral fraud at signup, and if your beta includes limited codes, see presale code abuse. The Free plan covers 20k identifications a month, which fits most launch lists; the form protection page shows the honeypot and timing layer in more detail.

Try it free

Prynt is device intelligence with a free tier — visitor IDs, bot & fraud Smart Signals, and behavioral biometrics, powered by a cross-site network. Start free.

Keep reading