> ## Documentation Index
> Fetch the complete documentation index at: https://docs.compute.prentis.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Rate limits and quotas

> What throttles you, which of the three 429s you are looking at, and how to back off.

Three different things can slow you down, and they arrive as the same HTTP status. Telling
them apart is the difference between "retry in a moment" and "retrying will never work".

## The three 429s

| Code                     | What it means                                                                       |  Retry? |
| ------------------------ | ----------------------------------------------------------------------------------- | :-----: |
| `TENANT_RATE_LIMITED`    | Your account is going faster than its limit allows.                                 | **yes** |
| `UPSTREAM_RATE_LIMITED`  | The model itself is temporarily over capacity. Nothing about your account is wrong. | **yes** |
| `TENANT_BUDGET_EXCEEDED` | Your account hit its spending limit.                                                |  **no** |

<Warning>
  The third one is the trap. `TENANT_BUDGET_EXCEEDED` is a `429`, so a client that branches
  on the status code alone will retry it forever and never succeed. **Branch on
  `error.code`, not on the status.**
</Warning>

## Backing off

Both retryable cases set `Retry-After` in seconds. Honour it — it is the platform telling
you what it knows, and it beats a fixed guess:

```python theme={null}
import time
import openai

for attempt in range(5):
    try:
        resp = client.chat.completions.create(...)
        break
    except openai.RateLimitError as e:
        code = (e.body or {}).get("error", {}).get("code")
        if code == "TENANT_BUDGET_EXCEEDED":
            raise                                     # retrying cannot help
        wait = float(e.response.headers.get("Retry-After", 2 ** attempt))
        time.sleep(wait)
```

If `Retry-After` is absent, exponential backoff with jitter is the right fallback. Retrying
immediately, in a loop, across every worker you have, turns a brief upstream wobble into a
sustained one.

## Money, not speed

Three more codes stop you for billing reasons rather than throughput. None of them are
retryable, and none of them are billed:

| Code                   | Status | Fix                                                    |
| ---------------------- | :----: | ------------------------------------------------------ |
| `INSUFFICIENT_BALANCE` |   402  | Add credits in the console.                            |
| `PAYMENT_REQUIRED`     |   402  | A payment is overdue — update the payment method.      |
| `TENANT_SUSPENDED`     |   403  | The account is suspended; check billing or contact us. |

Auto-reload, spending limits and the current balance all live in the console under
**Billing**. Setting a spending limit is what produces `TENANT_BUDGET_EXCEEDED` — it is a
guardrail you asked for, not a failure.

## Reducing the pressure

* **Stream.** `stream: true` does not change your token cost, but it frees the connection
  sooner and gets the first token to your user much earlier.
* **Cap `max_tokens`.** An unbounded generation occupies capacity until the model decides
  to stop, and you pay for all of it.
* **Retry the right thing.** A retry after `UPSTREAM_RATE_LIMITED` may well land on a
  different provider for the same model — that is what `x-provider-class: fallback` on the
  response is telling you.

## Seeing your own usage

Every response carries a `usage` object with the token counts you were billed on. The
console shows the same figures per request, per model and per day, so reconciling what you
measured against what you were charged does not require an export.

## Next

<CardGroup cols={2}>
  <Card title="Errors" icon="triangle-exclamation" href="/errors">
    All 28 codes, with retryability and billing.
  </Card>

  <Card title="Authentication" icon="lock" href="/authentication">
    What a key is allowed to reach in the first place.
  </Card>
</CardGroup>
