BotID gegen AI-Budget-Abuse

Jeder POST gegen unseren /api/chat kostet uns Geld — er löst eine billable streamText()-Call gegen das Vercel AI Gateway aus. Ohne Schutz reicht ein simples Bash-Script und ein Stündchen Wartezeit, um $50 Cloud-Budget zu verbrennen. Hier ist, wie wir das gelöst haben.

Das Problem

Unser AI-Chat-Widget steht öffentlich auf der Website. Der Use-Case ist klar: ein:e Besucher:in fragt „passt euer Pricing zu meinem Projekt?", das Modell antwortet mit Vorschlägen, das Lead kommt qualifiziert. Funktioniert.

Das Problem: der Endpoint ist öffentlich. Jede:r kann posten. Im Sommer hatten wir einen Test-Lauf mit einem Bot-Script, der 1000 Anfragen in 10 Minuten geschickt hat — Resultat: $8 AI-Cost in einer Stunde, alles Müll-Anfragen. Bei einer Dauerwelle wären das pro Tag dreistellig.

Was wir zuerst probiert haben

Rate Limits per IP

Klassisch: pro IP nur N Requests in T Sekunden. Funktioniert für naive Bots, scheitert bei distributed Botnets. Ein billiges Residential-Proxy-Subscription kostet $20/Monat und liefert tausende rotierende IPs. Die Ökonomie ist gegen uns.

Honeypot Field

Ein verstecktes Form-Feld, das echte User nie ausfüllen. Manche Scraper schon. Hilft gegen die Hälfte der naiven Bots, gegen Headless-Chrome aber gar nicht.

reCAPTCHA / hCaptcha

Funktioniert, aber: jede:r echte User muss klicken. UX-Drop messbar. Und für DSGVO-Compliance mit US-CAPTCHA-Anbietern ist Auftragsverarbeitung notwendig. Wollten wir nicht.

Was wir gemacht haben: Vercel BotID

BotID ist Vercels Bot-Detection-Service, powered by Kasada. Drei Eigenschaften, die für uns entschieden haben:

Invisible: kein CAPTCHA, kein „I'm not a robot"-Klick. Erkennt anhand von Browser-Fingerprint + Interaction-Signals (Mouse-Movement-Pattern, Keyboard-Timing).

Dynamic: Detection-Methoden rotieren pro Page-Load. Macht Reverse-Engineering durch Angreifer extrem aufwendig.

EU-konform: Vercel als Auftragsverarbeiter mit AVV-Coverage.

Integration in 4 Schritten

1. Package installieren:

npm install botid

2. next.config.ts wrappen — das wired Middleware-Signals:

import { withBotId } from "botid/next/config";

const nextConfig: NextConfig = { /* … */ };
export default withBotId(nextConfig);

3. <BotIdClient> ins app/layout.tsx einbauen — das sammelt Client-Signals und attached sie an Fetches gegen die geschützten Routen:

import { BotIdClient } from "botid/client";

const PROTECT = [
{ path: "/api/chat", method: "POST" as const },
{ path: "/api/contact", method: "POST" as const },
];

export default function Layout({ children }) {
return (


<BotIdClient protect={PROTECT} />
{children}


);
}

4. Im Route-Handler checkBotId() aufrufen, vor jeglichem teuren Call:

import { checkBotId } from "botid/server";

export async function POST(req: Request) {
const bot = await checkBotId();
if (bot.isBot && !bot.bypassed) {
return Response.json(
{ error: "rate_limited" },
{ status: 429 }
);
}

// teurer streamText() Call läuft nur für Menschen
const result = streamText({ model: "openai/gpt-5-mini", ... });
return result.toUIMessageStreamResponse();
}

Wichtig: das richtige Verhalten bei Bot-Detection

Wir geben Bots 200 OK mit benigner JSON-Antwort oder 429 — nie 401/403. Wenn der Bot weiß dass er erkannt wurde, optimiert er. Wenn er denkt er kommt durch, ist's ihm schlicht egal — er bekommt nur keinen AI-Output.

Pretend success. Lass den Bot in den Sand laufen, statt ihm zu sagen warum.

Was wir gelernt haben

Server-only Detection ist schwach

Wer nur checkBotId() aufruft ohne <BotIdClient> einzubauen, kriegt nur UA + Header-basierte Detection. Headless-Chrome mit gespoofter UA kommt da locker durch. Erst der Client-Layer macht's robust.

Listen geschützter Routen einmal pflegen

Zentral in app/layout.tsx. Wenn ein neuer expensive Endpoint dazukommt, in beiden Stellen ergänzen: BotIdClient.protect[] + checkBotId()-Call im Route-Handler.

Test mit echtem cURL

Nach Deploy: curl -X POST https://deine-domain.com/api/chat -d '{...}' — sollte 429 zurückgeben (kein Browser-Signal vorhanden). Wenn da 200 mit AI-Antwort kommt, ist die Detection nicht aktiv.

Was es uns gekostet hat

BotID ist bei Vercel im Pro-Plan inkludiert (Deep-Analysis kostet extra für sehr hohe Volumes). Setup-Zeit: 20 Minuten. Wartung: null. Kein eigener Anti-Abuse-Code, kein CAPTCHA-Service, kein User-Friction.

Ergebnis nach 4 Wochen: AI-Gateway-Spend pro Tag stabil unter $1, vorher war Spike-Range $5-$15 abhängig vom Bot-Traffic. Auf Jahressicht spart das ~$3000 — und wichtiger: macht den Endpoint dauerhaft verteidigbar.

Wenn dein API-Endpoint pro Call mehr kostet als 0,1¢ und öffentlich ist, brauchst du Bot-Detection. Heute. Nicht nächste Woche.