Per-user rate limits and AI spend caps for Next.js + Supabase + Stripe

Copy-paste TypeScript and one Supabase migration, designed to help cap abuse and surprise bills. $19 (plus VAT where applicable). One-off, no monthly bill from the kit.

Get the Spend Caps Kit ($19 plus VAT where applicable)
Stack & Ship cover: Spend Caps Kit. Rate limits, per-user AI spend caps, and a checkout refresh-loop guard for Next.js + Supabase + Stripe. Chips: Upstash or Supabase, Streaming usage, Stripe idempotency, Claude Code + Cursor review. Terminal (real run of the kit's fire-requests.mjs against a local /api/chat route limited with PRESETS.aiRoute): sent 50 POST http://localhost:3000/api/chat, 200: 10, 429: 40, PASS: got 429s (first Retry-After: 60). Beside it, a meter labelled as an illustration: AI budget this month: $1.62 / $2.00, warn at 80%. Footer: Not affiliated with OpenAI, Anthropic, Stripe, or Supabase.

What it does

The kit runs inside your app, on Upstash or on your own Supabase Postgres. It covers three places where a small app tends to get an unexpected bill: a route that anyone can hammer, an AI call with no per-user ceiling, and a checkout button that creates a new Stripe session on every refresh.

Rate limits (lib/ratelimit.ts). Five presets: anonymous IP, signed-in user, AI route, auth/OTP, and checkout. They run on Upstash's sliding window or a Supabase Postgres counter. Over the limit, the route returns 429 with Retry-After. If the limiter backend is down, it returns 503 by default; you can switch that to let traffic through.

AI spend cap (lib/spend-cap.ts). A dollar ledger per user per month, priced from each response's token usage, including cached tokens, cache writes, and streams. A stream cut off before its final usage is charged at least the up-front estimate. It warns at 80% and blocks the next AI call at 100%.

  • The price table covers 16 current OpenAI and Anthropic models, last checked 17 Oct 2026.
  • A model that isn't in the table is refused, not counted as $0.
  • Each call reserves its estimated cost first, so parallel calls can't all slip under the cap at once.

Checkout guard (lib/checkout-guard.ts). For the same user and cart within 10 minutes, it hands back the open Stripe Checkout Session instead of creating another. It adds idempotency keys and a per-user limit, and dedupes webhooks on event.id with a pending/completed status.

SQL (sql/001_spend_caps_kit.sql). Four tables with RLS on, and two security definer functions with a pinned search_path that only your server's service role can call.

Also in the zip

  • Example wiring: proxy.ts (Next 16) or middleware.ts, an AI route, a checkout route, a webhook route, and a checkout button that disables itself.
  • A backstop checklist for provider-side limits in OpenAI, Anthropic, Vercel, Supabase, and Upstash, each marked "verify in your dashboard".
  • A check-abuse review as a Claude Code skill and a Cursor rule. It looks for unthrottled routes, AI calls without a user or cap, and checkout without idempotency.
  • A load test script that fires 50 requests at your route and passes when it sees 429s.
  • A 15-minute setup guide and a "how it works and its limits" page.

How we tested it

  • 51 automated tests pass on Node 20.19 (npm run verify): 39 unit tests, 10 SQL tests that run the migration in PGlite (Postgres in WASM) and check grants and RLS, and 2 tests of the load script.
  • The unit tests cover limits, client IP sources, cost maths, reservations under 20 parallel calls, 50 cut-off streams against a $1 cap, checkout reuse over 50 clicks, and webhook dedupe.
  • The example routes type-check against Next.js 16.4, Stripe 23, OpenAI 7.35, Upstash Ratelimit 2.2, and supabase-js 2.118.

What this doesn't do

  • The Supabase backend is a fixed-window counter, so it can allow up to 2x the limit across a window boundary. Use the Upstash backend if that matters.
  • The SQL was tested in PGlite with stub Supabase roles, not against a hosted Supabase project.
  • No live provider calls were tested. The tests use fakes, not live OpenAI, Anthropic, Stripe, or Upstash accounts.
  • The price table uses standard-tier prices. Batch, flex, fast mode, data-residency uplifts, and tool fees aren't included. Prices change: check the table before you rely on it.
  • Rate limits and spend caps reduce the risk of surprise bills; they are not a replacement for the provider-side limits in the checklist.

Requirements

Node 20+, Next.js App Router, and Supabase. Upstash is optional. The kit has no runtime dependencies of its own; you install the SDKs you already use.

Get the Spend Caps Kit ($19 plus VAT where applicable)

OpenAI is a trademark of OpenAI. Claude and Claude Code are trademarks of Anthropic. Stripe, Supabase, Vercel, Upstash, Next.js and Cursor are trademarks of their owners. This kit is not affiliated with or endorsed by any of them.

FAQ

Is there a monthly fee?
No. You pay $19 once, plus VAT where applicable. The kit runs in your app, on Upstash or your own Supabase Postgres.
What happens if a user calls a model that isn't in the price table?
The call is refused before it reaches the provider, rather than counted as $0. Add the model's prices to PRICES in lib/spend-cap.ts to allow it.
What if a user fires several AI calls at once?
Each call reserves its estimated cost before it runs, and a failed call releases its reservation. That's designed to stop parallel calls from all slipping under the cap together (tested with 20 parallel calls).
Do I need Upstash?
No. Without it, limits run on a Postgres counter in your Supabase project. That counter is fixed-window; Upstash gives you a sliding window.
Has it been run against a live Supabase project or live provider accounts?
Not yet. The SQL ran in PGlite and the provider calls are faked in tests. See "What this doesn't do".
Do I get updates?
Free updates within 1.x. Single-user commercial licence.
Refunds?
Digital download: by buying, you ask for immediate access and agree that your 14-day right to cancel ends once the download or access starts. Our voluntary 14-day "if it didn't help" refund guarantee still applies, and your rights for faulty content are not affected.