# Rate limits
Source: https://docs.file.ai/api/rate-limits

> Read the rate-limit headers, handle a 429 and back off before you hit the limit.

Production

# Rate limits

Copy pageCopy page

Read the rate-limit headers, handle a 429 and back off before you hit the limit.

Copy pageCopy page

fileAI limits how many API requests a project can make in each time window. Every response tells you how much of the allowance is left, and a request over the limit gets `429 Too Many Requests` with the number of seconds to wait. Pace your client with the headers and you rarely see a `429` at all.

## 

[​

](#how-it-works)

How it works

Two limits apply, each with its own quota and window:

Policy name

Counts

Applies to

`default`

Every request

All endpoints

`file-upload`

Upload initiations

`POST /v1/files/upload`, `/v1/files/upload/batch`, `/v1/files/upload/multipart` and `/v1/files/upload/multipart/batch`

*   An upload request counts against both: `file-upload` is checked first, then `default`. An upload rejected by `file-upload` never reaches `default`.
*   Each request costs one, whatever it contains. A batch upload registering 100 files costs the same as a single upload, so batching is the cheapest way to send many files.
*   Requests are counted per project. Every API key on the same project draws on the same allowance.
*   The window is fixed: it starts with the first request, and the full allowance returns when it ends. It does not refill gradually.

## 

[​

](#headers)

Headers

Every authenticated response carries these headers, success or error:

Header

Example

Meaning

`RateLimit-Policy`

`"default";q=<limit>;w=<window>`

Each limit that applied to this request: quota `q` per window of `w` seconds. Upload requests list both `"file-upload"` and `"default"`, except when the `file-upload` limit itself returns `429`: then only `"file-upload"` is listed, because `default` was never checked.

`RateLimit`

`"default";r=<remaining>;t=<seconds>`

The most constraining limit right now: requests remaining `r`, and seconds until its window resets `t`. `t` is a number of seconds, not a timestamp.

`Retry-After`

`<seconds>`

Seconds to wait before retrying. Sent on `429` only.

`X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`

Legacy equivalents for the `default` limit. `Reset` is seconds until reset. Deprecated: use `RateLimit` and `RateLimit-Policy`.

`X-RateLimit-FileUpload-Limit`, `-Remaining`, `-Reset`

Legacy equivalents for the `file-upload` limit, on upload requests. Deprecated.

Read your actual quota and window from `RateLimit-Policy`; they can differ between projects.

## 

[​

](#the-429-response)

The 429 response

A request over a limit is rejected before it runs, so nothing is created or changed. The body is a [problem document](/api/errors) with code `RATE_LIMITED` and a `retryAfter` member that matches the `Retry-After` header:

429 Too Many Requests

```
{
  "type": "https://errors.file.ai/rate-limited",
  "title": "Too many requests",
  "status": 429,
  "detail": "API rate limit exceeded",
  "instance": "/prod/v1/files",
  "code": "RATE_LIMITED",
  "requestId": "0f1c8e03-978e-40d5-bc93-6894a57f9324",
  "errors": [],
  "retryAfter": 18
}
```

`detail` is `File upload rate limit exceeded` when the `file-upload` limit was hit. Branch on `code`, not `detail`. The `RateLimit` header on a `429` describes the limit that was breached.

## 

[​

](#back-off)

Back off

1

Watch the headers

Parse `RateLimit` on every response. When `r` gets low, spread the remaining requests over the `t` seconds left instead of sending them at once.

2

On a 429, wait Retry-After

Sleep for `Retry-After` seconds (or `retryAfter` from the body), then retry the same request. Waiting longer is fine; retrying sooner just earns another `429`.

3

Make retries safe

Send an [`Idempotency-Key`](/api/idempotency) on writes. A `429` is never stored against the key, so the retry runs normally.

Python

Node.js

```
import re, time, requests

def request_with_backoff(method, url, **kwargs):
    for _ in range(5):
        r = requests.request(method, url, **kwargs)
        if r.status_code == 429:
            time.sleep(int(r.headers.get("Retry-After", "1")))
            continue
        # Slow down before the limit: pause when only a few requests remain
        m = re.search(r";r=(\d+);t=(\d+)", r.headers.get("RateLimit", ""))
        if m and int(m.group(1)) == 0:
            time.sleep(int(m.group(2)))
        return r
    raise RuntimeError(f"still rate limited, X-Request-Id {r.headers.get('X-Request-Id')}")
```

```
async function requestWithBackoff(url, init) {
  let res;
  for (let attempt = 0; attempt < 5; attempt++) {
    res = await fetch(url, init);
    if (res.status === 429) {
      const wait = Number(res.headers.get("retry-after") ?? 1);
      await new Promise((r) => setTimeout(r, wait * 1000));
      continue;
    }
    // Slow down before the limit: pause when nothing remains in this window
    const m = /;r=(\d+);t=(\d+)/.exec(res.headers.get("ratelimit") ?? "");
    if (m && Number(m[1]) === 0) {
      await new Promise((r) => setTimeout(r, Number(m[2]) * 1000));
    }
    return res;
  }
  throw new Error(`still rate limited, X-Request-Id ${res.headers.get("x-request-id")}`);
}
```

Several workers on one project share one allowance. Coordinate them, for example with a shared queue, rather than letting each back off on its own.

## 

[​

](#related)

Related

## Errors

Problem documents, codes and when to retry.

## Idempotent requests

Retry writes without applying them twice.

## Uploads

Batch uploads cost one request for up to 100 files.

## Troubleshooting

Common failures and how to fix them.

[Previous](/api/errors)[

Pagination

Page through list endpoints with cursors, or with the legacy page and limit parameters.

Next

](/api/pagination)

⌘I

## On this page

*   [How it works](#how-it-works)
*   [Headers](#headers)
*   [The 429 response](#the-429-response)
*   [Back off](#back-off)
*   [Related](#related)
