URL: /docs/api/rate-limits

---
title: Rate limits
description: Per-key, per-plan limits counted in fixed one-minute windows, the headers on every response, and how to recover from a 429.
---

The rate limit is **per key, per plan**, counted in fixed one-minute windows. REST calls and [MCP](/docs/mcp/introduction) calls come out of the same window: one key, one counter, whichever interface it is used through.

The counter lives in Postgres rather than in each server instance, so the published number is a real shared ceiling rather than a per-instance one.

## Limits

| Plan | Requests a minute, per key | Live keys per account |
| --- | --- | --- |
| Free | 20 | 2 |
| Pro | 60 | 5 |
| Business | 120 | 10 |

Source: `PLAN_LIMITS` in the application. `GET /api/v1/me` returns the live figure for the key you hold, under `rate_limit.limit` — read it rather than hard-coding a row of this table. A key carries no plan of its own; the owner's current plan is resolved behind it on every request, so an upgrade or a lapse moves the limit without the key being reissued. See [Authentication](/docs/api/authentication).

Two keys on one account get two counters. That separates the rate-limit bookkeeping and nothing else — reveals, holds and the reveal ledger are account-scoped.

## The window is fixed, not sliding

The counter is keyed on the truncated minute. It resets on the wall-clock minute boundary, not sixty seconds after your first request.

The practical consequence: `X-RateLimit-Remaining` can read the same before and after a burst simply because the minute turned over, which looks like the limit not working. It is working; you crossed a boundary. Pace against `X-RateLimit-Reset` rather than counting your own calls.

## Headers on every response

```http
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 47
X-RateLimit-Reset: 1758506460
```

| Header | Value |
| --- | --- |
| `X-RateLimit-Limit` | The plan's per-minute ceiling. |
| `X-RateLimit-Remaining` | What is left in the current one-minute window. |
| `X-RateLimit-Reset` | An **epoch second** — when the current window rolls over. Not a duration, so you can schedule against it without knowing when the response was generated. |

These ride on every response, the `429` included. They are least useful on a `200` and most useful on the response that tells you to slow down. From a browser they are exposed through `Access-Control-Expose-Headers`.

## When you hit a 429

```http
HTTP/1.1 429 Too Many Requests
Retry-After: 14
X-RateLimit-Limit: 20
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1758506460
```

```json
{
  "error": {
    "code": "rate_limited",
    "message": "Rate limit is 20 requests a minute per key on the free plan. Retry in 14s."
  }
}
```

Sleep for `Retry-After` seconds and retry. The header is the number of seconds until the current window ends, so a client that honours it lands in a fresh window rather than in the tail of the one it just exhausted.

```python
import os, time, requests

BASE = "https://getrevised.com/api/v1"
AUTH = {"Authorization": f"Bearer {os.environ['REVISED_API_KEY']}"}

def get(path: str, **params) -> dict:
    """GET with one retry per 429, paced by Retry-After."""
    while True:
        res = requests.get(f"{BASE}{path}", headers=AUTH, params=params, timeout=30)
        if res.status_code != 429:
            res.raise_for_status()
            return res.json()
        wait = int(res.headers.get("Retry-After", "5"))
        print(f"rate limited, sleeping {wait}s")
        time.sleep(wait)

page = get("/domains", limit=250, rsMin=60)
print(len(page["data"]), "listings")
```

```text Output
rate limited, sleeping 14s
250 listings
```

## Staying under it

- **Read fewer, larger pages.** `limit` goes to 250 and a request costs the same whether it returns 1 row or 250. A full pass at `limit=250` on the free plan is 20 pages a minute, which is 5,000 listings a minute. See [Pagination](/docs/api/pagination).
- **Poll with `since` rather than re-reading the directory**, on a plan that includes it. A delta is a handful of pages; a full pass is not.
- **Do not retry a `401`, a `400` or a `404`.** Failed requests still spend the counter.
- **Do not spread one job across several keys to dodge the limit.** Use the key ceiling for separate systems that genuinely need separate budgets, and upgrade the plan if one system needs more throughput.
- `GET /api/v1/me` costs one request and no reveals. Calling it once at startup is cheap; calling it before every request is not.

## Failed authentication is limited separately

Requests that fail authentication are counted against a separate in-memory budget keyed by client IP — generous for a client with a stale key in its configuration, hostile to a loop guessing prefixes. Over it, the response is `429` `rate_limited` with `Too many failed key attempts. Retry in Ns.` and a `Retry-After`, raised before any database lookup. Authenticated traffic never touches that guard — only a failure spends a token from it.
