AI app abuse protection
Automated clients – scrapers, data harvesters, and script-based attackers - treat AI features as free compute. Without bot protection, every request from a bot reaches your AI provider and inflates your costs.
Arcjet bot detection runs inside your application, before the AI call, so denied requests never reach your provider. It classifies known bots, verifies good bots, and detects emerging threats in real time so you can control access per route with full application context (identity, subscription level, session state).
Get started
Section titled “Get started”In this example we use the Vercel AI SDK to create a simple AI chat endpoint with Next.js, and Arcjet to protect it from abuse. The same principles can be applied to any AI application, including those built with other frameworks.
We assume you already have a Next.js app set up.
Install the dependencies:
# Export your Arcjet API key from https://app.arcjet.comexport ARCJET_KEY="ajkey_..."
npm install @arcjet/next ai @ai-sdk/openaiCreate an AI chat endpoint:
import { openai } from "@ai-sdk/openai";import arcjet, { detectBot, shield } from "@arcjet/next";import type { UIMessage } from "ai";import { convertToModelMessages, streamText } from "ai";
const aj = arcjet({ key: process.env.ARCJET_KEY!, // Get your site key from https://app.arcjet.com rules: [ // Shield protects against common web attacks e.g. SQL injection shield({ mode: "LIVE" }), // Block all automated clients — bots inflate AI costs detectBot({ mode: "LIVE", // Blocks requests. Use "DRY_RUN" to log only allow: [], // Block all bots. See https://arcjet.com/bot-list }), ],});
export async function POST(req: Request) { const decision = await aj.protect(req);
if (decision.isDenied()) { if (decision.reason.isBot()) { return new Response("Automated clients are not permitted", { status: 403, }); } return new Response("Forbidden", { status: 403 }); }
// Arcjet approved - now read the body and call your AI provider const { messages }: { messages: UIMessage[] } = await req.json();
const result = await streamText({ model: openai("gpt-4o"), messages: await convertToModelMessages(messages), });
return result.toUIMessageStreamResponse();}And hook it up to a chat UI:
"use client";
import { useChat } from "@ai-sdk/react";import { useState } from "react";
export default function Chat() { const [input, setInput] = useState(""); const [errorMessage, setErrorMessage] = useState<string | null>(null); const { messages, sendMessage } = useChat({ onError: async (e) => setErrorMessage(e.message), }); return ( <div className="flex flex-col w-full max-w-md py-24 mx-auto stretch"> {messages.map((message) => ( <div key={message.id} className="whitespace-pre-wrap"> {message.role === "user" ? "User: " : "AI: "} {message.parts.map((part, i) => { switch (part.type) { case "text": return <div key={`${message.id}-${i}`}>{part.text}</div>; } })} </div> ))}
{errorMessage && ( <div className="text-red-500 text-sm mb-4">{errorMessage}</div> )}
<form onSubmit={(e) => { e.preventDefault(); sendMessage({ text: input }); setInput(""); setErrorMessage(null); }} > <input className="fixed dark:bg-zinc-900 bottom-0 w-full max-w-md p-2 mb-8 border border-zinc-300 dark:border-zinc-800 rounded shadow-xl" value={input} placeholder="Say something..." onChange={(e) => setInput(e.currentTarget.value)} /> </form> </div> );}Then run the server:
npm run devRequests appear in your Arcjet dashboard in real time.
Bot detection is a request-based rule. On an agent, abuse control is a rate limit plus inbound prompt-injection screening so a single actor cannot loop tools or jailbreak the model.
We assume you already have a Claude Agent SDK project set up. For helper options and denial behavior, see the Claude Agent SDK agent guard.
Install the dependencies:
# Export your Arcjet API key from https://app.arcjet.comexport ARCJET_KEY="ajkey_..."
npm install @arcjet/guard @anthropic-ai/claude-agent-sdkCreate the example:
import { query, tool } from "@anthropic-ai/claude-agent-sdk";import { launchArcjet, detectPromptInjection, tokenBucket } from "@arcjet/guard";import { guardHooks, guardTool } from "@arcjet/guard/claude-agent-sdk/v0";import { z } from "zod";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const lookupLimit = tokenBucket({ bucket: "lookups", refillRate: 5, intervalSeconds: 10, maxTokens: 10,});
export const lookupOrder = guardTool( arcjet, tool( "lookup_order", "Look up an order by ID", { orderId: z.string() }, async ({ orderId }) => ({ content: [{ type: "text", text: `${orderId}: shipped` }], }), ), { action: "order.looked-up", rules: (input) => [lookupLimit({ key: input.orderId, requested: 5 })], },);
export async function runAgent(sessionId: string, userText: string) { for await (const message of query({ prompt: userText, options: { sessionId, hooks: guardHooks(arcjet, { sessionId, inbound: { action: "message.received", rules: ({ prompt }) => [detectPromptInjection()(prompt)], }, }), }, })) { void message; }}Then start or invoke the agent with a test prompt.
Requests appear in your Arcjet dashboard in real time.
Bot detection is a request-based rule. On an agent, abuse control is a rate limit plus inbound prompt-injection screening so a single actor cannot loop tools or jailbreak the model.
We assume you already have a Mastra project set up. For helper options and denial behavior, see the Mastra agent guard.
Install the dependencies:
# Export your Arcjet API key from https://app.arcjet.comexport ARCJET_KEY="ajkey_..."
npm install @arcjet/guard @mastra/coreCreate the example:
import { launchArcjet, detectPromptInjection, tokenBucket } from "@arcjet/guard";import { guardProcessor, guardTool } from "@arcjet/guard/mastra/v1";import { Agent } from "@mastra/core/agent";import { createTool } from "@mastra/core/tools";import { z } from "zod";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const lookupLimit = tokenBucket({ bucket: "lookups", refillRate: 5, intervalSeconds: 10, maxTokens: 10,});
export const lookupOrder = guardTool( arcjet, createTool({ id: "lookup-order", description: "Look up an order by ID", inputSchema: z.object({ orderId: z.string() }), async execute({ orderId }) { return { orderId, status: "shipped" }; }, }), { action: "order.looked-up", rules: (input) => [lookupLimit({ key: input.orderId, requested: 5 })], },);
const inbound = guardProcessor(arcjet, { action: "message.received", rules: ({ text }) => [detectPromptInjection()(text)],});
export const agent = new Agent({ id: "support-agent", name: "support-agent", instructions: "Help the user look up orders.", model: "openai/gpt-4o-mini", tools: { lookupOrder }, inputProcessors: [inbound],});Then start or invoke the agent with a test prompt.
Requests appear in your Arcjet dashboard in real time.
Bot detection is a request-based rule. On an agent, abuse control is a rate limit plus inbound prompt-injection screening so a single actor cannot loop tools or jailbreak the model.
We assume you already have a Vercel Eve project set up. For helper options and denial behavior, see the Vercel Eve agent guard.
Install the dependencies:
# Export your Arcjet API key from https://app.arcjet.comexport ARCJET_KEY="ajkey_..."
npm install @arcjet/guard eveCreate the example:
import { launchArcjet, detectPromptInjection, tokenBucket } from "@arcjet/guard";import { guardInbound, guardTool } from "@arcjet/guard/vercel-eve/v0";import { defineTool } from "eve/tools";import { z } from "zod";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const lookupLimit = tokenBucket({ bucket: "lookups", refillRate: 5, intervalSeconds: 10, maxTokens: 10,});
export const lookupOrder = guardTool( arcjet, defineTool({ description: "Look up an order by ID", inputSchema: z.object({ orderId: z.string() }), async execute(input) { return { orderId: input.orderId, status: "shipped" }; }, }), { action: "order.looked-up", rules: (input) => [lookupLimit({ key: input.orderId, requested: 5 })], },);
export async function screenInbound( message: string, conversationId: string,) { const verdict = await guardInbound(arcjet, message, { action: "message.received", correlationId: conversationId, rules: [detectPromptInjection()(message)], });
if (!verdict.allowed) { throw new Error(verdict.message); }}Then start or invoke the agent with a test prompt.
Requests appear in your Arcjet dashboard in real time.
Bot detection is a request-based rule. On an agent, abuse control is a rate limit plus inbound prompt-injection screening so a single actor cannot loop tools or jailbreak the model.
We assume you already have a OpenAI Agents project set up. For helper options and denial behavior, see the OpenAI Agents agent guard.
Install the dependencies:
# Export your Arcjet API key from https://app.arcjet.comexport ARCJET_KEY="ajkey_..."
npm install @arcjet/guard @openai/agentsCreate the example:
import { launchArcjet, detectPromptInjection, tokenBucket } from "@arcjet/guard";import { guardTool, openaiAgentsContext } from "@arcjet/guard/openai-agents/v0";import { Agent, run, tool } from "@openai/agents";import { z } from "zod";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const lookupLimit = tokenBucket({ bucket: "lookups", refillRate: 5, intervalSeconds: 10, maxTokens: 10,});const inbound = detectPromptInjection();
export const lookupOrder = guardTool( arcjet, tool({ name: "lookup_order", description: "Look up an order by ID", parameters: z.object({ orderId: z.string() }), execute: async ({ orderId }) => ({ orderId, status: "shipped" }), }), { action: "order.looked-up", rules: (input) => [lookupLimit({ key: input.orderId, requested: 5 })], },);
const agent = new Agent({ name: "support-agent", instructions: "Help the user look up orders.", tools: [lookupOrder],});
export async function runAgent(conversationId: string, userText: string) { const appContext = { sessionId: conversationId }; const decision = await arcjet.guard({ label: "message.received", rules: [inbound(userText)], ...openaiAgentsContext({ context: appContext, conversationId }), });
if (decision.conclusion === "DENY" || decision.hasFailedOpen()) { throw new Error("Message blocked"); }
return run(agent, userText, { context: appContext });}Then start or invoke the agent with a test prompt.
Requests appear in your Arcjet dashboard in real time.
Bot detection is a request-based rule. On an agent, abuse control is a rate limit plus inbound prompt-injection screening so a single actor cannot loop tools or jailbreak the model.
We assume you already have a LangGraph project set up. For helper options and denial behavior, see the LangGraph agent guard.
Install the dependencies:
# Export your Arcjet API key from https://app.arcjet.comexport ARCJET_KEY="ajkey_..."
npm install @arcjet/guard @langchain/langgraph @langchain/coreCreate the example:
import { launchArcjet, detectPromptInjection, tokenBucket } from "@arcjet/guard";import { guardTool, langgraphAgentContext } from "@arcjet/guard/langgraph/v1";import { tool } from "@langchain/core/tools";import { z } from "zod";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const lookupLimit = tokenBucket({ bucket: "lookups", refillRate: 5, intervalSeconds: 10, maxTokens: 10,});const inbound = detectPromptInjection();
export const lookupOrder = guardTool( arcjet, tool( async ({ orderId }) => ({ orderId, status: "shipped" }), { name: "lookup_order", description: "Look up an order by ID", schema: z.object({ orderId: z.string() }), }, ), { action: "order.looked-up", rules: (input) => [lookupLimit({ key: input.orderId, requested: 5 })], },);
export async function runAgent( graph: { invoke: Function }, conversationId: string, userText: string,) { const config = { configurable: { thread_id: conversationId } }; const decision = await arcjet.guard({ label: "message.received", rules: [inbound(userText)], ...langgraphAgentContext(config), });
if (decision.conclusion === "DENY" || decision.hasFailedOpen()) { throw new Error("Message blocked"); }
return graph.invoke( { messages: [{ role: "user", content: userText }] }, config, );}Then start or invoke the agent with a test prompt.
Requests appear in your Arcjet dashboard in real time.
Bot detection is a request-based rule. On an agent, abuse control is a rate limit plus inbound prompt-injection screening so a single actor cannot loop tools or jailbreak the model.
We assume you already have a LangChain project set up. For helper options and denial behavior, see the LangChain agent guard.
Install the dependencies:
# Export your Arcjet API key from https://app.arcjet.comexport ARCJET_KEY="ajkey_..."export ARCJET_ENV=development
pip install "arcjet[langchain-agents]" langchain langchain-openaiCreate the example:
import os
from arcjet.guard import DetectPromptInjection, TokenBucket, launch_arcjetfrom arcjet.guard.langchain import ArcjetMiddleware, ToolPolicyfrom langchain.agents import create_agentfrom langchain_core.tools import tool
arcjet = launch_arcjet(key=os.environ["ARCJET_KEY"])inbound = DetectPromptInjection()lookup_limit = TokenBucket( refill_rate=5, interval_seconds=10, max_tokens=10, bucket="lookups",)
@toolasync def lookup_order(order_id: str) -> dict: """Look up an order by ID.""" return {"order_id": order_id, "status": "shipped"}
agent = create_agent( model="openai:gpt-4o-mini", tools=[lookup_order], middleware=[ ArcjetMiddleware( guard=arcjet, policies={ "lookup_order": ToolPolicy( action="order.looked-up", rules=[lookup_limit(key="user123", requested=5)], ) }, tools=[lookup_order], ) ],)
async def run_agent(user_id: str, prompt: str): decision = await arcjet.guard( label="message.received", actor=user_id, rules=[inbound(prompt)], ) if decision.conclusion == "DENY" or decision.has_failed_open(): raise RuntimeError("Message blocked")
return await agent.ainvoke( {"messages": [{"role": "user", "content": prompt}]}, config={"configurable": {"thread_id": user_id}}, )Then start or invoke the agent with a test prompt.
Requests appear in your Arcjet dashboard in real time.
Bot detection is a request-based rule. On an agent, abuse control is a rate limit plus inbound prompt-injection screening so a single actor cannot loop tools or jailbreak the model.
We assume you already have a Genkit project set up. For helper options and denial behavior, see the Genkit agent guard.
Until @arcjet/guard/genkit/v1 is published, the import lives on
4e416787,
not in the published @arcjet/guard package on npm.
Install the dependencies:
# Export your Arcjet API key from https://console.arcjet.comexport ARCJET_KEY="ajkey_..."
npm install @arcjet/guard genkitCreate the example:
import { launchArcjet, detectPromptInjection, tokenBucket } from "@arcjet/guard";import { guardTool, guardMiddleware, genkitContext,} from "@arcjet/guard/genkit/v1";import { genkit, z } from "genkit";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });const ai = genkit({ // Configure your model plugin.});
const lookupLimit = tokenBucket({ bucket: "lookups", refillRate: 5, intervalSeconds: 10, maxTokens: 10,});const inbound = detectPromptInjection();
export const lookupOrder = guardTool( arcjet, ai.defineTool( { name: "lookup_order", description: "Look up an order by ID", inputSchema: z.object({ orderId: z.string() }), }, async ({ orderId }) => ({ orderId, status: "shipped" }), ), { action: "order.looked-up", rules: (input) => [lookupLimit({ key: input.orderId, requested: 5 })], },);
export async function runAgent(conversationId: string, userText: string) { const appContext = { sessionId: conversationId }; const decision = await arcjet.guard({ label: "message.received", rules: [inbound(userText)], ...genkitContext({ context: appContext }), });
if (decision.conclusion === "DENY" || decision.hasFailedOpen()) { throw new Error("Message blocked"); }
return ai.generate({ prompt: userText, tools: [lookupOrder], use: [guardMiddleware(arcjet, { sessionId: conversationId })], context: appContext, });}Then start or invoke the agent with a test prompt.
Requests appear in your Arcjet dashboard in real time.
Bot detection is a request-based rule. On an agent, abuse control is a rate limit plus inbound prompt-injection screening so a single actor cannot loop tools or jailbreak the model.
We assume you already have a Vercel AI SDK project set up. For helper options and denial behavior, see the Vercel AI SDK agent guard.
Install the dependencies:
# Export your Arcjet API key from https://app.arcjet.comexport ARCJET_KEY="ajkey_..."
npm install @arcjet/guard ai @ai-sdk/provider-utilsCreate the example:
import { launchArcjet, detectPromptInjection, tokenBucket } from "@arcjet/guard";import { aiToolsContext, createAgentContext, guardTool,} from "@arcjet/guard/vercel-ai/v7";import { generateText, tool } from "ai";import { z } from "zod";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const lookupLimit = tokenBucket({ bucket: "lookups", refillRate: 5, intervalSeconds: 10, maxTokens: 10,});const inbound = detectPromptInjection();
export async function runAgent(userId: string, prompt: string) { const decision = await arcjet.guard({ label: "message.received", actor: userId, rules: [inbound(prompt)], });
if (decision.conclusion === "DENY" || decision.hasFailedOpen()) { throw new Error("Message blocked"); }
const lookupOrder = guardTool( arcjet, tool({ description: "Look up an order by ID", inputSchema: z.object({ orderId: z.string() }), execute: async ({ orderId }) => ({ orderId, status: "shipped" }), }), { action: "order.looked-up", actor: userId, rules: () => [lookupLimit({ key: userId, requested: 5 })], }, );
const tools = { lookupOrder }; const context = createAgentContext({ correlationId: userId });
return generateText({ model: "openai/gpt-4o-mini", prompt, tools, toolsContext: aiToolsContext(context, tools), });}Then start or invoke the agent with a test prompt.
Requests appear in your Arcjet dashboard in real time.
In this example we use LangChain to create a simple AI chat server with Flask, and Arcjet to protect it from abuse. The same principles can be applied to any AI application, including those built with other frameworks.
Set up the environment and install dependencies (uses uv, but you can also use pip to install the Arcjet Python SDK):
# Export your Arcjet API key from https://app.arcjet.comexport ARCJET_KEY="ajkey_..."export ARCJET_ENV=development
# Export your OpenAI API key (used by LangChain)export OPENAI_API_KEY="sk-..."
# Install dependenciesuv add arcjet flask langchain langchain-openaiCreate the chat server:
import loggingimport os
from arcjet import Mode, arcjet_sync, detect_bot, shieldfrom flask import Flask, jsonify, requestfrom langchain_core.output_parsers import StrOutputParserfrom langchain_core.prompts import ChatPromptTemplatefrom langchain_openai import ChatOpenAI
app = Flask(__name__)
logging.basicConfig(level=logging.INFO)logger = logging.getLogger(__name__)
arcjet_key = os.getenv("ARCJET_KEY")if not arcjet_key: raise RuntimeError("ARCJET_KEY is required. Get one at https://app.arcjet.com")
openai_api_key = os.getenv("OPENAI_API_KEY")if not openai_api_key: raise RuntimeError( "OPENAI_API_KEY is required. Get one at https://platform.openai.com" )
llm = ChatOpenAI(model="gpt-4o-mini", api_key=openai_api_key)
prompt = ChatPromptTemplate.from_messages( [ ("system", "You are a helpful assistant."), ("human", "{message}"), ])
chain = prompt | llm | StrOutputParser()
# Create a single Arcjet client at startup and reuse it across requestsaj = arcjet_sync( key=arcjet_key, # Get your key from https://app.arcjet.com rules=[ # Shield protects against common web attacks e.g. SQL injection shield(mode=Mode.LIVE), # Block all automated clients — bots inflate AI costs detect_bot( mode=Mode.LIVE, # Blocks requests. Use Mode.DRY_RUN to log only allow=[ "CURL", # Allow curl so we can test it (see README) # Uncomment to allow these other common bot categories # See the full list at https://arcjet.com/bot-list # BotCategory.MONITOR, # Uptime monitoring services # BotCategory.PREVIEW, # Link previews e.g. Slack, Discord ], ), ],)
@app.post("/chat")def chat(): decision = aj.protect(request)
if decision.is_denied(): if decision.reason_v2.type == "BOT": return jsonify(error="Automated clients are not permitted"), 403 return jsonify(error="Forbidden"), 403
# Arcjet approved — proceed with the AI call body = request.get_json() message = body.get("message", "") if body else "" reply = chain.invoke({"message": message})
return jsonify(reply=reply)
if __name__ == "__main__": app.run(debug=True)Then run the server:
uv run python app.pyAnd send a message to the API endpoint:
curl -X POST http://localhost:5000/chat \ -H "Content-Type: application/json" \ -d '{"message": "What is the capital of France?"}'Requests appear in your Arcjet dashboard in real time.
In this example we use LangChain to create a simple AI chat server with FastAPI, and Arcjet to protect it from abuse. The same principles can be applied to any AI application, including those built with other frameworks.
Set up the environment and install dependencies (uses uv, but you can also use pip to install the Arcjet Python SDK):
# Export your Arcjet API key from https://app.arcjet.comexport ARCJET_KEY="ajkey_..."export ARCJET_ENV=development
# Export your OpenAI API key (used by LangChain)export OPENAI_API_KEY="sk-..."
# Install dependenciesuv add arcjet fastapi uvicorn langchain langchain-openaiCreate the chat server:
import loggingimport os
from arcjet import Mode, arcjet, detect_bot, shieldfrom fastapi import FastAPI, Requestfrom fastapi.responses import JSONResponsefrom langchain_core.output_parsers import StrOutputParserfrom langchain_core.prompts import ChatPromptTemplatefrom langchain_openai import ChatOpenAIfrom pydantic import BaseModel
app = FastAPI()
logging.basicConfig(level=logging.INFO)logger = logging.getLogger(__name__)
arcjet_key = os.getenv("ARCJET_KEY")if not arcjet_key: raise RuntimeError("ARCJET_KEY is required. Get one at https://app.arcjet.com")
openai_api_key = os.getenv("OPENAI_API_KEY")if not openai_api_key: raise RuntimeError( "OPENAI_API_KEY is required. Get one at https://platform.openai.com" )
llm = ChatOpenAI(model="gpt-4o-mini", api_key=openai_api_key)
prompt = ChatPromptTemplate.from_messages( [ ("system", "You are a helpful assistant."), ("human", "{message}"), ])
chain = prompt | llm | StrOutputParser()
class ChatRequest(BaseModel): message: str
# Create a single Arcjet client at startup and reuse it across requestsaj = arcjet( key=arcjet_key, # Get your key from https://app.arcjet.com rules=[ # Shield protects against common web attacks e.g. SQL injection shield(mode=Mode.LIVE), # Block all automated clients — bots inflate AI costs detect_bot( mode=Mode.LIVE, # Blocks requests. Use Mode.DRY_RUN to log only allow=[ "CURL", # Allow curl so we can test it (see README) # Uncomment to allow these other common bot categories # See the full list at https://arcjet.com/bot-list # BotCategory.MONITOR, # Uptime monitoring services # BotCategory.PREVIEW, # Link previews e.g. Slack, Discord ], ), ],)
@app.post("/chat")async def chat(request: Request, body: ChatRequest): decision = await aj.protect(request)
if decision.is_denied(): if decision.reason_v2.type == "BOT": return JSONResponse( {"error": "Automated clients are not permitted"}, status_code=403 ) return JSONResponse({"error": "Forbidden"}, status_code=403)
# Arcjet approved — proceed with the AI call reply = await chain.ainvoke({"message": body.message})
return {"reply": reply}Then run the server:
uv run uvicorn main:app --reloadAnd send a message to the API endpoint:
curl -X POST http://localhost:8000/chat \ -H "Content-Type: application/json" \ -d '{"message": "What is the capital of France?"}'Requests appear in your Arcjet dashboard in real time.
Configure bot detection
Section titled “Configure bot detection”allow: [] blocks all automated clients. This is the recommended default for AI
routes where no bot traffic is legitimate.
To allow specific categories or named bots from our list of known bots, add them to the allow list:
detectBot({ mode: "LIVE", allow: [ "CURL", // Allow curl-based scripts "CATEGORY:MONITOR", // Uptime monitoring services "CATEGORY:PREVIEW", // Link previewers (Slack, Discord, etc.) ],})detect_bot( mode=Mode.LIVE, allow=[ "CURL", # Allow curl-based scripts BotCategory.MONITOR, # Uptime monitoring services BotCategory.PREVIEW, # Link previewers (Slack, Discord, etc.) ],)Budget control
Section titled “Budget control”Bot protection controls who can call your AI features. To also control how much each user can consume, combine it with AI budget control:
rules: [ detectBot({ mode: "LIVE", allow: [] }), tokenBucket({ // Token bucket rate limiting is best for AI budget control mode: "LIVE", characteristics: ["userId"], // Link limits to users refillRate: 2_000, // Refill 2000 tokens per interval interval: "1h", // Refill interval capacity: 5_000, // Max tokens }),]rules=[ detect_bot(mode=Mode.LIVE, allow=[]), # Token bucket rate limiting is best for AI budget control token_bucket( mode=Mode.LIVE, characteristics=["userId"], # Link limits to users refill_rate=2_000, # Refill 2000 tokens per interval interval=3_600, # Refill interval in seconds (1 hour) capacity=5_000, # Max tokens ),]The get started guide shows the combined pattern.
Prompt injection detection
Section titled “Prompt injection detection”Bot protection controls who can call your AI features, but legitimate users can still submit malicious prompts. Combine bot detection with prompt injection detection to also block jailbreaks, role-play escapes, and instruction overrides before they reach your AI model.