Blog
Explainer

Quotas are not credits: which one your plan actually needs

A quota resets on a fixed cycle and belongs to the plan. A credit is a balance the customer bought. Pick by who absorbs the spike.

Dharmendra Jagodana7 min read

In short

A quota is an allowance in a plan that resets on a fixed cycle, usually monthly: 10,000 API calls a month, gone at the reset. A credit is a balance the customer bought that carries until spent. Quotas belong to the subscription, credits to the customer, and picking the wrong one shows up as an email about a bill.

A quota is an allowance that comes with a plan and resets on a fixed cycle, usually monthly: 10,000 API calls a month, included, gone at the reset. A credit is a balance the customer bought that sits until it is spent or expires. Quotas belong to the subscription. Credits belong to the customer.

That sounds like a distinction for the billing team. It is not. It decides who absorbs a spike, whether a customer can keep working on the 28th of the month, and what the support inbox looks like on the 1st.

Why there are two mechanisms at all

Subscription revenue is predictable and consumption revenue is not, and the two mechanisms exist to encode two different promises. A quota says: pay this much every month and you get this much, and we both know the ceiling. A credit says: pay for what you will use, use it when you like, and the ceiling is your wallet.

Quotas alone leave money on the table. Products that only offer credits make budgeting impossible for the customer who needs a fixed number to put in a purchase order. So most pricing pages end up with both, often without anyone having decided that on purpose.

How a quota actually works

A quota is a counter against a slug. api-calls, storage-mb, video-renders. The plan version says how many units are included. Each time the thing happens you record it, server-side, and the record comes back with what is left:

const usage = await bb.usage.record(workspaceId, {
  quotaSlug: 'api-calls',
  quantity: 1,
  idempotencyKey: req.headers.get('x-request-id'),
});

if (usage.available <= 0) {
  return Response.json({ error: 'Quota exceeded' }, { status: 429 });
}

Three things in that snippet are the whole design. The record and the check are one call, so there is no window where a burst of requests all pass the check before any of them is counted. The idempotency key means a retried request does not count twice, which matters the moment you put this behind a queue. And the response is a number, not a boolean, so the UI can warn at 80% instead of refusing at 100%.

At the limit a quota does one of two things. It stops: the record call fails with a 400, and your route answers with a 429 and a message about upgrading. Or it enters overage, where the units past the included amount are metered and billed separately. Both are legitimate. What is not legitimate is a quota that silently keeps counting past the limit with no state change, because then the number on the invoice is the first the customer hears of it.

Then the period rolls over and the counter goes back to zero. Whatever was unused is gone. That is the defining property, and it is the one customers argue about.

How a credit actually works

A credit is a ledger entry. The customer buys a package and the balance goes up. Spend some, it goes down. Nothing resets. Standing the wallet up is its own job, and the balance turns out not to be a single number.

try {
  await bb.credits.consume(workspaceId, {
    amount: 50,
    description: 'Batch export',
    idempotencyKey: req.headers.get('x-request-id'),
  });
} catch (err) {
  if (err.code === 'INSUFFICIENT_CREDITS') {
    return Response.json({ error: 'Not enough credits' }, { status: 402 });
  }
}

Notice the status code changed. The platform rejects an exhausted quota with a 400, and the quota route above answers its own caller with a 429, too many requests, because the customer has hit a rate the plan set. A credit exhaustion is a 402, payment required, because the fix is to buy more. Writing that route in Next.js is its own guide. That is not pedantry. The two errors send the customer to two different screens, and the one for credits should have a buy button on it.

Two properties of credits cause most of the design work. The first is that amount is a parameter, so different actions can cost different amounts, and the price of an action can change without touching the customer's balance. A quota counts events; a credit system prices them. The second is expiry. Credits that never expire are an open liability on your books forever. Credits that do expire need the customer to see what is expiring and when, or the expiry reads as theft. A downgrade is where the two clocks separate: credits already bought run until they expire on their own while the quota allowance drops at the switch.

Key takeaway

A quota counts events and resets. A credit prices events and carries. Quota exhaustion should reach your caller as a 429 that says upgrade, credit exhaustion is a 402 that says buy, and the two should send the customer to two different screens.

Which one, then

It comes down to three questions, and the third is the one people skip.

Does usage reset? If a customer who used nothing in March should start April with the same allowance as everyone else on the plan, that is a quota. If what they did not use in March is still theirs in April, that is a credit.

Is the unit priced, or counted? If every event costs the same and the plan includes a number of them, count with a quota. If a short render and a long render should cost different amounts, price with credits. Trying to express variable cost as a quota means inventing a synthetic unit, and every synthetic unit eventually needs a page explaining it.

Who absorbs the spike? A quota with a hard stop puts the spike on the customer: they hit the wall and wait or upgrade. A quota with overage puts it on the invoice, which is fine if they expected it and a dispute if they did not. Credits put it on the customer's wallet, in advance, which is the only version where nobody is surprised.

If the answer isUse
Resets monthly, same cost per eventQuota
Carries over, same cost per eventCredits
Variable cost per event, any reset ruleCredits
Fixed monthly budget in a purchase orderQuota

When each one is the wrong choice

Credits are the wrong tool for anything the plan should simply include. Seats are the usual mistake. A team plan that includes five members is a quota on members, and making people buy member-credits turns a pricing page into a puzzle. The same goes for storage, projects, and most things a customer counts on their fingers.

Quotas are the wrong tool when the wall is the churn moment. A self-serve product where a customer hits the limit on the 20th and cannot do anything until the 1st has just handed them ten days to try a competitor. Overage softens that, but overage on a product with no spending cap is how a customer ends up with an invoice they did not authorise. If usage is bursty and the customer would rather top up than wait, that is a credit system, and it should have been from the start.

The combination most API products land on is a quota included with the plan and credits for what goes over. The included amount gives the customer a number for the budget. The credits give them a way past it without a plan change. The per-call billing guide walks through that loop in one route handler, including the case where the unit is not a request.

A trial is the third thing again: not an allowance and not a balance, but a clock, and running one without a card is where that difference starts to bite.

What this is not

A quota is not an entitlement, although it is enforced by one. The plan entitles the workspace to api-calls; the quota is the number attached to that entitlement. And neither is a feature flag, even though all three end up as a boolean around a component in the UI. That confusion has its own post: entitlements are not feature flags.

Both mechanisms, the quota counter and the credit ledger, sit inside BuildBase's billing module, and the quota reference has the full surface. But the decision above is the same whatever you build it on.

billing
metering
credits
quotas

Frequently Asked Questions

What is the difference between a quota and a credit in SaaS billing?

A quota is an allowance included in a plan that resets on a fixed cycle, usually monthly, so unused units are lost at the reset. A credit is a prepaid balance the customer owns; it carries over until spent or until an expiry you set. Quotas are a property of the subscription, credits are a property of the customer.

Can a quota have overage?

Yes. A quota can hard-stop at the limit, or it can enter an overage state where usage beyond the included amount is metered and billed separately. Which one you choose is a product decision, and the quota system should be able to report both states so the UI can warn before the wall rather than at it.

Do credits expire?

They can, and whether they do is your call rather than a rule. Expiring credits protect you from carrying an open liability forever; non-expiring credits are simpler to explain and sell. If you expire them, show the customer what is expiring and when, well before it happens.

Should I use quotas or credits for API pricing?

Use a quota when every plan includes a predictable amount and you want the spend to reset monthly. Use credits when usage is bursty, when customers want to top up without waiting for renewal, or when the unit of work varies in cost. Many APIs use both: a quota included with the plan, then credits for what goes over.

Put this into practice

The modules in this post are one console away. 7-day free trial, no credit card.

7-day free trialNo credit card requiredCancel anytime