Your free tier is a payment terminal for someone else
A denial of wallet attack does not take your service down. It runs your service exactly as designed, at scale, and sends you the invoice. Rate limits were built to protect uptime, and uptime is not the thing being attacked.
Picture a four person company that sells a developer tool with a language model behind it. The free tier gives every new account a modest monthly allowance of tokens, because that is how developers try things, and trying things is how the company gets customers. It has worked for two years. The allowance costs a few cents per signup, and roughly one signup in three hundred eventually becomes a paying customer, which is a fine trade.
On a Tuesday morning the founder opens the cloud console before standup, the way she does every morning, and the month to date number is wrong. Not wrong in the way that makes you check your filters. Wrong in the way that makes you put your coffee down. The inference line is roughly eight times what it was in the same period last month.
Her first thought is a runaway job, so she checks. There is no runaway job. Every request is well formed. Every request is inside its account's allowance. Every account passed the signup flow, confirmed an email address, and solved the challenge widget. The traffic is coming from a few thousand accounts that each behave impeccably, use their allowance completely, and never come back. The dashboard she built to celebrate growth is showing her the best signup month in company history.
Nothing is broken. Nothing triggered an alert. The system is working perfectly, at volume, on behalf of somebody who is not her customer, and she is paying for all of it in cash.
What is a denial of wallet attack?
The name is a joke that stopped being funny. A denial of service attack tries to exhaust a resource until legitimate users cannot get through. A denial of wallet attack does the opposite: it makes sure every request succeeds, because a successful request is the one that costs you money.
The term came out of the serverless community, where the economics are most naked. If your architecture scales automatically and bills per invocation, then an attacker who can trigger invocations has a direct line into your bank account with no intermediate step. There is no queue to fill, no connection pool to exhaust, no CPU to peg. The platform absorbs the load beautifully, exactly as advertised, and the meter runs.
Two things turned this from a serverless curiosity into a problem for almost every company with a free tier.
The first is that model inference put a real, non trivial marginal cost behind an ordinary looking web request. For most of the history of software as a service, the marginal cost of one more free user doing one more free thing rounded to zero. A page view, a database query, a webhook: fractions of a cent, absorbed into fixed infrastructure you were paying for anyway. A single generation request against a capable model is not that. It is a real purchase, made by you, on behalf of whoever sent the request.
The second is that creating an account stopped being work. Account creation is catalogued by the Open Web Application Security Project as an automated threat in its own right, OAT-019, alongside credential stuffing and scraping, which tells you the industry has understood the shape of this for years. What changed recently is not the technique but the price. Filling in a signup form, receiving an email, clicking a link, and solving a visual challenge used to require either a human minute or a moderately skilled scripting effort. Now it requires a prompt.
Two flavours, one invoice
It is worth separating the two versions, because they have different attackers and the same accounting consequence.
The first is farming. Thousands of accounts are created to harvest whatever the free tier gives away: model tokens, build minutes, storage, API quota, promotional credits. The harvested resource is used directly, or resold. Nobody is targeting you personally. You are simply a cheap supplier who has not noticed.
The second is targeted denial of wallet. Somebody who dislikes you, or who is short your stock, or who is testing an extortion model, deliberately drives billable usage on your metered service to inflate your costs. This is rarer and nastier, and it is the version that gives the category its name.
The defence is largely the same for both, which is fortunate, because most companies discover the first one and never find out whether they were exposed to the second.
Why is this different from ordinary bot abuse?
This is the part that trips up experienced teams, because the instinct built up over a decade of bot defence points in slightly the wrong direction.
Classic bot abuse has a cheap failure mode. If a scraper hits you too hard, you rate limit it, and the cost of the requests you already served was negligible. If a credential stuffing run comes through, you slow it down, and the cost of the attempts was a few database reads. The damage in classic bot abuse is availability, data leakage, or account takeover. Serving a bot request is not itself the injury.
In a denial of wallet attack, serving the request is the injury. There is no cheap failure mode. Every request you handle successfully, politely, within limits, in accordance with your own published terms, converts directly into a line on a supplier invoice you will pay in real currency at the end of the month.
Here is the analogy worth keeping. A denial of service attack is a crowd blocking the door of your shop so real customers cannot get in. A denial of wallet attack is a crowd walking in, one at a time, politely taking the free sample you put on the counter, and leaving. Nobody is blocked. Nobody is rude. Your sample budget is gone by eleven in the morning and you have sold nothing.
That difference has three practical consequences that change how you should think about controls.
Your alerting is pointed at the wrong metric. Latency is fine. Error rate is fine. Saturation is fine, because you autoscaled. The signal is on the finance dashboard, which is updated daily at best, often monthly, and read by somebody who is not on the incident rotation.
Rate limiting does not solve it, it only shapes it. A per account limit caps the damage per account and does nothing at all about the number of accounts. If the attacker's cost of creating an account is lower than the value of that account's allowance, they will simply create more accounts, and your limit has become a specification of exactly how much they can take per unit of effort. You have not stopped the leak. You have documented it.
The abuse looks like your best growth month. This is the genuinely cruel part. Signup count, activation rate, and time to first successful API call all improve. Every top of funnel metric a growth team is measured on gets better while the company loses money. It is difficult to overstate how long this can go undiagnosed inside an organisation where growth and finance report separately.
What does an abused free tier actually cost?
Let us do the arithmetic honestly, with the numbers labelled as an illustration rather than a survey, because the real figures are commercially sensitive and almost nobody publishes them.
Take a company with 100,000 signups in a month. Suppose the free allowance costs the company forty cents of inference if fully consumed, which is a modest allowance at 2026 prices. Suppose that on a clean funnel, 20 percent of free users consume their whole allowance, because most people try a thing twice and wander off.
On a clean funnel that is 100,000 times 0.2 times forty cents, or 8,000 dollars a month in free tier cost. That is a sane acquisition expense.
Now suppose a fifth of those signups are automated, and unlike humans, automated accounts consume 100 percent of the allowance, because that is the entire point of creating them. The 20,000 automated accounts cost 20,000 times forty cents, or 8,000 dollars, all by themselves. Your free tier cost has doubled and your paying conversion has not moved by a single customer, because farms do not convert.
| Line | Clean funnel | 20 percent automated | With a uniqueness proof at signup |
|---|---|---|---|
| Monthly signups | 100,000 | 100,000 | 80,000 humans, farms priced out |
| Allowance fully consumed | 20,000 | 36,000 | 16,000 |
| Free tier cost at 0.40 per allowance | 8,000 | 14,400 | 6,400 |
| Paying conversions at 1 in 300 humans | 267 | 267 | 267 |
| Free tier cost per conversion | 30 | 54 | 24 |
| Attacker cost per harvested allowance | near zero | near zero | one verified human per account |
Two things in that table matter more than the totals, which you should replace with your own numbers anyway.
The first is the last row. Every column except the last has an attacker cost of approximately nothing, and any control that does not change that row is decoration. You can tune the other rows all year and the economics of farming your free tier will not move.
The second is the conversion row, which does not change across the first three columns. This is the argument that wins the meeting with the growth team. Removing farmed signups does not cost you customers, because farmed signups were never going to be customers. It costs you a vanity metric. If the growth team's targets are set on signup count, you will have an organisational problem before you have a technical one, and it is worth naming that out loud early.
Scale that model to a company with genuine volume and the seven figure number in the brief stops being rhetorical. It is simply 100,000 signups a month with a heavier allowance, or a million signups a month with this one.
Why does every existing signup defence fail?
Every company that hits this problem reaches for the same five controls, roughly in this order. Each of them is reasonable. Each of them has a specific structural reason it does not close the category, and it is worth being precise about which, because the failure modes are different and some of these are still worth deploying.
Require a credit card
This works. It is the single most effective control on this list, and it is also the most expensive one you own, because you are paying for it in the currency the free tier exists to earn.
The free tier is an acquisition instrument. Its entire job is to let somebody who has never heard of you find out whether your thing is any good, before they have any reason to trust you with a payment instrument. Putting a card form in front of that is not a small tax on conversion, it is a reversal of the sequence. The developer evaluating four options on a Thursday afternoon will try the three that do not ask.
The industry has run this experiment publicly and painfully. Several well known platforms have tightened or ended free offerings and pointed at abuse when explaining why, including GitLab, which announced in 2021 that new free tier users would need to provide a credit card before using shared continuous integration runners, and Heroku, which announced in 2022 that it would end its free product plans. These are the companies' own public announcements and worth reading in their own words rather than through summaries, because the reasoning is more nuanced than the headlines suggested. The pattern across the sector is consistent: the abuse is real, and the remedy chosen has consistently been to charge the honest user for the dishonest one.
There is also a quieter failure. Cards are rentable. Prepaid and virtual card numbers are trivially available, in bulk, and a farm running a business case that clears a few dollars per harvested account can afford them comfortably. A card requirement is a high toll on the honest and a modest cost of goods for the organised.
Verify the email address
This stops nothing and everyone knows it. Disposable address services exist in the hundreds, catch all domains cost a few dollars a year, and the entire round trip is automatable. Email verification is worth keeping for deliverability and for support reasons, and it should not appear anywhere on a list of abuse controls.
Verify a phone number
Phone verification does raise the attacker's cost, which is why it is popular. It also has three problems that compound.
It costs you real money per attempt, which means an attacker can impose cost on you simply by triggering verifications, which is a denial of wallet attack against your anti abuse control. It excludes people, which is a genuine harm and not merely a conversion statistic: users on shared phones, users in countries where your provider has poor coverage or high pricing, users who reasonably decline to hand a phone number to a company they are evaluating. And the resale market for verification codes is mature and cheap, which means the control is a toll on the honest and a line item for the organised, exactly like the card.
Show a challenge widget
Invisible challenge widgets are genuinely good engineering. Modern implementations avoid image puzzles most of the time and lean on device and browser signals, and they will remove a meaningful fraction of unsophisticated automation for very little user friction. Deploy one. It is close to free and it raises the floor.
What it does not do is prove a human. It produces a probability that a request came from a browser behaving the way browsers behave, and browser behaviour is fully reproducible by a determined adversary with a real browser under automation, a residential proxy, and patience. A challenge widget is a speed bump priced in fractions of a cent, and the sophisticated end of this market has been paying that price for years.
Fingerprint the device
This is the control the security market will sell you hardest, and it deserves a careful answer rather than a dismissal, because the reason it fails is instructive and the reason it is tempting is legitimate.
It is tempting because it works for a while, it requires nothing of the user, and it produces satisfying dashboards. It fails for three reasons that are all getting worse rather than better.
The first is technical. Device signals are inference from an environment the adversary controls completely. Everything a fingerprint reads, an attacker can set. This is not a bug in any particular vendor's implementation, it is the structure of the problem, and it is why the arms race has no end state.
The second is that the platforms are actively removing the raw material. Browser vendors have spent years reducing passive entropy, and privacy oriented defaults keep tightening. A control built on signals the platform vendors have publicly committed to degrading is a control with a scheduled expiry date.
The third is that it costs you something you may not have priced. Fingerprinting is data collection about people who have not become your customers, and it is the kind of collection your privacy policy has to describe and your data protection assessment has to justify under a minimisation principle. You are accumulating a liability in order to guess at a fact.
The deeper issue underneath all five is the same. None of these controls answers the question you actually have. You want to know whether this signup is one unique person. Every control above answers a different question: does this request have a payment instrument, does it have a phone number, does it come from a browser, does it look like other requests. You are triangulating around a fact instead of establishing it, and the triangulation is exactly as good as the adversary allows it to be.
What would one human, one free tier look like?
The alternative is to stop grading the signup and start proving the thing you care about, which is that a distinct person is behind this account, once, at the moment the entitlement is issued.
The mechanism is a per action human signature. At signup, or better, at the moment free credits are actually issued, the user completes a short device bound check on their own phone or laptop: a passkey signature with a liveness step, taking a few seconds. What the service receives is a receipt: a signed statement that a live human completed the check, together with a one way key derived on the user's device.
The one way key is the part that makes this usable and worth understanding properly, because it is what separates this from every identity gate a developer has ever resented.
The key is deterministic for the same person against your service, and it is not reversible into a face, a name, a document, or a key at any other service. That gives you exactly one capability and no more: the ability to tell whether two signups on your platform are the same person. You cannot learn who they are. You cannot look them up. You cannot correlate them with their account on somebody else's platform. You are not storing a face template, because none leaves the device.
The analogy is a hand stamp at a festival gate. The stamp tells the person on the gate that you have already come in today. It does not tell them your name, it does not follow you home, and it is useless at a different festival. What it does perfectly is make the second entry on the same wristband visible.
Here is the shape of the gate at the entitlement issuance point.
POST /v1/signup/issue-credits
// 1. The client has completed the human check and holds a receipt.
const receipt = await manav.verify({ mode: "once", scope: "free-tier-2026" });
// 2. The server verifies the receipt offline against the published key.
// No callback, no vendor availability dependency in the signup path.
const ok = verifyReceipt(receipt, PUBLISHED_ED25519_KEY);
if (!ok) return deny("invalid_receipt");
// 3. The one way key is the only durable thing you store.
const humanKey = receipt.subject_key; // stable for this person, on your service only
if (await creditsAlreadyIssued(humanKey)) {
return deny("already_claimed"); // second free tier for the same person
}
// 4. Issue the entitlement and record what was proven.
await issueCredits(accountId, ALLOWANCE);
await recordEntitlement({ humanKey, accountId, receiptId: receipt.id, issuedAt: now() });
Read the third step again, because it is the whole design. The only thing you keep is an opaque key and the fact that it has claimed once. You are not building a profile. You are maintaining a list of stamps.
The receipt itself is small and boring on purpose. Conceptually it carries the scope it was issued for, the time, the one way subject key, and an Ed25519 signature that verifies against a published key with no call back to anyone. That last property matters more than it sounds during a signup flow: your registration path does not acquire a new runtime dependency on a third party being up.
Note what is not in it. No name. No email. No document. No image. No device fingerprint. No behavioural score. The receipt proves presence and uniqueness, and deliberately proves nothing else, which is what makes it defensible to ship in front of developers who will read your privacy policy.
Where to put the gate
Most teams reach for the signup form, and that is usually the wrong place. Put the check at the moment of entitlement, not the moment of registration.
Let people create an account, look around, read the docs, and see whether your product is worth their attention. Ask for the check when they claim the thing that costs you money: the free credits, the trial minutes, the promotional balance. This is better on every axis. The user has context for why you are asking. The conversion cost lands on a smaller and more motivated population. And you have not put a gate in front of the top of your funnel, which is the thing the growth team was right to be worried about.
What about shared devices, libraries, and people without smartphones?
Any uniqueness control has to survive contact with how people actually live, and a design that quietly excludes people is not a good design, it is a bad design with good metrics.
A shared laptop is fine. The check is bound to the person completing it, not to the machine, so two people using the same library computer produce two different keys and both get their allowance. This is a genuine advantage over device based controls, which routinely punish households, shared workspaces, university labs, and internet cafes for the crime of sharing hardware.
A user with no smartphone needs a path, and the honest position is that the companion device flow assumes one. The mitigations are real but partial: the check can run on the same laptop where a platform authenticator is available, and you should keep a documented fallback that a human being can operate. The important discipline is to treat the fallback as a normal path with its own controls rather than an exception that quietly becomes the attacker's preferred route, which is what happens to every fallback nobody owns.
Users who decline are also a real population. Some people will not do a liveness check to try a developer tool, and that is a legitimate preference rather than a signal of guilt. The design answer is proportionality: gate the expensive entitlement, not the account, and offer a smaller allowance without the check if your economics permit it. A tiered free tier, generous for verified humans and minimal for everyone else, is usually a better business than a single tier that has to be small enough to survive farming.
What this cannot do
Every control has a boundary, and the boundary here is worth stating plainly, because a vendor who tells you this eliminates abuse is telling you something false.
It does not stop an attacker who has many real humans. If a farm can pay people to complete checks, it can produce distinct valid keys. What changes is the unit economics: the marginal cost of an abused account moves from approximately zero to the cost of a human minute, which is several orders of magnitude, and in most cases that is enough to make the business case collapse. It is not a wall. It is a price.
It does not stop a legitimate user abusing their own allowance. One person, one account, one allowance, consumed entirely and repeatedly through some other mechanic, is a product design problem rather than an identity problem, and you should solve it with your allowance design.
It does not help with targeted denial of wallet against an unauthenticated endpoint. If you expose a costly operation to the public internet with no account required, no signup control reaches it. That case needs a different answer, mostly caching, hard budget ceilings, and a per endpoint cost budget with an automatic circuit breaker.
It adds a step, and steps cost conversion. Anyone who tells you a verification step is free is selling. The argument is not that it is free, it is that it is cheaper than a credit card wall and more effective than a challenge widget, and that placing it at entitlement rather than registration puts the cost where it is most defensible.
Uniqueness is per service today. The key is scoped to your service by design, which is the privacy property, and it also means enrolment does not currently spare a user the check at the next company. That is the correct default and it is a real friction cost.
What to do this week
- Put a cost per signup number on the whiteboard. Free allowance, fully consumed, at your current unit prices. Almost nobody has this number, and every argument that follows depends on it.
- Instrument allowance consumption as a distribution, not an average. Humans trail off. Farms consume 100 percent and stop. The bimodal shape is visible within a day of looking and it is the fastest diagnostic you have.
- Put the free tier cost line on the same dashboard as the signup count. The organisational failure that lets this run for two quarters is that growth and finance look at different screens.
- Set a hard monthly budget ceiling with an automatic circuit breaker on every metered downstream service, and test that it fires. This is the control that turns a catastrophe into an incident.
- Move the gate from registration to entitlement. Even before you change the control, changing its position recovers conversion at the top of the funnel.
- Deploy an invisible challenge widget if you have not. It is nearly free, it removes the unsophisticated layer, and it lets you see what is left.
- Pilot a uniqueness proof on the entitlement issuance path for one segment, and measure two numbers only: verified human signup rate, and free tier cost per paying conversion. Those two numbers settle the argument in either direction.
- Write down what you would do if the abuse were deliberate. If somebody is targeting your bill rather than harvesting it, your response is different, and deciding that during the incident is expensive.
If you want to see the shape of the gate before you build anything, the discount and signup fraud demo and the human check demo both run in a browser with no signup, and the integration details are in the developer documentation.
Frequently asked questions
What is a denial of wallet attack? It is abuse of a free tier, trial, or pay per use service that succeeds on purpose, so the provider is billed for work done on behalf of an attacker. Unlike a denial of service attack it does not reduce availability. Every request is served correctly and appears on the provider's supplier invoice, which is why monitoring built around latency and error rates never fires.
Why do so many free trials now require a credit card? Because a card is the cheapest available proxy for a unique person, and companies have been choosing it as abuse rose. It is effective and expensive, since it puts a payment instrument in front of an evaluation the free tier existed to make easy, and it is defeated at the organised end by prepaid and virtual card numbers.
Do rate limits stop free tier abuse? No. A per account rate limit caps damage per account and does nothing about the number of accounts. If creating an account costs an attacker almost nothing, your rate limit simply specifies how much each account can take. Rate limits protect availability, which is not the resource under attack.
Does device fingerprinting solve fake signups? Not durably. Fingerprinting infers from an environment the attacker fully controls, browser vendors are steadily reducing the available signals, and it collects data about non customers that your privacy documentation must then justify. It raises the floor for a period and does not change the attacker's marginal cost.
Can you prove a unique human without collecting identity? Yes. A device bound check with liveness can produce a one way key that is stable for one person against one service and reversible into nothing. It answers whether two signups are the same person and cannot reveal who that person is, which is why it works in front of developers who read privacy policies.
Will a human check hurt signup conversion? It costs something, and anyone claiming otherwise is selling. Placing it at entitlement issuance rather than registration usually recovers most of the cost, because the user has context for the request and the population being asked is smaller and more motivated than everyone who lands on your signup page.
What about agents that legitimately need accounts? This is the coming case and it should be designed for rather than blocked. The right shape is an agent acting under a scoped, revocable delegation signed by a human, so the entitlement is still anchored to one person even though software is using it.
Sources
- Open Web Application Security Project, Automated Threats to Web Applications, including OAT-019 Account Creation: owasp.org/www-project-automated-threats-to-web-applications
- Imperva bad bot research, which reports automated traffic as a large share of web requests. Vendor telemetry reflects the traffic that vendor sees and its own detection thresholds, so treat all such shares as directional: imperva.com bad bot report
- GitLab company announcements on free tier continuous integration and credit card verification: about.gitlab.com/blog
- Heroku announcements on the end of free product plans: blog.heroku.com
- Cloudflare Turnstile documentation, on what a challenge widget establishes: developers.cloudflare.com/turnstile
- IETF Privacy Pass working group, on privacy preserving authorisation tokens: datatracker.ietf.org/wg/privacypass
- Apple DeviceCheck and App Attest documentation, on device attestation and what it proves: developer.apple.com/documentation/devicecheck
- General Data Protection Regulation, Article 5, data minimisation: gdpr-info.eu/art-5-gdpr
A rate limit tells the attacker how much they may take per account. It is not a control, it is a price list, and the free tier is where you publish it.