Token budgets
A token budget caps how many LLM tokens a project can consume in a fixed window. You declare the allowance, and Catalyst counts every token your apps spend on LLM calls that pass through it. When the allowance runs out, an enforce budget rejects further calls until the window resets, and an alert budget lets them through and tells you it happened.
Budgets exist because an agent's token consumption is hard to predict and easy to get wrong. An agent stuck in a tool-call loop, a prompt-injected app, or a leaked workload credential can burn through a provider account overnight, and a request-per-second limit is no defence, because one request can cost 100 tokens or a million. A budget puts a ceiling on the damage without touching application code.
What a budget is made of
A budget is a project-scoped TokenBudget resource with four parts:
| Part | What it does |
|---|---|
| Limit | The token allowance for one window. Tokens are prompt + completion, as reported by the LLM provider. |
| Window | hour, day, or month. Consumption resets to zero at each window boundary. |
| Mode | enforce rejects calls once the allowance is gone. alert only counts, so traffic always goes through. |
| Scope | Which traffic the budget applies to. Optional. |
Scope narrows a budget to a workload identity, a conversation component, a model, or any combination of the three. Selectors are combined with AND, so a budget scoped to appId: research-agent and model: gpt-4o only counts that agent's calls to that model. A budget with no scope covers every LLM call in the project.
Budgets stack. A request is checked against, and charged to, every budget whose scope matches it, so a project-wide monthly ceiling and a per-agent daily allowance can coexist. The tighter one runs out first.
How enforcement works
A budget applies to every LLM call your workloads make through Catalyst, whichever provider is behind it, whichever agent framework made it, and whichever of the two routes to a provider it took.
Catalyst checks the remaining allowance before the call goes out, then counts what the call actually used once it comes back. A rejected call never reaches the provider, so it costs you nothing.
That ordering has two consequences you should know about before setting a limit:
- A budget can overshoot by one request. A request admitted with one token left runs to completion, because output tokens cannot be counted before they are generated. The next request in that scope is rejected. So a limit is a ceiling with a little give in it, not an exact cut-off.
- Editing a limit applies forward, not backward. A new limit takes effect within seconds and governs what you spend from then on. It does not rewrite what the window has already used, so setting a limit below that point exhausts the budget straight away, and the reported usage says so without waiting for another call.
Editing a budget's limit, mode, or scope never clears its count, though changing its window does. To start a window over deliberately, reset the budget: consumption restarts from zero while the limit, window, mode, and scope stay as they are. Resetting an exhausted enforce budget is how you lift a block without waiting for the window to roll over. A budget's name is its identity, so one that is deleted and recreated under the same name also starts from zero and shares nothing with its predecessor.
Enforce or alert
Use alert mode to work out what an allowance should be. The budget counts and shows consumption but never rejects a call, so you can watch a realistic number build up over a window or two before committing to it. Switching to enforce afterwards is a single edit.
Both modes raise an alert when a budget crosses its limit, once per depletion rather than once per blocked call. So an alert-mode budget tells you it would have started rejecting traffic, without rejecting any. See Get notified when a budget runs out for routing those alerts.
What budgets cover
Your workloads reach an LLM provider through Catalyst in one of two ways, and budgets cover both:
- The Conversation API, Catalyst's own interface, which abstracts the provider so your code stays portable. It is covered over HTTP and gRPC, for every provider Catalyst supports.
- The LLM proxy, which takes your provider's own SDK pointed at Catalyst instead of at the provider. Your prompts, your provider key and the request body stay as they are. What changes is the base URL and one authentication header.
Agents are covered either way, since an agent's model calls take one of those two routes. Give one agent its own allowance by scoping a budget to its workload identity.
MCP tool calls are not covered. A tool call has no token count of its own. Control those with MCP access policies instead.
Scope across the two routes
Workload scope and model scope work on both routes. Component scope does not: a call through the LLM proxy names a provider and a model directly and involves no Catalyst component, so a budget scoped to a component covers Conversation API traffic only. To cover both, scope by workload identity or model, or leave the budget project-wide.
The model a budget matches is the model the caller asked for. Through the proxy that comes from the request itself; through the Conversation API it is the model the component is configured with.
Guardrails, not billing
Budgets are a safety net, and they behave like one:
- If Catalyst cannot count for a moment, calls are allowed through rather than blocked. Keeping your traffic flowing takes priority over strict enforcement.
- Consumption is recorded as calls complete, and a rare blip can undercount. The number is accurate enough to act on, but it will not reconcile against a provider invoice.
For what an individual LLM call actually cost, use the token counts in API logs and metrics, which record every call whether or not a budget covers it.
See also
- Apply token budgets: create, monitor, and reset budgets from the console and the CLI.
- Policies: access, workflow access, and resiliency controls Catalyst enforces inline on your traffic.
- AI agents: what runs the LLM calls a budget bounds.
- Observability: where token usage is reported.