How to stop free-tier abuse without killing signups
The usage graph spikes at 3:41am: four thousand free generations in an hour, all from disposable emails. Your first instinct is a credit-card wall. That instinct will cost you more than the abuse does.
Every free generation costs you real money
The usage graph spikes at 3:41am on a Tuesday: four thousand image generations in fifty minutes, every one of them from a free account, every account registered to an address like qx81@tempmail.lol. For a normal SaaS this would be a curiosity - some bandwidth, some database rows, shrug. For an AI app it is a line item, because every free action has a real marginal cost: tokens billed by your provider, GPU seconds on your render queue, video minutes that cost more than the coffee you were drinking when you set the free quota. A farmed free tier does not degrade your service, it debits your bank account directly. And yet the free tier is also your only acquisition channel - the entire reason it exists is that a stranger can try the product in thirty seconds without talking to anyone, and every ounce of friction you add at signup costs you some of the legitimate users the tier was built for. So the goal is not to make signup harder. The goal is to raise the cost of abuse while leaving the cost of signup at zero, and those are different projects.
Know which abuser you're fighting
Free-tier abuse is three different problems wearing one name, and they need different countermeasures. Tier one is casual overuse: one human, one account, several browser tabs firing parallel requests that sail past your daily cap. They are not malicious - your limit just is not real, and they found out by accident. Tier two is multi-accounting: one human, ten Gmail aliases, cycling through accounts when each one hits the wall. Annoying, bounded, and cheap to dampen. Tier three is industrial farming: scripts, disposable-email APIs, your client traffic reverse-engineered, hundreds of accounts created programmatically to extract free inference at scale - usually to resell it. The expensive mistake most teams make is deploying a wall sized for tier three - credit card required, CAPTCHA everywhere - that only ever stops tier one, because the industrial farmer has stolen card numbers and CAPTCHA-solving services, while your legitimate signup does not. Match the countermeasure to the tier, and start at the bottom, because the bottom tier is usually most of the volume.
Layer 0: make the limit you advertise actually real
Before you buy a fraud-detection product, check whether your cap is enforced at all, because a striking share of "abuse" is just the limit not being real. Two classic holes. First, client-side-only gating: the React app hides the generate button after ten uses, but the API endpoint behind it checks nothing - anyone with the network tab open has unlimited access. Second, the check-then-act race: your server reads the counter, sees 9 of 10, calls the model, then increments - and twenty parallel requests all read 9 of 10 before any of them writes, so a single user with a for-loop burns 30 generations against a cap of 10. The fix is to make the check and the increment one atomic operation on the server, so that under any concurrency exactly the advertised number of requests succeeds and the rest get a clean 429. If your cap holds atomically server-side, tier one is already solved - not discouraged, solved - and you have spent zero friction doing it.
// Check and increment in ONE statement - no read-then-write gap
const result = await db.run(
'UPDATE counters SET used = used + ? ' +
'WHERE user_id = ? AND period_start = ? AND used + ? <= quota',
[qty, userId, periodStart, qty],
);
if (result.rowsAffected === 0) {
// Cap reached (or no counter row) - nothing was incremented
return json({ error: 'limit_reached' }, { status: 429 });
}
// Quota is held; safe to call the model now
const image = await generateImage(prompt);Let anonymous visitors try - on a smaller meter
Here is the counterintuitive move: instead of forcing signup before the first generation, remove the account requirement entirely and give anonymous visitors a much tighter quota keyed on a device or session identifier - three generations instead of twenty-five, cheap models only. The user id in your metering layer is just an opaque string you choose, so a cookie-backed session id like anon_f3a91c works exactly like a real user id: same counters, same enforcement, just attached to a different, stingier plan. When the visitor wants the full free tier, that is the moment you ask for a verified email - and now signup is not a wall in front of the product, it is an upgrade from something they already tried and liked. This converts better than signup-first, because the product sells itself before you ask for anything, and it simultaneously caps drive-by abuse: the person who clears cookies to reset their anonymous quota is grinding through three cheap generations per reset, which costs them more effort than it costs you money. You have made the free sample smaller without making the door narrower.
Filter email quality, not email ownership
Verified email is your tier-two checkpoint, and it works only if the email is worth something - which a disposable address is not. Three cheap checks, none of which a legitimate user will ever notice. Block disposable-email domains at signup: the open-source blocklists cover tens of thousands of burner domains and the lookup is a hash-set membership test, microseconds per signup. Normalize plus-aliases and dot-tricks before uniqueness checks: jane+1@gmail.com, jane+2@gmail.com and j.a.n.e@gmail.com are one mailbox, so treat them as one account instead of letting one Gmail address mint unlimited free tiers. And rate-limit account creation per IP - five signups per hour from one address is a human household at its absolute peak; fifty is a script. The point is the asymmetry: a real user signs up once, with their real address, from their own connection, and sails through all three checks without seeing them. The multi-accounter now needs real, distinct mailboxes, which converts a thirty-second loop into a manual chore. You have not added friction - you have added cost, and only for the person extracting value.
The friction ladder: gate the expensive 10%
Now arrange those layers into a ladder where the friction a user faces scales with the value they can extract, instead of being a flat wall at the door. Rung one: anonymous, no account, tiny quota, cheapest models - cost of abuse near zero because the value extracted is near zero. Rung two: verified non-disposable email, the normal free tier - enough quota to evaluate the product properly for weeks. Rung three, and only rung three, carries heavy verification: card-on-file or phone verification, reserved for the features where your marginal cost actually hurts - video generation, the premium image model, long-context calls. The arithmetic that makes this work is that abuse value is concentrated: the expensive features are typically a tenth of your catalog but the large majority of your potential burn, so gating that 10% behind a card removes most of the farming incentive while the 90% of users who came for the everyday features never see a card form at all. A farmer who wants your cheap model at free-tier volume is welcome to grind for scraps; the thing worth industrializing is locked behind exactly the check that industrial operations find expensive to fake at scale.
For industrial farming, detection beats prevention
You will not pre-block tier three, because tier three is built to pass every gate you put up - real-looking emails, residential proxies, human-solved CAPTCHAs. What farming cannot do is look like organic usage, and that is where per-user usage analytics stop being a reporting feature and become a security tool. Farmed cohorts have a signature: a batch of accounts created within the same hour, each with an identical usage shape, each hitting exactly 100% of the daily cap, every day, at machine-regular intervals - real users are bursty, partial, and irregular. If your events carry metadata like model and feature, the signal gets sharper still, because farms harvest the expensive path exclusively: fifty accounts that have only ever called the premium model and never opened the editor are not fifty enthusiasts. Make it a weekly ritual to sort free users by provider cost and skim the top twenty - the farms sit at the top, clustered and uniform, and a same-day registration cohort with matching consumption curves is as close to a confession as you will get. You cannot block what you cannot see; most teams being farmed simply have no per-user view, so the burn hides inside an aggregate bill.
What not to do
Every one of these is a real countermeasure someone ships in the angry week after discovering abuse, and every one of them costs more in lost signups or support load than the abuse it prevents.
- Credit-card-wall the entire free tier. It kills your acquisition funnel to stop tier one and tier two - the tiers already handled by atomic enforcement and email hygiene - while tier three passes it with stolen or prepaid cards.
- CAPTCHA on every action. A CAPTCHA at a suspicious signup is fine; a CAPTCHA per generation taxes every legitimate user on every use, and farms route around it with solving services for fractions of a cent.
- Shadow limits the user cannot see. Silently degrading or blocking users past an unpublished threshold feels clever and generates a support ticket per occurrence - "your app is broken" - plus public posts when they compare notes. Advertise the limit and return a clear limit_reached.
- Banning by IP address. Offices, universities, and mobile carriers put thousands of people behind one NAT address; one abuser gets you a thousand false positives. Rate-limit signups per IP, but never hard-ban the address.
The enforcement layer is the boring part - don't build it
Everything above reduces to one stack: per-user counters enforced race-safely, an anonymous plan and a verified plan keyed on ids you choose, reserve/commit semantics for the expensive paths, and per-user cost analytics that make farming visible before the invoice does. That stack is exactly what Vevee provides as a drop-in SDK - can, track, reserve/commit/release, plans with limit groups, per-user usage analytics - with a free tier to start, so you can spend the week on your product instead of on your counters table.
Keep reading
The race condition in "if (usage < limit)" that is costing your AI app money
A user at nine of ten images opens six tabs and clicks generate in all of them. Every tab passes your limit check, every tab gets an image, and you pay for all six. The bug is one read-then-write - and your unit tests will never catch it.
MeteringHow to build a credits system for your AI app (ledger design, rollover, refunds)
A user emails: "I had 40 credits this morning and now I have 12, and I only made one image." If your balance is a column you mutated, you cannot answer that email. Here is the ledger design that can.