Updated · 3 min read
the short answer
How do I rate limit a Supabase Edge Function?
Edge Functions have no built-in per-user limit, so add one in the function: identify the caller (user ID, or IP for public forms), count their recent requests, and return 429 when they go over. A table in Postgres works for small apps; Upstash Redis is faster and handles bursts. Start with the functions that cost money per call: AI models, email, SMS and file processing.
Why AI-built apps need this first
In our 2026 study, 441 of the 1,029 AI-built apps called an AI model themselves; 5% of those had an AI endpoint anyone could use, and 6% had a cost risk someone else could trigger. 4% of all apps had an endpoint with no limit on how often it can be called. A script calling your chat function in a loop costs you money for every request, and it does not need to break anything to do it.
Which endpoints to limit
- AI calls (chat, image generation, summaries): per user, per minute and per day
- Email and SMS sending (contact forms, invites, password reset): per IP and per recipient
- Sign-up and login: Supabase Auth has its own limits, check them in the dashboard
- File uploads and anything that runs a long job
Option 1: a Postgres table (no extra service)
Log each request and count the last minute before doing the work. Use the service role key only inside the function. Under heavy parallel load two requests can slip through at once; for a small app that is fine.
// inside the Edge Function, after you checked the user (see the Edge Functions guide)
const admin = createClient(Deno.env.get("SUPABASE_URL")!, Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!);
const since = new Date(Date.now() - 60_000).toISOString();
const { count } = await admin
.from("ai_requests")
.select("*", { count: "exact", head: true })
.eq("user_id", user.id)
.gte("created_at", since);
if ((count ?? 0) >= 10) return new Response("Too many requests", { status: 429 });
await admin.from("ai_requests").insert({ user_id: user.id });Option 2: Upstash Redis (faster, handles bursts)
Upstash has a free tier and a rate limit library that works in Deno.
import { Ratelimit } from "npm:@upstash/ratelimit";
import { Redis } from "npm:@upstash/redis";
const ratelimit = new Ratelimit({
redis: new Redis({ url: Deno.env.get("UPSTASH_REDIS_REST_URL")!, token: Deno.env.get("UPSTASH_REDIS_REST_TOKEN")! }),
limiter: Ratelimit.slidingWindow(10, "1 m"),
});
const { success } = await ratelimit.limit(user.id);
if (!success) return new Response("Too many requests", { status: 429 });Limits are not the only brake
- Require login for AI features, so every request has a user to count
- Set max_tokens on every model call
- Set a monthly spending limit and alerts at the AI provider
- Never call the AI provider from the browser: the key would be public
Questions
Does Supabase have built-in rate limiting?
Supabase Auth limits sign-ups, logins and emails. Edge Functions and database calls from your own code have no per-user limit, so you add it yourself.
Should I limit by user ID or IP address?
By user ID for features behind login, because IPs change and are shared. By IP (and recipient) for public forms like contact and password reset.
What limit should I set for an AI chat feature?
Start with what a real user needs, for example 10 messages a minute and 200 a day, then watch your logs. A daily cap per user is what protects your bill.
How do I rate limit a Next.js API route?
The same way: identify the caller, count in Redis or your database, return 429. The Upstash library works in Next.js route handlers and middleware too.