Budgets, caps & credits
Spend is bounded at three levels: each key has its own caps, a parent key caps the sum of its sub-keys, and your organization has a credit balance and, optionally, budgets. A call must fit all three before it runs.
Key caps
Section titled “Key caps”Every key carries a caps object (visible on GET /v1/key and on sub-keys returned by /v1/keys). Each cap is checked on every call:
| Cap | Default | Checked | Refusal |
|---|---|---|---|
usd_day |
none | At reservation: spent today + reserved + this call’s estimate must stay within it. Calendar day, UTC. | 402 key_cap_exceeded |
usd_month |
none | Same, for the calendar month, UTC. | 402 key_cap_exceeded |
rpm |
60 | At admission: requests in the current minute, refused attempts included. | 429 rate_limited |
tpm |
none | At reservation: the worst-case token estimate of every call that reached the reservation in the current minute — refused ones included, never corrected to actual use. | 429 rate_limited |
max_in_flight |
20 | At admission: calls reserved and not yet settled. | 429 too_many_in_flight |
max_output_tokens |
4,096 | At validation: max_tokens above it is refused; when you omit max_tokens, it becomes the ceiling. |
400 max_tokens_exceeds_key_limit |
Spend caps are in USD at your organization’s sale price. A call is admitted against the estimate it reserves, then counts its real cost once settled — see Admission & settlement. A Chat Completions or Responses call that fails without reported usage can still count the input part of its estimate, or the full estimate, against the caps. That amount is counted against the key’s caps (and its parent’s) only — it is not billed: no credits are debited, and it adds nothing to the cost in /v1/usage or /v1/credits. It shows in the key’s spent_usd on /v1/key. For rate limits and retry guidance, see Rate limits & caps.
Sub-keys and their parent
Section titled “Sub-keys and their parent”A key with the keys:manage scope can mint sub-keys with POST /v1/keys — one level deep. A sub-key never exceeds its parent: lower or equal caps and rates, a subset of its scopes and models, an expiry no later than the parent’s. Anything you leave out is inherited. A request that asks for more is refused with 400 child_exceeds_parent.
How the limits combine at call time:
- Spend is shared. A sub-key’s call reserves its estimate on the sub-key and on the parent’s daily and monthly counters. The parent’s
usd_dayandusd_monthare a ceiling for the parent and all its sub-keys together. When the parent’s cap refuses, the message reads “Spend cap reached for the parent of this key”. - Rates are per key. Requests per minute, tokens per minute and calls in flight are counted for the key that makes the call only.
- Status is inherited. Disabling, revoking or expiring the parent stops every sub-key: their calls answer
401 invalid_api_key.
See Sub-keys per customer for a full pattern.
Organization credits and budgets
Section titled “Organization credits and budgets”After the key’s caps, the call’s estimate must fit your organization:
| Code | HTTP | Meaning | What to do |
|---|---|---|---|
insufficient_credits |
402 | No active subscription, or not enough credits left for this call’s estimate. | Add credits or upgrade your plan in the dashboard. |
budget_exceeded |
402 | A budget your organization set in its dashboard is exhausted. | Raise the budget, or wait for its next period. |
spend_refused |
402 | Your organization’s spend controls refused the call for another reason. | Check your organization’s billing settings in the dashboard. |
pricing_unavailable |
503 | No price is configured for your organization yet. A retry will not fix it. | Contact support. |
admission_unavailable |
503 | The spend controls could not be checked; the call is refused rather than served unchecked. | Retry after Retry-After (5 s). |
GET /v1/credits (scope usage:read) returns your organization’s balance for the current period, in credits — the same unit as belarel.cost.credits:
{ "object": "credits", "unit": "credits", "has_subscription": true, "included": 50000, "consumed": 12840.5, "balance": 37159.5 }Check where a key stands
Section titled “Check where a key stands”GET /v1/key works with any valid key, whatever its scopes, and is never refused for calls in flight. It returns the key’s settings and caps, plus what it spent and reserved:
curl https://api.belarel.com/v1/key \ -H "Authorization: Bearer $BELAREL_API_KEY"import os, requests
r = requests.get( "https://api.belarel.com/v1/key", headers={"Authorization": f"Bearer {os.environ['BELAREL_API_KEY']}"},)day = r.json()["usage"]["day"]print(day["spent_usd"], day["reserved_usd"], day["remaining_usd"])const r = await fetch("https://api.belarel.com/v1/key", { headers: { Authorization: `Bearer ${process.env.BELAREL_API_KEY}` },});const { usage } = await r.json();console.log(usage.day.spent_usd, usage.day.reserved_usd, usage.day.remaining_usd);"usage": { "day": { "spent_usd": 3.12, "reserved_usd": 0.04, "limit_usd": 10, "remaining_usd": 6.84, "resets_at": "2026-10-03T00:00:00+00:00" }, "month": { "spent_usd": 41.9, "reserved_usd": 0.04, "limit_usd": null, "remaining_usd": null, "resets_at": "2026-11-01T00:00:00+00:00" }},"rate_limit": { "requests_per_minute": 60, "requests_this_minute": 7, "tokens_per_minute": null, "max_in_flight": 20, "in_flight": 1 }remaining_usd is null when the window has no cap. These figures are the key’s own; a parent’s figures include its sub-keys’ spend.
Get notified when a cap is reached
Section titled “Get notified when a cap is reached”When a spend cap refuses a call, Belarel sends the api_key.cap_reached webhook to your organization — at most once per capped key per UTC day. When the parent’s cap is the one that refused, the event names the parent key. Its payload carries the key’s caps and its spend for the day and the month, reservations included. Subscribe in your organization’s dashboard; see Webhooks.

