Skip to Content
Launching the open-source gateway

Budgets and limits

Cap spend, requests, and tokens per API key and per customer, before any provider is called.

The gateway checks every request against your limits before it calls a provider. A request over a hard cap is refused with an error you can act on, and costs nothing. Use limits as a safety net: give each app and environment its own API key with its own caps, and put a budget on each customer you serve.

LimitScopeWhat it capsOver it
spending_limitAPI keySpend per month, in USD402 plan_permission_denied
max_usageAPI keyRequests over the key’s lifetime429 key_usage_limit_exceeded
rate_limitAPI keyRequests per minute429 rate_limit_exceeded
tpm_rate_limitAPI keyTokens per minute, input plus output429 rate_limit_exceeded, with Retry-After
Limit policiesAPI key and other scopesCost, requests, or tokens per window, or lifetime429 key_usage_limit_exceeded at the hard cap
Customer budgetOne customer_identifierSpend per period402
Customer rate limitOne customer_identifierChat requests per minute429 rate_limit_exceeded
Plan limitsOrganizationRequests and tokens per minute on your plan429 rate_limit_exceeded

Request and token rate limits clear in seconds and are covered on Rate limits. This page covers the caps that hold until a window resets.

Key limits

Open Gateway › API keys and click the key. Each limit shows its usage, what’s left, and when it resets.

An API key's limits: a $500 lifetime cost limit with an alert at $400, recurring limits for cost per day and requests per minute with usage bars and reset countdowns, expiry set to Never, and limit alerts sent by email to alex@acme.dev

The fields on the key itself:

FieldTypeDescription
spending_limitnumberSpend per month, in USD. At or over it, requests get 402 plan_permission_denied with the message Organization key spending limit exceeded.
max_usageintegerRequests over the key’s lifetime. -1 is unlimited, 0 allows none.
rate_limitnumberRequests per minute.
tpm_rate_limitnumberTokens per minute. Each scope (key, organization, plan) is its own bucket, so the tightest one wins.

An API key can’t raise its own caps: changes to these fields are refused when you authenticate with an API key, so make them from the dashboard. To make a key stop working on a date, set Expiry under Expiry controls.

Limit policies

A limit policy caps a metric (cost in USD, request_count, or token_count) over a window (minute, hour, day, week, month, or lifetime). Each policy has a hard cap that blocks and optional warning thresholds that only alert. Usage counts up within the window and resets when it ends.

Set a lifetime limit

On the key, under Lifetime limit, turn on Cost. Click the amount next to Block at to set the cap, and click + next to Alert at to add warning thresholds. A lifetime limit never resets and starts from what the key has already spent.

Add recurring limits

Next to Recurring limits, click +. Choose Cost, Requests, or Tokens and a period, set Block at and, if you want, Alert at, then click Save on the row. Stack rows for a daily and a monthly cap.

A new recurring limit row: For Cost per day, Alert at $40, Block at $50, with a Save button

Turn on alerts

Under Notifications, turn on Limit alerts, then choose Email or Slack and pick an address or channel.

Save

Click Save in the top bar, review the changes, and click Confirm.

To create a key and its caps in one call, use an API key with the Admin permission:

Terminal
curl https://api.respan.ai/api/temporary-keys/ \ -H "Authorization: Bearer $RESPAN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Contractor key", "limit_policies": [{ "metric": "cost", "period": "day", "max_value": 50, "warning_threshold": 40 }] }'

To change caps later, or to meter other scopes, use the Limit policies API.

Soft and hard limits

  • Soft (Alert at): the request goes through. Respan sends a limit alert by email or Slack and a limit_policy_soft_triggered webhook event. Each threshold alerts once per period.
  • Hard (Block at): the request is refused with 429 key_usage_limit_exceeded before any provider is called, so retries and fallbacks don’t run. A limit_policy_hard_triggered event is sent. Requests go through again when the window resets, or when you raise or turn off the cap.

Cost and tokens are counted when a call ends, so the call that crosses a cap completes and the ones after it are refused. A Requests cap refuses the request that would reach it, so Block at 100 allows 99 per period.

JSON
{ "error": { "message": "Spending limit reached", "type": "rate_limit_exceeded", "param": null, "code": "key_usage_limit_exceeded" } }

message is the limit’s description: Spending limit reached for a lifetime limit set in the dashboard, limit policy hard cap for a recurring one.

Customer budgets

Customer limits apply to requests tagged with a customer_identifier. See Customers to tag your requests.

  • Budget: go to Observability › Users & identifiers, click the customer, and under Budgets click the pencil next to Period budget. Or set period_budget and budget_duration (daily, weekly, monthly, or yearly) with Update user. Once the customer’s spend in the period passes it, their requests get 402 (Customer budget limit exceeded. Current usage: ..., Budget: ...) until the next period, and Respan sends a customer_budget_limit_reached event. Budget fields sent inside a request’s customer_params are ignored.
  • Rate limit: set Customer rate limit (requests/min) for every customer in Gateway › Routing config › Limits, or send customer_params.rate_limit on a request to override it for that customer. It applies to Chat Completions requests.
Customer limits: Customer monthly budget set to 25 and Customer rate limit (requests/min) set to 60
JSON
{ "model": "gpt-5.5", "messages": [{"role": "user", "content": "Hello!"}], "customer_params": { "customer_identifier": "user_8842", "rate_limit": 30 } }

A realtime session counts against the customer’s budget as its holds grow, and is closed with 1008 if it passes the budget.

Check current usage

WhatWhere
A key’s limitsGateway › API keys › your key: usage, what’s left, and the reset time for each limit
A limit policyGet a limit policy: current_state.current_value for the current window, with interval_start and interval_end
A customer’s budgetGet user: total_period_usage against period_budget, with period_start and period_end
One callGet a span by its X-Respan-Log-Id: limit_info holds the credit and budget state the call was checked against
Terminal
curl https://api.respan.ai/api/users/user_8842/ \ -H "Authorization: Bearer $RESPAN_API_KEY"

Admission order

Before any provider is called, a request passes these checks in order. The first one that fails decides the answer.

Request and token rate limits

The key’s, organization’s, and plan’s requests and tokens per minute. Refused with 429 rate_limit_exceeded. No log is written.

Payment deadline

An overdue bill refuses paid requests with 402 billing_payment_required. No log is written.

Limit policies and customer budget

A hard cap refuses with 429 key_usage_limit_exceeded. An exhausted customer budget refuses with 402. Both write a failed log at $0.

Key usage cap

The key’s lifetime max_usage. Refused with 429 key_usage_limit_exceeded.

Plan permission

A key at or over its monthly spending_limit, or an organization without a subscription, is refused with 402 plan_permission_denied.

Customer rate limit

Chat Completions only. Refused with 429 rate_limit_exceeded.

After admission, a call on Respan’s key is checked for credits. See When credits run out. Images, embeddings, speech, and transcription pass the same checks except tokens per minute and the customer rate limit. Realtime sessions are checked once, at the handshake.

Next steps