# Rate limits

A per-minute limit scaled by plan, a monthly quota on Free, and a per-IP budget without a key. Every limited response says how long to wait.

## Limits by plan

Two limits on calls made with a key: a per-minute window, and a monthly quota on Free. There is no daily cap.

| Plan | Per minute | Sustained | Per month |
|------|------------|-----------|-----------|
| Free | 60 / min | ~1 req/sec | 10,000 calls |
| Hobby | 600 / min | ~10 req/sec | no cap |
| Pro | 6,000 / min | ~100 req/sec | no cap |
| Scale | 60,000 / min | ~1000 req/sec | no cap |
| Enterprise | custom | — | no cap |

The two are independent: the window caps burst speed, the quota caps monthly volume. Pricing: https://tickerbot.io/pricing

## The per-minute window

Burst speed, measured over a rolling minute. The window is per account: every key on the account, plus the dashboard and the sandbox, shares it, so adding keys does not add throughput. The window is a rolling minute, not a clock minute: brief bursts above the sustained rate are absorbed as long as you stay under your per-minute cap.

When you cross the limit you get `429 rate_limit_exceeded` with a `Retry-After` header counting down to the moment your oldest request ages out of the window (at most 60s), which is not the same as waiting for the top of the minute. The JSON body names your plan’s limit and carries `retry_after_seconds` and an `upgrade_url`.

## The monthly quota

Free plan only. Paid plans have no monthly cap. The Free plan includes 10,000 calls per calendar month (UTC), resetting on the 1st. What counts is every call you compose: with a key, from the dashboard Shell, or from a Try-it panel on these pages while signed in. The dashboard's own tool tabs (Tickers, Signals, Webhooks) refresh on their own and do not count; they are capped at 10,000 calls a day per account instead.

When the quota is used up you get `402 monthly_quota_exceeded` with a `resets_at` timestamp and an `upgrade_url`. Rejected requests (429s and quota 402s) and server-side 5xx errors never consume quota. Quota 402s carry both header sets (`X-Quota-*` and the rate-limit headers), and an out-of-quota client bursting past its per-minute window still gets a `429` like anyone else.

## Headers

Every limited response reports where you stand, so you can slow down before you are stopped.

### Rate-limit headers

Every rate-limited response (success and 429 alike, quota-blocked 402s included) carries the rate-limit headers. They are absent only where no limiter runs: unauthenticated requests (401s) and Enterprise plans with no finite limit.

| Header | Description |
|--------|-------------|
| `X-RateLimit-Limit` | Per-minute request limit for the account (60/600/6,000/60,000 for Free/Hobby/Pro/Scale, custom for Enterprise). The window is shared by every key on the account, the dashboard, and the sandbox — adding keys does not add throughput. |
| `X-RateLimit-Remaining` | Requests remaining in the current minute window. |
| `X-RateLimit-Reset` | Unix timestamp (seconds) when the per-minute window resets — same format as `X-Quota-Reset`. |
| `Retry-After` | Seconds to wait before retrying. Present only on 429 responses. |
| `X-RateLimit-Warning` | Appears on successful responses once the window is ≥80% consumed — e.g. `Approaching plan limit (49/60).` Treat it as the back-off signal before the 429. |

### Quota headers

On Free-plan keyed `/v2/*` responses only: on paid plans there is no quota to report.

| Header | Description |
|--------|-------------|
| `X-Quota-Limit` | Monthly call quota for the plan (10,000 on Free; paid plans are unmetered and omit these headers). |
| `X-Quota-Remaining` | Calls remaining in the current calendar month (UTC). |
| `X-Quota-Reset` | Unix timestamp (seconds) of the first instant of next month, when the quota resets. |

## Query time limit

Every query is cut off at 30 seconds. The heaviest reads are whole-market ones: a cold as-of scan at an intraday instant can come close (https://tickerbot.io/docs/asof.md, Response time). Narrow with `q`, `universe` or `asset_class`, pin `interval` on as-of reads, or ask for a bare date when the close is enough.

## Without a key

The no-key sandbox (`/v2/sandbox/*`) is the same API with the same responses; without a key it is budgeted per IP at 30 requests/hour and 60/day. Carries `limits` and `retry_after_seconds` in the body, plus `Retry-After`, `X-Sandbox-Remaining-Hour` and `X-Sandbox-Remaining-Day` headers (the two `X-Sandbox-Remaining-*` headers also ride on every successful anonymous sandbox response, so you can watch the budget before hitting it). Signed in on the docs, or with an API key on `/v2`, your plan's per-minute limit applies instead.

## Backing off

- Read the `Retry-After` header on a 429 and wait at least that many seconds.
- Use `X-RateLimit-Remaining` to throttle yourself proactively.
- For which other errors are worth retrying, see Errors (https://tickerbot.io/docs/errors.md).
