---
title: "Rate limits"
description: "pirouter rate limits by the per-minute quota of each deployment, not by API key or workspace: a used-up quota answers 429 rate_limited with Retry-After until the next full minute, and a provider's own 429 reaches you only after failover has run out of providers."
canonical: "https://pirouter.ai/docs/api/rate-limits"
last-updated: "2026-10-17"
lang: "en"
source: "content/docs/en/api/rate-limits.mdx"
---

# Rate limits

pirouter rate limits by the per-minute quota of each deployment, not by API key or workspace: a used-up quota answers 429 rate_limited with Retry-After until the next full minute, and a provider's own 429 reaches you only after failover has run out of providers.

pirouter rate limits per deployment, not per key and not per workspace. Each deployment has two per-minute quotas, requests per minute (RPM) and tokens per minute (TPM). When the deployment chosen for a request has used up its quota, the request gets `429` `rate_limited` with a `Retry-After` header. Switching keys does not get around a limit.

## Two kinds of 429

You can receive two kinds of `429`, and they are handled differently:

| Compared        | pirouter                                               | Provider                                                                     |
| --------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------- |
| Who answers     | The pirouter gateway                                   | The provider, passed on as is by pirouter                                    |
| `error.code`    | `rate_limited`                                         | The provider's own error body, without this code                             |
| `X-Pi-Provider` | Absent                                                 | Present                                                                      |
| `Retry-After`   | Always present, 1 to 60 seconds                        | Only if the provider sent one                                                |
| Failover        | None: nothing was forwarded or billed                  | Already done: the model's other providers were tried, and all of them failed |
| What you do     | Wait `Retry-After` seconds, or switch to another model | Retry after `Retry-After` if present, otherwise with growing intervals       |

> **Note:** Check whether `error.code` is `rate_limited`, or whether the response carries `X-Pi-Provider`, to tell them apart. A `429` from a pirouter quota never triggers failover; a `429` from a provider does.

## Rules

- **Counted per deployment.** One model can have several deployments, each with its own quota.
- **Only RPM and TPM.** Both are per-minute quotas set on the deployment.

* **Calendar minutes.** Counters reset on each full UTC minute; it is not a sliding window.

- **RPM is counted on admission, TPM after the request ends.** A refused request still counts toward the minute's requests. Tokens are added after the request ends, as the actual input plus output; once the minute's total has reached the limit, the next request is refused. So one large request can take the minute's usage past the limit.

* **Only the chosen deployment is checked.** pirouter first picks one deployment by the [routing rules](https://pirouter.ai/docs/guides/routing.md), then checks its quota. If that deployment is used up, the answer is `429`; pirouter does not move the request to another deployment of the same model.

For example, with an RPM of 60 on a deployment, the 61st request in a minute gets `429` with `Retry-After` set to the seconds left in that minute; once the next full minute starts, the count is back to zero and requests go through.

## The `rate_limited` response

When pirouter rate limits a request it answers `429` with `error.type` `rate_limit_error` and `error.code` `rate_limited`. Branch on `error.code` in your code; do not match the text of `error.message`.

The response carries a `Retry-After` header: the number of seconds until the next full minute, from 1 to 60. Browser code can read it too.

The gateway answers this response itself, so it carries only `X-Pi-Trace-Id` and the CORS headers, not `X-Pi-Provider`. The request was not forwarded to any provider and costs nothing: the quota is checked before any balance is held.

In the console's **Logs**, such a request has the status **Rate limited**.

## A 429 from the provider

When a provider rate limits a request, pirouter first fails over to the model's other providers, then moves on to the [model fallbacks](https://pirouter.ai/docs/guides/fallbacks.md) in your `models`. You do not see any of this. You receive the provider's `429` only when every candidate failed and the last attempt was still a provider `429`. Its status and body are the provider's, it carries `X-Pi-Provider`, and a `Retry-After` from the provider is passed on as is.

## Rate limits and model fallbacks

- **pirouter's rate limit does not trigger a model fallback.** When a request is refused by a quota from the start, pirouter does not move on to the next model in `models`; it answers `429`. To get a reply right away, send the request to another model yourself.
- **A rate limit during a fallback ends the chain.** When an attempt has failed and the next candidate is refused by a quota, the answer is `429` `rate_limited` with `Retry-After`, not the error of the earlier attempt.

One request makes at most 5 attempts in total, across all models.

## How to retry

| You receive                                                      | What to do                                                                                                                 |
| ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `429` `rate_limited`                                             | Wait `Retry-After` seconds and send again with fewer requests in flight. Switching keys does not help                      |
| A provider `429` (with `X-Pi-Provider`)                          | pirouter has already failed over to other providers. Wait `Retry-After` if present, otherwise retry with growing intervals |
| `402`                                                            | Do not retry; [add credit](https://pirouter.ai/docs/guides/billing.md#add-credit) first                                    |
| `500`, `502`, `503`, `504`, or `upstream_timeout` inside a `200` | Retry with growing intervals, measured in seconds                                                                          |

The official OpenAI and Anthropic SDKs retry a `429` on their own by default and read `Retry-After`. That is the SDK's behavior; the number of retries is set in the SDK.

For other errors, see [What to retry](https://pirouter.ai/docs/api/errors.md#what-to-retry).

## Not available today

- **Rate limits per key or per workspace.**
- **Remaining-quota headers.** Responses carry no `x-ratelimit-*` headers, so you cannot see how much of the minute's quota is left.
- **A concurrency cap.** Quotas count only requests and tokens per minute.

## Next steps

- [Model fallbacks](https://pirouter.ai/docs/guides/fallbacks.md): Switch to the next model when the one you asked for is unavailable.
- [Request and response headers](https://pirouter.ai/docs/api/headers.md): What each response header means and when it appears.
