Guides
Build guide

Charge per API call in Next.js, with overages billed to Stripe

Meter every call, enforce the quota in the same request, and bill what goes over. The whole loop in one route handler.

Dharmendra Jagodana2 min read

In short

Record usage and check the quota in a single call with bb.usage.record, return 429 when the workspace is out, and let overages flow to the subscription. The metering and the enforcement are the same operation, which is what stops the two drifting apart.

Usage-based billing looks simple until the first customer disputes an invoice. Then you find out what your meter actually counted, and what it quietly dropped.

The failure is almost always the same shape: usage is recorded in one place and enforced in another, and the two disagree. This guide keeps them in one call.

Before you start

A BuildBase project with a plan that defines a quota. This guide uses the slug api-calls. You will also need Stripe connected for the overage half to mean anything.

Step one: record and check in the same call

The important line is that bb.usage.record returns what is left. You are not recording usage and then asking a second system whether the customer is allowed to be here.

// app/api/generate/route.ts
import BuildBase from '@buildbase/sdk';

const bb = BuildBase({ serverUrl, orgId, getSessionId });

export async function POST(request: Request) {
  const { workspaceId, prompt } = await request.json();

  const usage = await bb.usage.record(workspaceId, {
    quotaSlug: 'api-calls',
    quantity: 1,
  });

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

  // ... do the actual work
  return Response.json({ ok: true, remaining: usage.available });
}

What you should see: call it once and remaining drops by one. Call it past the plan limit and you get a 429 rather than a bill nobody agreed to.

Step two: charge by weight, not by request

A request that runs for 40ms and one that runs for 12 seconds are not the same product. If your costs scale with work, meter the work.

const usage = await bb.usage.record(workspaceId, {
  quotaSlug: 'api-calls',
  quantity: tokensUsed,
});

quantity is the only thing that changes. The quota, the enforcement and the billing all follow it.

Warning

Record usage after the work succeeds, not before, unless you are willing to bill for your own failures. If the work can fail halfway, decide deliberately which side of the failure the meter sits on. Customers forgive an outage. They do not forgive being billed for one.

Step three: credits, when the unit is not a request

Some products sell credits rather than calls. Same idea, different primitive, and the error tells you what you need to tell the user:

try {
  await bb.credits.consume(workspaceId, {
    amount: 10,
    description: 'AI generation',
  });
} catch (err) {
  if (err.code === 'INSUFFICIENT_CREDITS') {
    // err.available and err.requested are both on the error
  }
}

Returning "you need 10 and have 3" is a support ticket you never receive.

The failure modes worth knowing

Metering in middleware. Tempting, and wrong for anything expensive. The middleware does not know whether the work succeeded.

Retries. A client that retries a failed request meters twice unless the retry is idempotent. If your customers integrate with automatic retries, this is the invoice dispute you will actually get.

Trusting the client. quantity comes from your server after the work, never from the request body. Otherwise the quota is a suggestion.

Enforcing only in the UI. The gate in the interface is a courtesy. The gate in the route handler is the one that counts. The UI half is covered in enforcing quotas in React.

Install

npm i @buildbase/sdk
billing
metering
nextjs

Frequently Asked Questions

Does recording usage and checking the quota take two calls?

No. bb.usage.record returns the remaining balance in the same response, so the meter and the gate are one operation. Two calls is where drift comes from.

What happens when a workspace goes over its quota?

That depends on how you configure the quota. You can hard-stop by returning 429 on available <= 0, or allow overage and let it bill. This guide shows the hard stop because it is the safer default.

Ship it

Create a project and run the guide against your own workspace.

Simple pricingCancel anytimeFull platform access