Introducing the Arcjet Python SDK beta
The Arcjet Python SDK allows you to implement rate limiting, bot detection, email validation, and signup spam prevention in FastAPI and Flask style applications.
Introducing Arcjet prompt injection detection. Catch hostile instructions before inference. Works with Next.js, Node.js, Flask, FastAPI, and any JavaScript / TypeScript or Python application.
AI features are shipping into production faster than security review cycles. One of the first security problems engineering teams hit is prompt injection.
Attackers probe AI endpoints with jailbreaks and hostile instructions designed to override system behavior, expose hidden prompts, or extract data from model context. If you only find those issues in testing or in logs after launch, you are already late.
At Arcjet, we think protecting AI in production needs inline enforcement inside the request lifecycle where you have identity, route, session, and business context.
Today we’re introducing Arcjet AI prompt injection protection.
It detects risky prompts before they reach the model, so you can block obvious injection and jailbreak attempts at the boundary.
Prompt injection turns user input into control input. In practice, that means attackers try prompts like:
And that's just the beginning. You also have to protect against indirect injections (HTML comment injection), encoding attacks (base64, hex, ROT13, ASCII, emoji ciphers), instruction exploits (translations, variable expansion, config injection) and structural patterns (ChatML injection, many-shot, sandwich attacks).
This matters anywhere you expose AI features to users:
Once hostile instructions are in the context window, you are depending on the model to behave perfectly under adversarial input.
You need a decision point completely under your control before the model runs.
Arcjet’s advantage has always been enforcement inside the application layer, not just visibility after the fact. That same approach applies to AI.
Prompt injection protection is the next Arcjet building block for teams shipping AI in production. It gives you a decision point before the model runs, where you can block hostile instructions instead of hoping the model handles them correctly.
The goal is simple: make AI endpoints safer to ship in production.
A production chat endpoint needs more than one guardrail.
Some requests contain hostile instructions designed to override your system prompt. Others may be legitimate user requests that still contain sensitive data you do not want entering model context. And like any other public route, AI endpoints still need protection from common web attacks.
And now Arcjet prompt injection detection catches hostile instructions before inference. We've focused on prompt-extraction and shell-injection protection for this release, but this is just the first of multiple layers of protection Arcjet will offer.
Let's look at some code examples.
This is a chat endpoint using the Vercel JS AI SDK. Arcjet is configured with all of the above protections in just a few lines of code.
import { openai } from "@ai-sdk/openai";
import arcjet, {
detectBot,
detectPromptInjection,
sensitiveInfo,
shield,
tokenBucket,
} from "@arcjet/next";
import type { UIMessage } from "ai";
import { convertToModelMessages, isTextUIPart, streamText } from "ai";
const aj = arcjet({
key: process.env.ARCJET_KEY!, // Get your site key from https://app.arcjet.com
// Track budgets per user — replace "userId" with any stable identifier
characteristics: ["userId"],
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
}),
// Enforce budgets to control AI costs. Adjust rates and limits as needed.
tokenBucket({
mode: "LIVE", // Blocks requests. Use "DRY_RUN" to log only
refillRate: 2_000, // Refill 2,000 tokens per hour
interval: "1h",
capacity: 5_000, // Maximum 5,000 tokens in the bucket
}),
// Block messages containing sensitive information to prevent data leaks
sensitiveInfo({
mode: "LIVE", // Blocks requests. Use "DRY_RUN" to log only
// Block PII types that should never appear in AI prompts.
// Remove types your app legitimately handles (e.g. EMAIL for a support bot).
deny: ["CREDIT_CARD_NUMBER", "EMAIL"],
}),
// Detect prompt injection attacks before they reach your AI model
detectPromptInjection({
mode: "LIVE", // Blocks requests. Use "DRY_RUN" to log only
}),
],
});
export async function POST(req: Request) {
// Replace with your session/auth lookup to get a stable user ID
const userId = "user-123";
const { messages }: { messages: UIMessage[] } = await req.json();
const modelMessages = await convertToModelMessages(messages);
// Estimate token cost: ~1 token per 4 characters of text (rough heuristic).
// For accurate counts use https://www.npmjs.com/package/tiktoken
const totalChars = modelMessages.reduce((sum, m) => {
const content =
typeof m.content === "string" ? m.content : JSON.stringify(m.content);
return sum + content.length;
}, 0);
const estimate = Math.ceil(totalChars / 4);
// Check the most recent user message for sensitive information and prompt injection.
// Pass the full conversation if you want to scan all messages.
const lastMessage: string = (messages.at(-1)?.parts ?? [])
.filter(isTextUIPart)
.map((p) => p.text)
.join(" ");
// Check with Arcjet before calling the AI provider
const decision = await aj.protect(req, {
userId,
requested: estimate,
sensitiveInfoValue: lastMessage,
detectPromptInjectionMessage: lastMessage,
});
if (decision.isDenied()) {
if (decision.reason.isBot()) {
return new Response("Automated clients are not permitted", {
status: 403,
});
} else if (decision.reason.isRateLimit()) {
return new Response("AI usage limit exceeded", { status: 429 });
} else if (decision.reason.isSensitiveInfo()) {
return new Response("Sensitive information detected", { status: 400 });
} else if (decision.reason.isPromptInjection()) {
return new Response(
"Prompt injection detected — please rephrase your message",
{ status: 400 },
);
} else {
return new Response("Forbidden", { status: 403 });
}
}
const result = await streamText({
model: openai("gpt-4o"),
messages: modelMessages,
});
return result.toUIMessageStreamResponse();
}Check out the full get started guide for the details.
You can also do the same with Arcjet's Python SDK:
import logging
import os
from arcjet import (
Mode,
arcjet,
detect_bot,
detect_prompt_injection,
shield,
token_bucket,
)
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from 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
aj = arcjet(
key=arcjet_key, # Get your key from https://app.arcjet.com
rules=[
# Shield protects your app from common attacks e.g. SQL injection
shield(mode=Mode.LIVE),
# Create a bot detection rule
detect_bot(
mode=Mode.LIVE,
# An empty allow list blocks all bots, which is a good default for
# an AI chat app
allow=[
"CURL", # Allow curl so we can test it
# 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
],
),
# Create a token bucket rate limit. Other algorithms are supported
token_bucket(
# Track budgets by arbitrary characteristics of the request. Here
# we use user ID, but you could pass any value. Removing this will
# fall back to IP-based rate limiting.
characteristics=["userId"],
mode=Mode.LIVE,
refill_rate=5, # Refill 5 tokens per interval
interval=10, # Refill every 10 seconds
capacity=10, # Bucket capacity of 10 tokens
),
# Detect prompt injection attacks before they reach your AI model
detect_prompt_injection(
mode=Mode.LIVE, # Blocks requests. Use Mode.DRY_RUN to log only
),
],
)
@app.post("/chat")
async def chat(request: Request, body: ChatRequest):
# Replace with actual user ID from the user session
userId = "your_user_id"
# Call protect() to evaluate the request against the rules
decision = await aj.protect(
request,
# Deduct 5 tokens from the bucket
requested=5,
# Identify the user for rate limiting purposes
characteristics={"userId": userId},
# Check the user message for prompt injection
detect_prompt_injection_message=body.message,
)
# Handle denied requests
if decision.is_denied():
if decision.reason.is_prompt_injection():
return JSONResponse(
{"error": "Prompt injection detected — please rephrase your message"},
status_code=400,
)
status = 429 if decision.reason.is_rate_limit() else 403
return JSONResponse({"error": "Denied"}, status_code=status)
# All rules passed, proceed with handling the request
reply = await chain.ainvoke({"message": body.message})
return {"reply": reply}The key point is simple: prompt injection detection happens before the model runs. Shield and sensitive information detection show how that new capability fits into a production-ready request path.
Arcjet AI prompt injection protection is available today. Pricing starts at $2 per 1 million tokens.
No. Red teaming and evaluation help you find weaknesses before launch. Model-side guardrails help reduce unsafe behavior. Prompt injection protection gives you runtime enforcement at the request boundary in production, before you send requests to your model provider. This helps reduce inference costs and avoid attacks reaching production AI endpoints.
You want all three.
Yes. We run prompt injection detection models behind the scenes which require inference. Our benchmarks show Arcjet prompt injection detection adding around 100-200 ms of latency.
Keep the response boring. Do not leak detector details or explain exactly what was flagged. A simple blocked response is usually the right default.
The Arcjet Python SDK allows you to implement rate limiting, bot detection, email validation, and signup spam prevention in FastAPI and Flask style applications.
Announcing Arcjet’s local AI security model, an opt-in AI security layer that runs expert security analysis for every request entirely in your environment, alongside our Series A funding.
Native security for Next.js, Node.js, Bun, SvelteKit, Vercel, Netlify, Fly.io and other modern platforms. Announcing our seed funding led by Andreessen Horowitz.
Get the full posts by email every week.