AI budget control for OpenAI Agents
AI providers bill per token. Without per-user limits, a single user can exhaust your entire monthly budget – through prompt attacks, runaway loops, or just heavy legitimate use.
A token bucket rate limit maps directly onto how AI billing works. You estimate the cost of each request in tokens, deduct it from the user’s bucket, and deny requests when the bucket is empty. The bucket refills over time, giving each user a sustained allowance without sharp rate-limit cliffs.
Alternatively, you can use a fixed window or sliding window limit to enforce a hard cap on spend per user per day, week, or month. For details on different approaches, see the rate limiting algorithms reference.
Arcjet handles bucket state across all instances of your application – no Redis or external state store required.
Get started
Section titled “Get started”This example deducts an estimated token cost from a per-user bucket before a tool runs. When the bucket is empty, the tool does not execute.
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://console.arcjet.comexport ARCJET_KEY="ajkey_..."
npm install @arcjet/guard @openai/agentsCreate the example:
import { launchArcjet, tokenBucket } from "@arcjet/guard";import { guardTool } from "@arcjet/guard/openai-agents/v0";import { tool } from "@openai/agents";import { z } from "zod";
const arcjet = launchArcjet({ key: process.env.ARCJET_KEY! });
const tokenBudget = tokenBucket({ bucket: "ai-tokens", refillRate: 2000, intervalSeconds: 3600, maxTokens: 5000,});
export const completePrompt = guardTool( arcjet, tool({ name: "complete_prompt", description: "Complete a user prompt", parameters: z.object({ prompt: z.string(), estimatedTokens: z.number(), }), execute: async ({ prompt }) => ({ prompt }), }), { action: "prompt.completed", rules: (input) => [ tokenBudget({ key: "user123", // Replace with your authenticated user ID requested: Math.max(1, Math.ceil(input.estimatedTokens)), }), ], },);Then start or invoke the agent with a test prompt.
Requests appear in your Arcjet dashboard in real time.
Configure the rate limit
Section titled “Configure the rate limit”characteristics: ["userId"] - Tracks the bucket per user. Replace
"userId" with the characteristic that identifies a unique user in your
application, such as a session token, API key, or authenticated user ID. Pass the
value to aj.protect() as a named argument.
refillRate and interval - Set the sustained allowance. refillRate: 2_000, interval: "1h" gives each user 2,000 tokens per hour. Adjust to match your AI
provider’s pricing and your cost targets. These are hard coded in this example,
but you can also calculate them dynamically based on user subscription level or
other factors. Pass the calculated values to the rule.
capacity - The maximum tokens a user can accumulate. Setting capacity: 5_000 with refillRate: 2_000 lets users burst up to 5,000 tokens if they
haven’t used their allowance recently.
Token estimation
Section titled “Token estimation”The example uses a characters / 4 heuristic (~1 token per 4 characters for
common English text). This is a reasonable starting point – it avoids
introducing extra dependencies and works well enough for budget enforcement
where a small margin of error is acceptable.
For accurate counts, use a tokenizer:
- JavaScript / TypeScript:
tiktoken - Python:
tiktoken - Anthropic: provides a token counting API