A budget per key, and a limit per day.
Every agent gets its own key with a prepaid budget, a spend limit per period, rate and concurrency caps, an allowed-model list and an expiry. When it reaches its line it gets a 402, nothing is charged, and the rest of your balance is untouched.
- budget_usd
- a total, out of your balance
- spend_limit_period
- daily, weekly or monthly
- rpm
- 1 to 100,000
- tpm
- 1 to 50,000,000
- concurrency
- 1 to 10,000
- allowed_models
- up to 100 ids
- user_budget_usd
- per end user, per period
- allowed_ips
- IPv4, IPv6 or CIDR, up to 50
- Over the line
- 402, nothing charged
What it does for an agent
- A prepaid budget
- budget_usd is a hard cap for that key alone, carved out of your balance. Once its charges plus open holds reach it, the key gets 402 key_budget_exhausted until it is raised.
- A spend limit per period
- monthly_cap_usd is dollars per spend_limit_period: a UTC month unless set, or daily (resets 00:00 UTC) or weekly (Monday 00:00 UTC). The counter restarts each period.
- RPM, TPM and concurrency
- rpm, tpm and concurrency cap requests a minute, tokens a minute and requests in flight per key. Over any of them: a 429 rate_limited with retry-after, nothing charged.
- Allowed models and expiry
- allowed_models lists up to 100 ids the key may call and nothing else. expires_at (Unix milliseconds) ends the key on a date you choose.
- A budget per end user
- user_budget_usd with user_budget_period (daily, weekly or monthly) caps what one end user of your app can spend on the key: send x-customer-id (or the OpenAI user field) on each request. Over it: 402 user_budget_reached for that user only; usage reports group by user.
- An IP allowlist and a lifetime cap
- allowed_ips (up to 50 addresses or CIDR ranges) makes the key answer 403 ip_not_allowed from anywhere else on the model, endpoint, batch and key-API routes. An owner can set a maximum key lifetime for the organization, and every new key expires within it.
- Spend alerts before the line
- A key with a budget or a period limit emails the organization at 50, 80 and 100 percent, once per threshold per period; an optional daily spend alert covers the whole account. The same moments can call your webhook (key.budget_reached, key.limit_reached, balance.low).
- Budget autopilot
- Name a cheaper model on the key in the console and it answers near the monthly limit instead of stopping; the response carries x-uplink-autopilot with the model the request asked for.
- Session caps and the loop guard
- session_max_spend_usd and session_max_requests cap one x-session-id run; loop_guard refuses the same tool call or body sent more than 5 times in 10 minutes.
Copy, paste, run
Field names from the KeyCreate schema in the OpenAPI document, read 2026-10-31. The console sets the same fields on a form.
# A key for one agent: its own budget, a daily spend limit, rate caps, two models, gone in 30 days.curl https://api.relayswitch.io/v1/keys \ -H "Authorization: Bearer $ADMIN_KEY" -H "Content-Type: application/json" \ -d '{ "name": "nightly-agent", "budget_usd": 50, "monthly_cap_usd": 5, "spend_limit_period": "daily", "rpm": 120, "tpm": 2000000, "concurrency": 4, "allowed_models": ["claude-sonnet-5-5", "glm-5.3"], "fallback_models": ["glm-5.3"], "session_max_spend_usd": 1, "loop_guard": true, "expires_at": '"$(( $(date +%s) * 1000 + 30 * 86400000 ))"' }'# The secret is returned once; afterwards only its prefix is shown.from openai import OpenAIimport os client = OpenAI( base_url="https://api.relayswitch.io/v1", api_key=os.environ["RELAYSWITCH_API_KEY"],)from openai import APIStatusError try: reply = client.chat.completions.create( model="claude-sonnet-5-5", messages=[{"role": "user", "content": "Run the nightly report."}], ) print(reply.usage.cost) # what this request cost, in dollarsexcept APIStatusError as e: code = e.body["error"]["code"] if e.status_code == 402: # daily_limit_reached, weekly_limit_reached, monthly_limit_reached: # the key's spend limit for its period; it resets on the period. # key_budget_exhausted: the key's own budget is used up; raise it. # insufficient_balance: the account needs a top-up. stop_agent(reason=code) elif e.status_code == 429: # rate_limited, session_cap or loop_detected; nothing was charged. wait(int(e.response.headers.get("retry-after", "5"))) else: raiseWhat a $5 daily limit buys an agent, at today's prices
One agent turn late in a session: 80,000 cached input tokens, 2,000 fresh input tokens and 1,500 output tokens. A day is 150 such turns; a month is 20 working days. Priced from the live catalog at render; cached input at the cached rate where the model has one.
| Model | A turn | Turns under $5 a day | A day (150 turns) | A month (20 days) |
|---|---|---|---|---|
| Claude Opus 5.5 | $0.0326 | 153 | $4.89 | $97.80 |
| Claude Sonnet 5.5 | $0.0129 | 387 | $1.94 | $38.70 |
| GPT-6.1 Sol | $0.0105 | 476 | $1.58 | $31.50 |
| DeepSeek V4.1 Flash | $0.00089 | 5,617 | $0.1335 | $2.67 |
| GLM 5.3 | $0.0076 | 654 | $1.15 | $22.92 |
Read the row, set monthly_cap_usd to the day you want with spend_limit_period "daily", and the agent stops there with a 402. Set budget_usd to the month and it cannot spend past it whatever the day count. Every request's own charge is in usage.cost and x-uplink-charge.
The two answers a key over its line gets
From the /v1/chat/completions responses in the OpenAPI document, read 2026-10-31. Neither is charged.
| Status | Code | Means | Clears when |
|---|---|---|---|
| 402 | key_budget_exhausted | The key’s own prepaid budget is used up. | budget_usd is raised, or another key is used. |
| 402 | daily_limit_reached / weekly_limit_reached / monthly_limit_reached | The key’s spend limit for its period. | The period resets (00:00 UTC, Monday 00:00 UTC, the first of the month) or the limit is raised. |
| 402 | insufficient_balance | The account balance cannot cover the request. | A top-up lands. |
| 429 | rate_limited | The key’s rpm, tpm or concurrency, or the account’s concurrency. | retry-after seconds pass. |
| 429 | session_cap / loop_detected | A session reached session_max_requests or session_max_spend_usd, or repeated one call more than 5 times in 10 minutes. | reset_at in the error body. |
Questions
For agent builders.
One key per agent, each with its own line
A budget, a daily limit, the models it may call, the day it expires. Card or crypto, no KYC, no subscription.