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

# Rate limits

> Request rate limits, response headers, and how to handle 429s.

All `/v1` endpoints share two independent **per-minute** rate limits:

| Scope         | Default limit              |
| ------------- | -------------------------- |
| Per API key   | **600 requests / minute**  |
| Per source IP | **1200 requests / minute** |

Both are fixed one-minute windows. Whichever limit you hit first applies. The
defaults are well above normal create-and-poll traffic and may be tuned over
time — read the response headers rather than hard-coding these numbers.

<Note>
  [`POST /v1/uploads`](/api-reference/uploads/upload-input-media) has an additional,
  tighter cap of **60 uploads / minute per account** — a rolling window — because
  it's free and unmetered. It applies on top of the per-key and per-IP limits
  above and returns the same `429` + `Retry-After`. Regular generation and polling
  traffic isn't affected.
</Note>

<Note>
  Generation jobs also have a separate **per-plan concurrency cap** — the number
  of jobs you can have in flight at once, the same cap as the web workshop. That
  cap is independent of the request limits on this page: polling status never
  counts against concurrency, and concurrency never shows up in the
  `X-RateLimit-*` headers.
</Note>

## Response headers

Every API response reports your current standing:

| Header                  | Meaning                                      |
| ----------------------- | -------------------------------------------- |
| `X-RateLimit-Limit`     | Total requests allowed in the current window |
| `X-RateLimit-Remaining` | Requests left in the current window          |
| `X-RateLimit-Reset`     | Seconds until the window resets              |

## When you exceed a limit

Requests over the limit receive HTTP `429` with a `Retry-After` header (seconds)
and this body:

```json theme={null}
{
  "error": {
    "code": "rate_limited",
    "message": "Rate limit exceeded. Retry after 60 seconds.",
    "type": "rate_limit_error"
  }
}
```

<Warning>
  **Rejected requests still count against the window.** A tight retry loop keeps
  the window full and you stay rate limited indefinitely. On a `429`, stop and
  wait the full `Retry-After` before sending anything else.
</Warning>

## Staying under the limits

* **Poll gently.** Poll job status every few seconds (images) to every 10–30
  seconds (video) — not in a tight loop. See [Async jobs](/concepts/async-jobs).
* **Respect `Retry-After`.** On `429`, sleep for the advertised seconds, then
  resume. Add jitter if multiple workers share a key or IP.
* **Cache stable resources.** `GET /v1/models` changes rarely — cache it for
  minutes to hours instead of fetching it per request.
* **Watch `X-RateLimit-Remaining`.** If it trends toward zero, slow down before
  you hit the wall instead of after.
