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

# Rate limits

> Per-key limits and production Upstash enforcement

Each API key has configurable limits (`rate_limit_per_minute`, `rate_limit_per_day`). When exceeded, Clausum returns **429 Too Many Requests** with code `RATE_LIMIT_EXCEEDED`.

## Defaults (new secret keys)

| Window     | Typical default |
| ---------- | --------------- |
| Per minute | 120 requests    |
| Per day    | 10,000 requests |

Publishable and ingest keys may use lower defaults. Adjust per key in **Conexiones → Claves API** when creating or editing a secret key.

<Note>
  Limits are **per API key**. Use separate keys for checkout vs batch jobs so bursts do not throttle each other.
</Note>

## Production enforcement

In production, rate limits use **Upstash Redis** (`UPSTASH_REDIS_REST_URL` + `UPSTASH_REDIS_REST_TOKEN`) for distributed counting across Vercel instances. Without Upstash in production, Clausum fails closed on rate-limit checks — ensure Redis is configured before go-live.

Sandbox / development may use in-memory fallback when Upstash is not set.

## Response headers

| Header                  | Description                             |
| ----------------------- | --------------------------------------- |
| `X-RateLimit-Remaining` | Requests left in the current window     |
| `X-RateLimit-Reset`     | When the window resets (Unix timestamp) |

## Handling 429s

```ts theme={null}
async function requestWithRetry(
  url: string,
  options: RequestInit,
  attempt = 0
): Promise<Response> {
  const res = await fetch(url, options)
  if (res.status !== 429 || attempt >= 5) return res

  const reset = Number(res.headers.get("x-ratelimit-reset")) || 0
  const waitMs = reset ? Math.max(0, reset * 1000 - Date.now()) : 2 ** attempt * 250
  await new Promise((r) => setTimeout(r, waitMs + Math.random() * 250))
  return requestWithRetry(url, options, attempt + 1)
}
```

## Tips

<AccordionGroup>
  <Accordion title="Assess once per decision" icon="gauge-high">
    Call assess at checkout submit or payout authorization — not on every keystroke. Use the [browser SDK](/guides/browser-sdk) for device signals only.
  </Accordion>

  <Accordion title="Idempotent retries" icon="rotate">
    Retries after timeout should reuse the same `order_id` or `Idempotency-Key` — they do not double-count against velocity the same way duplicate orders would.
  </Accordion>

  <Accordion title="Request higher limits" icon="arrow-up-right-dots">
    High-volume production? Contact [api@clausum.ai](mailto:api@clausum.ai) before launch.
  </Accordion>
</AccordionGroup>
