AI app abuse protection for Claude Managed Agents
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”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 Managed Agents project set up. For helper options and denial behavior, see the Claude Managed Agents agent guard.
Install the dependencies:
# Export your Arcjet API key from https://console.arcjet.comexport ARCJET_KEY="ajkey_..."export ARCJET_ENV=development
pip install "arcjet[claude-managed-agents]" anthropicCreate the example:
import os
from anthropic import AsyncAnthropicfrom arcjet.guard import ( ArcjetDeniedError, DetectPromptInjection, TokenBucket, launch_arcjet,)from arcjet.guard.claude_managed_agents import ( guard_custom_tool, guard_events,)
arcjet = launch_arcjet(key=os.environ["ARCJET_KEY"])
# guard_events runs an inbound check before each user.message reaches the# session, so use the async client: the sync one can't be awaited here.client = AsyncAnthropic()inbound = DetectPromptInjection()lookup_limit = TokenBucket( refill_rate=5, interval_seconds=10, max_tokens=10, bucket="lookups",)
async def lookup_order(event) -> dict: return {"order_id": event.input["order_id"], "status": "shipped"}
# Pass run= for the hosted path. Call the result with the# agent.custom_tool_use event, the send callable, and the Anthropic session# id, so a denial can be posted as the tool result.guarded_lookup = guard_custom_tool( guard=arcjet, run=lookup_order, action="order.looked-up", rules=lambda arguments: [ lookup_limit(key=arguments["order_id"], requested=5) ],)
# guard_events wraps send. On DENY it raises and never calls the real send,# so the model never sees the prompt.send = guard_events( guard=arcjet, send=client.beta.sessions.events.send, action="message.received", rules=lambda arguments: [inbound(arguments["prompt"])],)
async def send_turn(session_id: str, user_text: str) -> bool: try: await send( session_id, events=[ { "type": "user.message", "content": [{"type": "text", "text": user_text}], } ], ) except ArcjetDeniedError: return False return TrueThen 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 Claude Managed Agents project set up. For helper options and denial behavior, see the Claude Managed Agents agent guard.
Install the dependencies:
# Export your Arcjet API key from https://console.arcjet.comexport ARCJET_KEY="ajkey_..."
npm install @arcjet/guard@git+https://github.com/arcjet/arcjet-js.git#cb35c8f92c3a2fb63fbeb9b386d79b1878c19d92 @anthropic-ai/sdkCreate the example:
import Anthropic from "@anthropic-ai/sdk";import { launchArcjet, detectPromptInjection, tokenBucket,} from "@arcjet/guard";import { claudeManagedAgentsContext, guardCustomTool, guardEvents,} from "@arcjet/guard/claude-managed-agents/v0";import type { AgentCustomToolUseEvent } from "@arcjet/guard/claude-managed-agents/v0";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });const client = new Anthropic();
const lookupLimit = tokenBucket({ bucket: "lookups", refillRate: 5, intervalSeconds: 10, maxTokens: 10,});
// Anthropic has already chosen the tool by the time this event arrives, so// the gate goes around the body your app executes. On deny, guardCustomTool// sends the error result itself and the body never runs.export function lookupOrder( event: AgentCustomToolUseEvent, sessionId: string, conversationId: string,) { return guardCustomTool( arcjet, { event, execute: async (input) => ({ orderId: String(input.orderId), status: "shipped", }), send: (result) => client.beta.sessions.events.send(sessionId, { events: [result] }), }, { action: "order.looked-up", rules: (input) => [ lookupLimit({ key: String(input.orderId), requested: 5 }), ], // Correlation is your own conversation id, never the Anthropic // session id. context: claudeManagedAgentsContext({ correlationId: conversationId }), }, );}
export async function sendTurn( sessionId: string, conversationId: string, userText: string,) { // guardEvents screens the prompt and only then sends `user.message`, so on // deny the model never sees it. const inbound = await guardEvents( arcjet, { events: [ { type: "user.message", content: [{ type: "text", text: userText }] }, ], inbound: { action: "message.received", rules: ({ text }) => [detectPromptInjection()(text)], }, context: claudeManagedAgentsContext({ correlationId: conversationId }), }, (body) => client.beta.sessions.events.send(sessionId, body), );
if (!inbound.allowed) { throw new Error(inbound.message); }}Then start or invoke the agent with a test prompt.
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.