n4nAI

Setting rate limits and budgets for autonomous agents

Practical steps to enforce rate limits and budgets for AI agents calling LLMs, with code for middleware, metering, and fallback guardrails.

n4n Team4 min read982 words

Audio narration

Coming soon — every post will get a voice note here.

Autonomous agents that call LLMs in tight loops will drain your quota and your wallet without explicit caps. Setting up rate limits budgets AI agents honor is the first line of defense for any production deployment. This guide gives you an end-to-end pattern: identity scoping, middleware throttling, token metering, and fallback routing you can ship today.

Step 1: Assign each agent a scoped identity

Never let multiple agents share one API credential. A shared key makes per-agent rate limits budgets AI agents impossible to attribute, and a single runaway loop takes down every other worker. Issue a distinct key or a signed header for every agent instance, even if they run in the same process or the same Kubernetes pod.

Most orchestration frameworks default to reading a single OPENAI_API_KEY from the environment. Override that pattern immediately. Pass the agent ID and its dedicated key through your dependency injection container or actor constructor.

// agent config
interface AgentConfig {
  id: string;
  apiKey: string;
  maxRequestsPerMin: number;
  maxTokensPerDay: number;
}

const agent: AgentConfig = {
  id: "research-loop-7",
  apiKey: process.env.AGENT_KEY_7!,
  maxRequestsPerMin: 10,
  maxTokensPerDay: 50_000,
};

Store these limits in a config service, not hardcoded. You will read them in the proxy and the agent loop. If you rotate keys, preserve the budget state (see Step 6) or the agent silently gets a fresh quota mid-day.

Why header-based identity beats IP throttling

Agents often run serverless, where IPs are ephemeral. A stable X-Agent-Id header survives container restarts and lets your proxy key rate limit counters correctly. Require the header at the edge; reject requests without it.

Step 2: Implement request rate limiting in a proxy

Put a thin proxy in front of the model endpoint. The proxy enforces requests-per-minute using a sliding window in Redis. Below is a minimal Flask example that rejects overflow with 429 and sends a Retry-After hint.

import redis
from flask import Flask, request, jsonify, abort

app = Flask(__name__)
r = redis.Redis(host="localhost", port=6379, db=0)

def allow_request(agent_id: str, limit: int) -> bool:
    key = f"ratelimit:{agent_id}"
    pipe = r.pipeline()
    pipe.incr(key)
    pipe.expire(key, 60)
    count, _ = pipe.execute()
    return count <= limit

@app.post("/v1/chat/completions")
def proxy():
    agent_id = request.headers.get("X-Agent-Id")
    limit = int(request.headers.get("X-Max-Req-Min", 10))
    if not agent_id or not allow_request(agent_id, limit):
        abort(429, "rate limit exceeded")
    # forward to upstream LLM provider here
    return jsonify({"ok": True})

Run the proxy on port 8080. Point your agent’s base_url to it. This gives you centralized rate limits budgets AI agents cannot bypass by spawning threads or forking subprocesses. For horizontal scaling, Redis is already shared; just run multiple proxy replicas behind a load balancer.

Choosing a window

A fixed 60-second window causes thundering herd at the boundary. Prefer a token-bucket algorithm (e.g., redis-py redis.client.Redis.hset with timestamped entries) for smoother throughput. The code above is deliberately minimal; replace it with limits library or Envoy ratelimit in production.

Step 3: Track token budgets with per-agent metering

Rate limiting requests is not enough; a single request can consume 100k tokens. Capture usage from the provider response and decrement a daily budget. The OpenAI-compatible response shape includes usage.total_tokens, usage.prompt_tokens, and usage.completion_tokens.

import json
from datetime import date

# called after every LLM response
def on_response(agent_id: str, resp_json: dict):
    used = resp_json["usage"]["total_tokens"]
    key = f"tokens:{agent_id}:{date.today().isoformat()}"
    r.incrby(key, used)
    r.expire(key, 86400)

# inside agent call
resp = client.chat.completions.create(model="gpt-4o", messages=...)
on_response(agent.id, resp.model_dump())

For an OpenAI-compatible endpoint, the usage object is standard. If you use a gateway that provides per-token usage metering, you can offload this bookkeeping. n4n.ai exposes that metering on a single endpoint covering 240+ models, which simplifies aggregation when agents switch models mid-run. Either way, the local counter is your authoritative guardrail for short-term enforcement.

Input vs output accounting

Prompt caching means you may be billed for fewer input tokens than you send. Still count full sent tokens against your agent’s logical budget to avoid surprise cache misses. Separate the billing meter from the safety meter.

Step 4: Configure fallback and cache hints to protect budget

When a provider is degraded, naive retries blow your budget. Use a gateway that honors client routing directives and forwards provider cache-control hints. Set cache_control on static prompt prefixes so repeated agent steps hit cache instead of recomputing.

{
  "model": "anthropic/claude-3.5-sonnet",
  "messages": [
    {"role": "system", "content": "You are a terse shell helper.", "cache_control": {"type": "ephemeral"}}
  ],
  "route": {"prefer": ["anthropic", "openai"], "fallback_on_429": true}
}

Automatic fallback when a provider is rate-limited or degraded keeps the agent alive without code changes. Your local rate limits budgets AI agents enforce still apply on top, because the gateway sees the same agent identity header. Without fallback, agents often implement exponential backoff that multiplies request counts; with it, they should disable local retries entirely.

CacheControl is a budget tool

Mark long system prompts, few-shot examples, and tool schemas as ephemeral cacheable. In a 100-step agent run, that cuts billed tokens by the number of steps minus one. The savings fund longer agent horizons under the same daily cap.

Step 5: Inject guardrails into the agent execution loop

The agent must check its remaining budget before each LLM call. Wrap the call in a function that queries Redis and short-circuits. This is the only place the agent touches the network.

async def guarded_call(agent: AgentConfig, messages: list):
    req_key = f"ratelimit:{agent.id}"
    tok_key = f"tokens:{agent.id}:{date.today().isoformat()}"
    req_count = int(r.get(req_key) or 0)
    tok_used = int(r.get(tok_key) or 0)
    if req_count >= agent.maxRequestsPerMin:
        raise RuntimeError("request rate limit hit")
    if tok_used >= agent.maxTokensPerDay:
        raise RuntimeError("token budget exhausted")
    return await client.chat.completions.create(model="gpt-4o", messages=messages)

This guard should sit inside the agent’s tool abstraction, not at the top level. If the guard raises, the agent should sleep or yield, not retry blindly. Add a circuit breaker: after three consecutive limit errors, halt the agent and page the owner.

Sync and async uniformity

Whether your agent is Python asyncio or a TypeScript LangChain runner, the check is the same: read two integers, compare, throw. Keep the logic in a shared library so every agent version enforces identical rules.

Step 6: Verify the limits work

Verification is concrete. Write a test that forces overflow and asserts the correct failure mode.

def test_rate_limit():
    r.delete("ratelimit:test-agent")
    for i in range(10):
        assert allow_request("test-agent", 10) is True
    assert allow_request("test-agent", 10) is False

Then run a live load test with a single agent at maxRequestsPerMin=5 and fire 20 requests in 10 seconds using hey or locust. Watch proxy logs: exactly 5 should pass, 15 return 429. For budgets, seed tokens:agent:2025-01-01 to just under the cap, send one large completion, and confirm the next call raises token budget exhausted.

redis-cli set tokens:agent:2025-01-01 49900
python -c "import agent; agent.guarded_call(...)"  # second call must raise

Check the gateway dashboard (or your Redis keys) to confirm per-agent totals match expected spend. If you used an OpenAI-compatible gateway with metering, compare its usage report to your local counter; they should differ by less than one request’s worth of tokens. Add this comparison as a CI job that runs against a staging agent weekly.

Operational notes

Keep limits in a dynamic store. Agents that learn to batch calls will need higher token caps but lower request caps. Review weekly.

Do not trust client-reported usage for billing; the server-side meter is authoritative. When you rotate agent keys, copy the Redis budget keys or the agent starts with a fresh quota mid-day.

Rate limits budgets AI agents respect are not a luxury. They are the sandbox boundary that keeps a bug from becoming an incident. Ship the proxy first, add metering second, and only then let the agent run unattended.

Tagsrate-limitsbudgetsai-agent-guardrailscost-control

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →

All sandboxing & guardrails for autonomous agents posts →