---
title: Rate limits
slug: rate-limits
docTags: 
createdAt: 2026-06-02T09:00:53.233Z
---

Every response tells you where you stand against the rate limit. Read the
headers and back off on `429` and you'll never get surprised.

## Rate-limit headers

Every response carries these headers:

| Header                  | Meaning                              |
| ----------------------- | ------------------------------------ |
| `X-RateLimit-Limit`     | Your ceiling for the current window. |
| `X-RateLimit-Remaining` | Requests left in the current window. |
| `X-RateLimit-Reset`     | When the window resets.              |

The standardized draft equivalents (`RateLimit-Limit`, `RateLimit-Remaining`,
`RateLimit-Reset`) are emitted too, so a client built against either spelling
works.

The current limit is **10,000 requests per minute**. (This is the operating
ceiling today, not a contractual floor — don't hard-code it; read
`X-RateLimit-Limit` if you need the live value.)

## When you're limited

Exceeding the limit returns `429` with a `rate_limit_error`:

```json
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests.",
    "type": "rate_limit_error",
    "request_id": "req_a1b2c3"
  }
}
```

The response includes a `Retry-After` header — the number of seconds to wait
before retrying. Honor it.

## Handling it well

Retry on `429` (and on `5xx`) with exponential backoff, preferring the
`Retry-After` value when present:

```python
import time, requests

def call_with_retry(session, url, max_retries=5):
    for attempt in range(max_retries):
        resp = session.get(url)
        if resp.status_code != 429:
            return resp
        wait = int(resp.headers.get("Retry-After", 2 ** attempt))
        time.sleep(wait)
    return resp  # give up after max_retries
```

For large workloads, prefer the **batch** endpoints over a tight loop of
single-email calls — submit a job and poll, rather than hammering `/v1/verify`.
See the **Quickstart** and the API reference.
