Advanced guide / Chapter 2 of 7
Budgets and limits.
Most spend alerts arrive after the money is gone. ACPpotamia works the other way round: every call reserves its cost before it is sent. However many agents run at once, together they can't spend more than you allowed.
Four levels, all checked together
| Level | Limits | Resets | Where to set it |
|---|---|---|---|
| Team | Monthly budget, monthly tokens, requests a minute | 1st of each month (UTC) | Console → Limits |
| API key | Monthly budget, requests a minute, allowed models | 1st of each month | Console → API keys → Set limits |
| Tenant (Pro, Enterprise) | Monthly budget, requests a minute; a default plus each customer's own | 1st of each month | Console → Limits and Tenants, or the Tenant API |
| Session | Requests a minute, total spend for the task | Never (one session = one task) | Console → Limits |
A worked example
A company sets a team budget of $500 a month. Its CI pipeline uses its own key capped at $20. Its product gives every customer $25 a month by default, and one large customer $200. Each agent task may spend at most $2 and make 30 requests a minute.
| Situation | What happens |
|---|---|
| A test suite loops overnight on the CI key | It stops at $20 with 402 key_budget_exhausted. Production keys are untouched. |
| Customer 4821 reaches $25 | Their calls get 402 tenant_budget_exhausted. Every other customer keeps working. |
| One support task keeps searching | At $2 it gets 402 session_budget_exhausted; loop detection often stops it earlier. |
| All of the above at once, near month end | The team cap of $500 still holds over everything. |
When a budget is nearly used up
Say a call asks for a long answer but the budget can only cover part of it. ACPpotamia lowers that call's max_tokens to what's left, and the call still succeeds with a shorter answer. Once nothing is left, calls get HTTP 402 and never reach the provider.
Handling limits in your code
TypeScript
try {
const reply = await client.chat.completions.create({ model: "gpt-5-mini", messages });
} catch (err) {
const code = err?.error?.code; // the same shape OpenAI and Anthropic SDKs expose
if (err.status === 402) {
// A budget is used up: show a friendly message, offer an upgrade, or wait for the 1st.
} else if (err.status === 429 && code?.endsWith("rate_limited")) {
// Back off for err.headers["retry-after"] seconds, then retry.
} else if (code === "loop_detected" || code === "session_killed") {
// Do not retry: fix the agent, then revive the session in the console.
}
}