All stories
Incident

The $4,000 Vercel Bill: An API Route With No Rate Limit

ChatGPT wrote an API route without rate limiting. A bot discovered it within hours and ran up a $4k serverless compute bill overnight.

The Build

It started as a weekend project. A developer — let’s call her Priya — asked ChatGPT to scaffold a Next.js API route that fetched data from a third-party API, transformed it, and returned JSON. Standard stuff. The generated code was clean, well-typed, and worked on the first try.

She deployed to Vercel, tested it once in the browser, and went to bed.

The Bot

By 3 AM, a crawler had discovered the endpoint. It wasn’t a sophisticated attack — just an automated bot that probes common API paths (/api/data, /api/search, /api/users) looking for open endpoints. It found Priya’s route and started hammering it.

Each request triggered a serverless function invocation. Each invocation made an outbound fetch to the third-party API. The function ran for 800ms on average. The bot was sending 20 requests per second.

The Math

Priya woke up to a billing alert email. By then, the damage was done.

What the AI Missed

The generated API route had proper error handling, TypeScript types, and even input validation. It was genuinely good code — for a tutorial. But it was missing three things that any production-ready API route needs:

  1. Rate limiting — No per-IP or per-session throttle.
  2. Authentication — The route was publicly accessible with no auth check.
  3. Cost ceiling — No spending alert or automatic disable threshold on Vercel.

ChatGPT doesn’t think about operational costs. It generates code that works, not code that survives the open internet.

The Fix

// What the AI should have generated
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';

const ratelimit = new Ratelimit({
  redis: Redis.fromEnv(),
  limiter: Ratelimit.slidingWindow(10, '60 s'),
});

export async function GET(request: Request) {
  const ip = request.headers.get('x-forwarded-for') ?? '127.0.0.1';
  const { success } = await ratelimit.limit(ip);

  if (!success) {
    return new Response('Too many requests', { status: 429 });
  }

  // ... actual logic
}

The Takeaway

AI-generated API routes are feature-complete but operationally naive. They don’t model the economics of serverless, the behavior of automated crawlers, or the reality that every public endpoint will be discovered and probed within hours of deployment. Monitoring for anomalous traffic patterns and exposed unprotected endpoints is not optional — it’s the difference between a $0 weekend project and a $4,000 lesson.