Skip to content
INFRO

Documentation

Spend controls

Cap INFRO spend with monthly limits at the org, project, member, and key level: alert or block behavior, threshold notifications, billing-day resets.


Spend limits put a hard number on what any slice of your INFRO usage can cost. A spend_limit attaches at four levels — the organization, a project, a member, or a single API key — and either notifies you as spend crosses thresholds or refuses requests once the monthly budget is gone.

This page defines the spend_limit object, how limits at different levels combine, and exactly what a blocked request returns. It is the org-wide superset of the per-key ceiling covered in Rate & spend limits: same enforcement, same 402, more places to attach it.

The spend_limit object

One object shape works at every level:

spend_limit
{
  "monthly": 500,
  "behavior": "block",
  "alert_at": [0.5, 0.8, 1.0]
}
monthlynumberrequired
Budget in USD per billing month. Metered against usage.cost — the exact amounts INFRO bills for every request the limit covers, summed as responses complete.
behaviorstringrequired
"alert" sends notifications at each alert_at threshold and keeps serving past 100%. "block" sends the same notifications and refuses requests once monthly is exhausted.
alert_atnumber[]
Fractions of monthly at which to notify — each greater than 0 and at most 1, up to five values. Defaults to [0.5, 0.8, 1.0]. Each threshold fires once per billing month.

Limits meter what INFRO bills you. With BYOK, tokens on your own provider keys are billed to you by the provider directly — only INFRO's 5% fee counts toward a spend limit — so a BYOK-heavy workload needs provider-side budgets too.

Where limits attach

LevelCoversSet viaWho can set it
OrgEvery request in the organizationPATCH /v1/org or the consoleOwners
ProjectRequests made with the project's keysPATCH /v1/projects/{id}Owners and admins
MemberRequests made with keys the member createdPATCH /v1/org/members/{id}Owners and admins
KeyRequests made with that one keyPATCH /v1/keys/{id} or the consoleOwners and admins

A request is therefore subject to up to four limits at once: the key that made it, the member who created that key, the key's project, and the org. Developers and analysts cannot change limits at any level — see roles. Every limit change is written to the audit log as a policy.updated event.

The strictest limit wins

Every block limit that applies to a request must have budget remaining for the request to be admitted — the effective headroom is the smallest remaining budget among them. A $100 key inside a $500 project stops at $100; a $5,000 project under a $2,000 org limit stops when the org does.

Behaviors stay independent per level. A common setup is alert on the org, so finance hears about growth without an org-wide outage risk, and block on each project, so no single product can spend the others' budget. An alert limit never refuses a request, no matter how far spend runs past it.

Setting limits

Set limits in the console on each level's settings page, or over the API with a PATCH to the level's resource. The API requires a key created by an owner or admin — owner only for the org-level limit. Setting "spend_limit": null removes the limit at that level.

curl -X PATCH https://api.infro.io/v1/projects/proj_a1b2c3 \
  -H "Authorization: Bearer $INFRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "spend_limit": {
      "monthly": 500,
      "behavior": "block",
      "alert_at": [0.5, 0.8, 1.0]
    }
  }'
Response
{
  "id": "proj_a1b2c3",
  "name": "checkout",
  "spend_limit": {
    "monthly": 500,
    "behavior": "block",
    "alert_at": [0.5, 0.8, 1.0]
  },
  "month_to_date": 212.47
}

The same body works at the other three levels; each PATCH response echoes the resource with its current month_to_date spend.

curl -X PATCH https://api.infro.io/v1/org \
  -H "Authorization: Bearer $INFRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"spend_limit": {"monthly": 20000, "behavior": "alert", "alert_at": [0.8, 1.0]}}'

Alerts and thresholds

Threshold crossings notify org owners and admins by email. To route them to Slack or a webhook, or to alert on spend that has no limit attached, create a spend alert rule — the Alerts API covers scopes, windows, and channels, and webhook deliveries are signed like every other webhook.

Each alert_at threshold fires once per billing month. With behavior "alert", the 1.0 crossing is a notification like any other and spend keeps accruing past the budget; with "block", it coincides with enforcement kicking in.

What a blocked request returns

A request refused by a block limit returns HTTP 402 with error type insufficient_credits. This is deliberately the same status and type documented in Rate & spend limits and the error catalog — INFRO does not add a separate code for org-level limits, so your existing 402 handling covers every exhausted budget, from the account balance to a key ceiling to any limit on this page. The message names the limit that refused the request.

402 response body
{
  "error": {
    "message": "Insufficient credits: project proj_a1b2c3 has reached its monthly spend limit.",
    "type": "insufficient_credits",
    "code": null
  }
}

402 is not retryable — the same request fails the same way until the limit is raised, removed, or the billing month resets. Exclude it from automatic retries and alert on it instead, exactly as the retry rules prescribe.

Enforcement is admission-time. Requests already in flight when the budget runs out complete normally and are billed, so actual spend can overshoot monthly by roughly the cost of your concurrent traffic — cents for most workloads, more under heavy parallel batch load. A spend limit never cuts off a response mid-stream.

Resets and mid-month changes

Every limit's counter resets to zero at 00:00 UTC on the org's billing day — the day of the month your billing cycle renews, shown in the console. All levels reset together; there are no rolling windows.

Changes take effect immediately, on the next admitted request. Raising an exhausted block limit unblocks traffic at once — there is no propagation delay to wait out during an incident. Lowering a limit below month-to-date spend starts blocking immediately, or with "alert" fires each newly crossed threshold once. Switching between alert and block is likewise immediate.

Blocked requests in the request log

A blocked request is recorded in the request log like any other refusal: GET /v1/requests/{id} shows the 402 status with insufficient_credits, a gateway span only — no route_selection or provider spans, because no route was attempted — zero token counts, and a cost of 0. Nothing is billed and nothing counts toward any limit.

Blocked requests do still consume the key's per-minute rate limit, and they count toward error_rate in usage aggregates. Watch error_rate on the scope you limited: a jump made of 402s means the block engaged.

Per-key ceilings and this page

The per-key spend ceiling in Rate & spend limits — set at key creation, reported by GET /v1/key as limit — is a cumulative cap: it never resets, and it always blocks. It remains the right tool for bounding the lifetime blast radius of a leaked key; see Authentication. The monthly spend_limit on this page is a budget: it resets on the billing day and can alert instead of block. A key can carry both, and as everywhere else, the strictest one wins.

To watch month-to-date spend against your limits, query GET /v1/usage grouped by project or key with from set to the billing day — see Analytics.