Skip to content

Prompt injection detection for Python + FastAPI

Arcjet prompt injection detection evaluates each incoming prompt for injection patterns inside your application before it reaches the AI provider. Detected attacks are blocked before the AI call is made, protecting both your application behavior and your AI budget.

What is Arcjet? Arcjet is the runtime security platform that ships with your code. Enforce budgets, stop prompt injection, detect bots, and protect personal information with Arcjet's AI security building blocks.

In this example we use LangChain to create a simple AI chat server with FastAPI, and Arcjet to block prompt injection attacks before they reach the AI model. 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):

Terminal window
# Export your Arcjet API key from https://console.arcjet.com
export ARCJET_KEY="ajkey_..."
export ARCJET_ENV=development
# Export your OpenAI API key (used by LangChain)
export OPENAI_API_KEY="sk-..."
# Install dependencies
uv add arcjet fastapi uvicorn langchain langchain-openai

Create the chat server:

main.py
import logging
import os
from arcjet import Mode, arcjet, detect_prompt_injection, shield
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://console.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 requests
aj = arcjet(
key=arcjet_key, # Get your key from https://console.arcjet.com
rules=[
# Shield protects against common web attacks e.g. SQL injection
shield(mode=Mode.LIVE),
# 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):
# Pass the user message so detect_prompt_injection can evaluate it
decision = await aj.protect(
request, detect_prompt_injection_message=body.message
)
if decision.is_denied():
if decision.reason_v2.type == "PROMPT_INJECTION":
logger.warning("Request blocked due to prompt injection")
return JSONResponse(
{"error": "Prompt injection detected — please rephrase your message"},
status_code=400,
)
# SHIELD or any other denial
return JSONResponse({"error": "Forbidden"}, status_code=403)
# Arcjet approved — call the AI model
reply = await chain.ainvoke({"message": body.message})
return {"reply": reply}

Then run the server:

Terminal window
uv run uvicorn main:app --reload

And send a message to the API endpoint:

Terminal window
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.

Need help with anything? Email support@arcjet.com to get support from our engineering team.

Discussion