Skip to content

Budgets

A budget is the most important number on the dashboard. It's the maximum amount of money a workflow is allowed to spend in a billing period. Set it too low and your agent stops working. Set it too high and a runaway agent burns through real money before you notice.

This page covers what the budget controls, how the dashboard shows it, and what happens at each boundary.

Where you see it

On the Workflows detail page, the budget appears as a progress bar near the top:

Spend this period         $47.30 of $50.00  (95%)
████████████████████████░░
Time to exhaustion         ~16 hours at current rate

Three numbers:

  • Spend this period — total cents spent since the last period rollover. Resets automatically.
  • Budget — the cap. Set this in workflow settings.
  • Time to exhaustion — at the current rate of spend, when the budget will run out. Useful for "should I raise the cap?".

What the budget covers

The budget covers spend, not calls. Calls are rate-limited separately — see Policies.

"Spend" is calculated from token counts reported by your LLM provider. The dashboard knows the per-model pricing for every model the SDK tracks:

  • Input tokens × input rate
  • Output tokens × output rate
  • Cache read / cache write tokens (if your provider exposes them) at their respective rates
  • Reasoning tokens for o1/o3-style models at the reasoning rate

The total spend is the sum across all @protect calls inside the workflow, across the current period.

Periods

A "period" is the window after which the spend counter resets. NullRun has two period sources:

Plan Period source When it resets
Lite (free) Rolling 1-month window anchored at organizations.created_at One month after signup (e.g. signed up Jun 15 → resets Jul 15, Aug 15, …)
Paid (Starter / Growth / Scale) Your billing cycle (Polar subscription) Set when you subscribed; on renewal

The dashboard shows the period start and end dates next to the spend bar. When the period rolls over, the spend counter resets to zero and the budget applies fresh.

What happens at the boundary

Three scenarios, depending on the workflow's enforcement mode:

Hard mode (default)

Spending → $49.95 of $50.00
Next @protect call:        #2.00 projected
gate decision:             block
SDK raises:                 NullRunBudgetError (NR-B004)
with nullrun.guard():     prints the 4-line dev report, sys.exit(1)
Hard mode — the projected cost of the next call exceeds the remaining budget. The gate returns `block` before the model runs.

The agent stops cleanly at the boundary. No partial charge — the projected cost is reserved when the gate approves, and the actual cost is reported after the LLM returns. If the call is denied, no charge happens.

Soft mode

Soft mode lets the agent run past its budget when an active chain is present, up to the configured overdraft cap (max_overdraft_cents or max_overdraft_percent, whichever is lower). The chain returns to standard Hard mode once the cap is exhausted. See Policies → BudgetLimit extra fields for the full configuration contract.

How to set the budget

The first time you create a workflow, no budget cap is configured. max_budget_cents == 0 means "no per-key budget configured" — the gate passes through to the org-level plan cap, not "block everything" — so the agent runs against the org's default policy until you raise the per-key cap.

To set the budget:

  1. Open the workflow.
  2. Click Settings.
  3. Find Budget and enter cents ($50 = 5000).
  4. Save.
Workflow detail — Overview tab. The Budget card sits at the top showing spent / cap. Workflow detail — Overview tab. The Budget card sits at the top showing spent / cap.
Workflows · Budget card

Reasonable starting budgets:

Use case Suggested budget
Personal / dev experiment $5 (500 cents) per period
Single-tenant internal tool $20 (2000 cents) per period
Customer-facing AI feature $100 (10000 cents) per period, plus an alert at 80%

The dashboard warns you when spend crosses 80% of the cap and again at 100%. Configure alert destinations under Notifications in the sidebar (Channels + Alert rules + Event subscriptions matrix).

What happens when you change the budget mid-period

  • Raise: the new cap takes effect immediately. The next gate call uses the new cap.
  • Lower below current spend: the agent doesn't get retroactive refunds, but every call from this point onward rejects until the spend drops (which only happens at period rollover, since the counter is monotonic within a period).

Why cents, not dollars

The dashboard stores everything in cents to avoid floating-point rounding in pricing math. The budget_cents field in the API is always an integer. If you set budget_cents: 5000, your cap is exactly $50.00, no rounding errors.

Reservation and consumption

The gate reserves your projected cost before the model runs and reconciles the actual cost after. If the LLM call returns a cost that meaningfully exceeds the reservation, the /track commit rejects with CONSUME_OVERBUDGET (HTTP 422, error_code = "NR-O001") — no implicit re-reserve, ever. The tolerance is a fixed cents value (policies.consume_epsilon_cents, default 1¢); no percentage-based epsilon is supported.

Approximate budget endpoint

If you want to show "you've used X of Y" in a custom dashboard or notification without enrolling in the full NullRun dashboard, the gateway exposes an approximate-spend endpoint:

shell
curl "https://api.nullrun.io/api/v1/budget/approximate" \
  -H "Authorization: Bearer ***"

The response carries current_spend_cents_estimate, an is_approximate: true flag, a source field, a confidence level (High / Medium / Low), and last_updated_at. Use this for display only — never for enforcement, rate-limit logic, or agent-side gating. When the source is unavailable the endpoint returns 503 BUDGET_DATA_UNAVAILABLE; render that as "data unavailable", never as ≈ $0 spent.

See also

Deep dive

Spend is counted per organization per billing period. The period start is computed once, server-side, and bound into every reservation made for an execution, so the gate decision and the later commit for the same call always land in the same period even when a rollover happens in between. Spend within a period is monotonic: it returns to zero only when the period ends.

Enforcement uses the lower of the organization ceiling and the workflow budget, and the organization ceiling is always strict. In soft mode an active chain may overdraw by the lower of the configured cent and percentage allowances, and concurrent chains share that one allowance rather than each earning their own. The gate reserves the projected cost before the model runs; the actual cost is reported afterwards and must fit inside the reserved amount plus a fixed tolerance, one cent by default and configurable per policy. A cost above that tolerance is never silently re-reserved: the commit is rejected with CONSUME_OVERBUDGET while the period counter still reflects the real spend. The tolerance is a fixed number of cents rather than a percentage, so the drift a bad projection can absorb does not grow with the size of the budget.

Reservation and commit are both idempotent. A retry that arrives between a successful reservation and the recording of its result finds the reservation already in place and is told so, rather than reserving twice. A repeated commit for the same reservation either replays the original outcome or repeats the same rejection. The authorized amount is sealed when the gate decides, so a commit cannot re-check it against a cap tightened afterwards; the new cap governs the next call, not the one already in flight.

The approximate-spend endpoint resolves its answer from the fast-path counter, the durable record, and the last known value, in that order, and reports a confidence level so the caller can qualify the number. It is advisory. When every source is unavailable it returns 503 rather than zero, because a missing figure and an empty one are different claims. A reservation that is never committed expires after a bounded window; a commit arriving after that is reported as a missing reservation, and the caller must gate again to obtain a fresh one.