Content moderation
Arcjet content moderation detects harmful content in untrusted text before it
is stored, displayed, or forwarded. It is a Guard rule – call it from
guard() / Guard, not protect().
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.Quick start
Section titled “Quick start”This example screens inbound text with content moderation before the agent continues.
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 ModerateContent, launch_arcjet
arcjet = launch_arcjet(key=os.environ["ARCJET_KEY"])moderate = ModerateContent()
async def screen_message(text: str) -> None: decision = await arcjet.guard( label="message.received", rules=[moderate(text)], ) if decision.conclusion == "DENY" and decision.reason == "MODERATE_CONTENT": raise RuntimeError("Harmful content detected – rephrase your message")Then start or invoke the agent with a test prompt.
Requests appear in your Arcjet dashboard in real time.
This example screens inbound text with content moderation before the agent continues.
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, moderateContent } from "@arcjet/guard";import { genkitContext } from "@arcjet/guard/genkit/v1";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });const moderate = moderateContent();
export async function screenPrompt( conversationId: string, userText: string,) { const appContext = { sessionId: conversationId }; const decision = await arcjet.guard({ label: "message.received", rules: [moderate(userText)], ...genkitContext({ context: appContext }), });
if (decision.conclusion === "DENY" && decision.reason === "MODERATE_CONTENT") { throw new Error("Harmful content detected – rephrase your message"); }}Then start or invoke the agent with a test prompt.
Requests appear in your Arcjet dashboard in real time.
This example screens inbound text with content moderation before the agent continues.
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, moderateContent } from "@arcjet/guard";import { guardProcessor } from "@arcjet/guard/mastra/v1";import { Agent } from "@mastra/core/agent";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const inbound = guardProcessor(arcjet, { action: "message.received", rules: ({ text }) => [moderateContent()(text)],});
export const agent = new Agent({ id: "support-agent", name: "support-agent", instructions: "Help the user.", model: "openai/gpt-4o-mini", inputProcessors: [inbound],});Then start or invoke the agent with a test prompt.
Requests appear in your Arcjet dashboard in real time.
This example screens inbound text with content moderation before the agent continues.
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 } from "@anthropic-ai/claude-agent-sdk";import { launchArcjet, moderateContent } from "@arcjet/guard";import { guardHooks } from "@arcjet/guard/claude-agent-sdk/v0";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
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 }) => [moderateContent()(prompt)], }, }), }, })) { void message; }}Then start or invoke the agent with a test prompt.
Requests appear in your Arcjet dashboard in real time.
This example screens inbound text with content moderation before the agent continues.
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, moderateContent } from "@arcjet/guard";import { langgraphAgentContext } from "@arcjet/guard/langgraph/v1";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });const moderate = moderateContent();
export async function screenPrompt( conversationId: string, userText: string,) { const config = { configurable: { thread_id: conversationId } }; const decision = await arcjet.guard({ label: "message.received", rules: [moderate(userText)], ...langgraphAgentContext(config), });
if (decision.conclusion === "DENY" && decision.reason === "MODERATE_CONTENT") { throw new Error("Harmful content detected – rephrase your message"); }}Then start or invoke the agent with a test prompt.
Requests appear in your Arcjet dashboard in real time.
This example screens inbound text with content moderation before you store or display it.
Content moderation is a Guard rule, so this example uses @arcjet/guard inside
your Bun server – not protect().
We assume you already have a Bun project set up. For helper options and denial behavior, see the Agent guards guide.
Install the dependencies:
# Export your Arcjet API key from https://app.arcjet.comexport ARCJET_KEY="ajkey_..."
bun add @arcjet/guardCreate the example:
import { launchArcjet, moderateContent } from "@arcjet/guard";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });const moderate = moderateContent();
export default { port: 3000, fetch: async (req: Request) => { const { message }: { message: string } = await req.json();
const decision = await arcjet.guard({ label: "message.received", rules: [moderate(message)], });
if ( decision.conclusion === "DENY" && decision.reason === "MODERATE_CONTENT" ) { return Response.json( { error: "Harmful content detected – rephrase your message" }, { status: 400 }, ); }
return Response.json({ ok: true }); },};Then send a test POST request to the server.
Requests appear in your Arcjet dashboard in real time.
This example screens inbound text with content moderation before you store or display it.
Content moderation is a Guard rule, so this example uses @arcjet/guard inside
your Next.js route – not protect().
We assume you already have a Next.js app set up. For helper options and denial behavior, see the Agent guards guide.
Install the dependencies:
# Export your Arcjet API key from https://app.arcjet.comexport ARCJET_KEY="ajkey_..."
npm install @arcjet/guardCreate the example:
import { launchArcjet, moderateContent } from "@arcjet/guard";import { NextResponse } from "next/server";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });const moderate = moderateContent();
export async function POST(req: Request) { const { message }: { message: string } = await req.json();
const decision = await arcjet.guard({ label: "message.received", rules: [moderate(message)], });
if (decision.conclusion === "DENY" && decision.reason === "MODERATE_CONTENT") { return NextResponse.json( { error: "Harmful content detected – rephrase your message" }, { status: 400 }, ); }
return NextResponse.json({ ok: true });}Then send a test POST request to the route.
Requests appear in your Arcjet dashboard in real time.
This example screens inbound text with content moderation before the agent continues.
We assume you already have an 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, moderateContent } from "@arcjet/guard";import { openaiAgentsContext } from "@arcjet/guard/openai-agents/v0";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });const moderate = moderateContent();
export async function screenPrompt( conversationId: string, userText: string,) { const appContext = { sessionId: conversationId }; const decision = await arcjet.guard({ label: "message.received", rules: [moderate(userText)], ...openaiAgentsContext({ context: appContext, conversationId }), });
if (decision.conclusion === "DENY" && decision.reason === "MODERATE_CONTENT") { throw new Error("Harmful content detected – rephrase your message"); }}Then start or invoke the agent with a test prompt.
Requests appear in your Arcjet dashboard in real time.
This example screens inbound text with content moderation before the agent continues.
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, moderateContent } from "@arcjet/guard";import { generateText } from "ai";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });const moderate = moderateContent();
export async function runAgent(prompt: string) { const decision = await arcjet.guard({ label: "message.received", rules: [moderate(prompt)], });
if (decision.conclusion === "DENY" && decision.reason === "MODERATE_CONTENT") { throw new Error("Harmful content detected – rephrase your message"); }
return generateText({ model: "openai/gpt-4o-mini", prompt });}Then start or invoke the agent with a test prompt.
Requests appear in your Arcjet dashboard in real time.
This example screens inbound text with content moderation before you store or display it.
Content moderation is a Guard rule, so this example uses @arcjet/guard inside
your NestJS controller – not protect().
We assume you already have a NestJS project set up. For helper options and denial behavior, see the Agent guards guide.
Install the dependencies:
# Export your Arcjet API key from https://app.arcjet.comexport ARCJET_KEY="ajkey_..."
npm install @arcjet/guardCreate the example:
import { launchArcjet, moderateContent } from "@arcjet/guard";import { BadRequestException, Body, Controller, Post } from "@nestjs/common";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });const moderate = moderateContent();
@Controller("messages")export class MessagesController { @Post() async create(@Body() body: { message: string }) { const decision = await arcjet.guard({ label: "message.received", rules: [moderate(body.message)], });
if ( decision.conclusion === "DENY" && decision.reason === "MODERATE_CONTENT" ) { throw new BadRequestException( "Harmful content detected – rephrase your message", ); }
return { ok: true }; }}Then send a test POST request to /messages.
Requests appear in your Arcjet dashboard in real time.
This example screens inbound text with content moderation before you store or display it.
Content moderation is a Guard rule, so this example uses @arcjet/guard inside
your SvelteKit route – not protect().
We assume you already have a SvelteKit project set up. For helper options and denial behavior, see the Agent guards guide.
Install the dependencies:
# Export your Arcjet API key from https://app.arcjet.comexport ARCJET_KEY="ajkey_..."
npm install @arcjet/guardCreate the example:
import { launchArcjet, moderateContent } from "@arcjet/guard";import { error, json, type RequestEvent } from "@sveltejs/kit";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });const moderate = moderateContent();
export async function POST({ request }: RequestEvent) { const { message }: { message: string } = await request.json();
const decision = await arcjet.guard({ label: "message.received", rules: [moderate(message)], });
if (decision.conclusion === "DENY" && decision.reason === "MODERATE_CONTENT") { return error(400, "Harmful content detected – rephrase your message"); }
return json({ ok: true });}Then send a test POST request to /messages.
Requests appear in your Arcjet dashboard in real time.
This example screens inbound text with content moderation before you store or display it.
Content moderation is a Guard rule, so this example uses arcjet.guard inside
your Flask route – not protect(). Use launch_arcjet_sync with
guard_sync() in sync Flask code.
We assume you already have a Flask project set up. For helper options and denial behavior, see the Agent guards guide.
Install the dependencies:
# Export your Arcjet API key from https://app.arcjet.comexport ARCJET_KEY="ajkey_..."export ARCJET_ENV=development
pip install arcjet flaskCreate the example:
import os
from arcjet.guard import ModerateContent, launch_arcjet_syncfrom flask import Flask, jsonify, request
app = Flask(__name__)
arcjet = launch_arcjet_sync(key=os.environ["ARCJET_KEY"])moderate = ModerateContent()
@app.post("/messages")def create_message(): text = request.get_json()["message"] decision = arcjet.guard_sync( label="message.received", rules=[moderate(text)], ) if decision.conclusion == "DENY" and decision.reason == "MODERATE_CONTENT": return jsonify( error="Harmful content detected – rephrase your message" ), 400 return jsonify(ok=True)Then send a test POST request to /messages.
Requests appear in your Arcjet dashboard in real time.
This example screens inbound text with content moderation before the agent continues.
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, moderateContent } from "@arcjet/guard";import { guardInbound } from "@arcjet/guard/vercel-eve/v0";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
export async function screenInbound( message: string, conversationId: string,) { const verdict = await guardInbound(arcjet, message, { action: "message.received", correlationId: conversationId, rules: [moderateContent()(message)], });
if (!verdict.allowed) { throw new Error("Harmful content detected – rephrase your message"); }}Then start or invoke the agent with a test prompt.
Requests appear in your Arcjet dashboard in real time.
This example screens inbound text with content moderation before you store or display it.
Content moderation is a Guard rule, so this example uses @arcjet/guard inside
your Remix action – not protect().
We assume you already have a Remix project set up. For helper options and denial behavior, see the Agent guards guide.
Install the dependencies:
# Export your Arcjet API key from https://app.arcjet.comexport ARCJET_KEY="ajkey_..."
npm install @arcjet/guardCreate the example:
import { launchArcjet, moderateContent } from "@arcjet/guard";import type { ActionFunctionArgs } from "@remix-run/node";import { json } from "@remix-run/node";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });const moderate = moderateContent();
export async function action({ request }: ActionFunctionArgs) { const { message }: { message: string } = await request.json();
const decision = await arcjet.guard({ label: "message.received", rules: [moderate(message)], });
if (decision.conclusion === "DENY" && decision.reason === "MODERATE_CONTENT") { return json( { error: "Harmful content detected – rephrase your message" }, { status: 400 }, ); }
return json({ ok: true });}Then send a test POST request to the route.
Requests appear in your Arcjet dashboard in real time.
This example screens inbound text with content moderation before you store or display it.
Content moderation is a Guard rule, so this example uses arcjet.guard inside
your FastAPI route – not protect().
We assume you already have a FastAPI project set up. For helper options and denial behavior, see the Agent guards guide.
Install the dependencies:
# Export your Arcjet API key from https://app.arcjet.comexport ARCJET_KEY="ajkey_..."export ARCJET_ENV=development
pip install arcjet fastapiCreate the example:
import os
from arcjet.guard import ModerateContent, launch_arcjetfrom fastapi import FastAPI, HTTPExceptionfrom pydantic import BaseModel
app = FastAPI()
arcjet = launch_arcjet(key=os.environ["ARCJET_KEY"])moderate = ModerateContent()
class MessageRequest(BaseModel): message: str
@app.post("/messages")async def create_message(body: MessageRequest): decision = await arcjet.guard( label="message.received", rules=[moderate(body.message)], ) if decision.conclusion == "DENY" and decision.reason == "MODERATE_CONTENT": raise HTTPException( status_code=400, detail="Harmful content detected – rephrase your message", ) return {"ok": True}Then send a test POST request to /messages.
Requests appear in your Arcjet dashboard in real time.
This example screens inbound text with content moderation before you store or display it.
Content moderation is a Guard rule, so this example uses @arcjet/guard inside
your Node.js server – not protect().
We assume you already have a Node.js project set up. For helper options and denial behavior, see the Agent guards guide.
Install the dependencies:
# Export your Arcjet API key from https://app.arcjet.comexport ARCJET_KEY="ajkey_..."
npm install @arcjet/guardCreate the example:
import { launchArcjet, moderateContent } from "@arcjet/guard";import http from "node:http";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });const moderate = moderateContent();
const server = http.createServer(async (req, res) => { if (req.method !== "POST") { res.writeHead(405); res.end(); return; }
const chunks: Buffer[] = []; for await (const chunk of req) { chunks.push(chunk); } const { message } = JSON.parse(Buffer.concat(chunks).toString()) as { message: string; };
const decision = await arcjet.guard({ label: "message.received", rules: [moderate(message)], });
if (decision.conclusion === "DENY" && decision.reason === "MODERATE_CONTENT") { res.writeHead(400, { "Content-Type": "application/json" }); res.end( JSON.stringify({ error: "Harmful content detected – rephrase your message", }), ); return; }
res.writeHead(200, { "Content-Type": "application/json" }); res.end(JSON.stringify({ ok: true }));});
server.listen(8000);Then send a test POST request to the server.
Requests appear in your Arcjet dashboard in real time.
Language examples
Section titled “Language examples”npm install @arcjet/guardimport { launchArcjet, moderateContent } from "@arcjet/guard";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });const moderate = moderateContent();
const decision = await arcjet.guard({ label: "tools.chat", rules: [moderate(userMessage)],});
if (decision.conclusion === "DENY" && decision.reason === "MODERATE_CONTENT") { throw new Error("Harmful content detected – rephrase your message");}
const result = moderate.result(decision);// `detected` is true when harmful content was found. Billing is undefined// when the service does not report usage. Content moderation uses text_units.console.log(result?.detected, result?.billing?.unit, result?.billing?.count);pip install arcjetimport os
from arcjet.guard import ModerateContent, launch_arcjet
arcjet = launch_arcjet(key=os.environ["ARCJET_KEY"])moderate = ModerateContent()
decision = await arcjet.guard( label="llm.output", rules=[moderate(text)],)
if decision.conclusion == "DENY" and decision.reason == "MODERATE_CONTENT": raise RuntimeError("Harmful content detected – rephrase your message")
result = moderate.result(decision)# `detected` is True when harmful content was found. Billing is None# when the service does not report usage. Content moderation uses text_units.print(result.detected if result else None)if result and result.billing: print(result.billing.unit, result.billing.count)go get github.com/arcjet/arcjet-go@latestmoderation, err := arcjet.GuardModerateContent(arcjet.GuardModerateContentOptions{ Mode: arcjet.ModeLive, // required})if err != nil { return err}
decision, err := guard.Guard(ctx, arcjet.GuardRequest{ Label: "tools.generate", Rules: []arcjet.GuardRuleInput{moderation.Text(userMessage)},})if err != nil { return err}if decision.IsDenied() && decision.Reason == arcjet.ReasonModerateContent { return errors.New("content flagged by moderation")}
// Billing is optional. Content moderation usage is measured in text_units.if result := moderation.Result(decision); result != nil && result.Billing != nil { fmt.Printf("charged %d %s\n", result.Billing.Count, result.Billing.Unit)}Set Mode on every Guard rule. An empty Mode returns ErrInvalidMode.
Keep the response generic. Do not leak detector details or explain exactly what was flagged.