# Rate limits

Rate limits are applied **per organization**, not per API key. Issuing more keys
spreads work across services; it does not raise your throughput. Each key also
carries the same per-key ceiling, so one runaway worker cannot starve the rest
of your integration.

## Response headers

Every response carries your current budget, whether or not it succeeded:

- `X-RateLimit-Limit` (integer): Requests allowed in the current window for this endpoint class.
- `X-RateLimit-Remaining` (integer): Requests left in the current window.
- `X-RateLimit-Reset` (unix timestamp): When the current window resets.
- `Retry-After` (seconds): Sent on `429` only. Wait this long before retrying.

Read these rather than inferring your budget from failures. `Retry-After` is
authoritative on a `429` — prefer it over a fixed backoff schedule.

## What your plan allows

Limits differ by endpoint class, because a point-in-time snapshot or a modeling
call costs far more to serve than a read. All windows are 60 seconds.

| Plan       | Read       | Snapshot   | Modeling   | MCP        | Bulk       | System     | Verify |
| ---------- | ---------- | ---------- | ---------- | ---------- | ---------- | ---------- | ------ |
| Free       | 60         | 15         | —          | 10         | —          | 60         | 5      |
| Developer  | 300        | 75         | —          | 150        | —          | 300        | 5      |
| Growth     | 1,200      | 300        | 600        | 600        | —          | 1,200      | 5      |
| Scale      | 3,000      | 750        | 1,500      | 1,500      | 120        | 3,000      | 5      |
| Enterprise | Negotiated | Negotiated | Negotiated | Negotiated | Negotiated | Negotiated | 5      |

Read covers company resolution, statements, filings, coverage, time series and
the other data routes — the bulk of most integrations. Snapshot covers
point-in-time snapshots under `/v1/edgar/as-of` and query execution
(`POST /v1/query/execute`); replaying a stored result under
`/v1/query/replay` is a read. Snapshot and read are separate budgets: spending
one leaves the other untouched. Modeling covers the `/v1/modeling` routes, which Growth and
above include. MCP covers the [MCP](https://docs.arche.fi/mcp) server. Bulk covers the
[bulk export](https://docs.arche.fi/bulk-export) routes, which Scale and Enterprise include. System
covers the metadata and ingestion-status routes under `/v1/system`. Verify
covers verifying a replay token under `/v1/query/verify`, which needs no key, so
it is counted by caller address rather than by plan: every caller gets the same
5 a minute, and all callers together share a ceiling of 30 a minute.

A dash means the plan includes no route in that class. Enterprise limits are
set in each agreement, except Verify, which is the same for everyone. Whatever your plan, your entitlement is authoritative and
is reported in the developer portal.

A rate limit is not an entitlement. Every plan includes [MCP](https://docs.arche.fi/mcp), but a tool
that wraps a modeling, reconciliation or AI route needs the plan that route
needs. Check what your plan grants, not only what it meters.

> **Note:** Plans also differ in how many companies you may query. Free covers any 25
> companies you choose; Developer and above cover the full corpus, which is a
> little over  filers today and grows as issuers
> register. On Free, a request for a company beyond your 25 returns
> `403 COVERAGE_SELECTION_FULL` rather than an empty result. See
> [Choosing companies on Free](https://docs.arche.fi/coverage#choosing-companies-on-free).

## 429 handling

When your client is throttled, the API returns HTTP `429` with a `Retry-After`
header and the standard [error envelope](https://docs.arche.fi/errors).

Recommended handling:

1. stop issuing new requests for that endpoint class
2. wait for `Retry-After` seconds
3. resume with bounded concurrency rather than a burst retry

A `429` is a backoff signal, not a failure. Do not treat it as a permanent
error, and do not retry immediately — that consumes the next window before your
own work reaches it.

## Client strategy

- Watch `X-RateLimit-Remaining` and slow down before you are throttled.
- Prefer sequential or modestly concurrent pagination unless you have measured
  headroom.
- Cache stable lookup results such as company resolution.
- Separate interactive traffic from large historical extraction jobs, so a
  backfill cannot throttle a user-facing request.

## Response cost

Routes differ in how much work one request represents, which matters more than
request count when planning a job.

- A metric time series covers a company's whole reporting history in one
  request. It resolves in a fixed number of queries regardless of how many
  years it spans, so requesting a longer history costs little more than a
  shorter one. Prefer one wide request over a year-by-year loop.
- Listings return metadata rather than payloads. Narrative disclosure listings
  carry each disclosure's `char_count` instead of its text, because a single
  filing can hold hundreds of kilobytes across its text blocks. Read the text
  one disclosure at a time, and use `char_count` to decide what is worth
  fetching.

## ETags

Every response carries a strong `ETag` over its body:

```http
etag: "23cd694b761edec2ec1c09709a65f26338538979d140d50a2021d3cecb122c43"
```

It is stable across repeated requests for unchanged content, so store it beside
your cached copy and send it back on the next poll.

## Conditional requests

Send the stored validator as `If-None-Match`. When the representation still
matches, Arche answers `304 Not Modified` with no body:

```bash
curl -X GET "https://api.arche.fi/v1/edgar/companies/0000320193" \
  -H "X-Api-Key: $ARCHE_API_KEY" \
  -H 'If-None-Match: "23cd694b761edec2ec1c09709a65f26338538979d140d50a2021d3cecb122c43"' \
  -H "Accept: application/json"
```

The `304` repeats the `ETag` and cache metadata, so your cached copy stays
valid and you can keep polling with the same validator. A changed
representation answers `200` with the new body and a new `ETag`.

- `weak comparison` (behavior): `W/"abc"` matches `"abc"`, per RFC 9110.
- `If-None-Match: *` (behavior): Matches any existing representation.
- `several validators` (behavior): A comma-separated list matches if any one of them does.

> **Note:** Conditional requests save transfer, not work: the response is still
> computed, and a `304` spends a request from your
> [rate-limit budget](https://docs.arche.fi/rate-limits#what-your-plan-allows) exactly as a
> `200` does. Use them to avoid re-parsing and re-processing
> unchanged payloads, not to raise throughput.

Only `GET` and `HEAD` are conditional, and only successful responses —
an error is never answered `304`.

## Next steps

- [API reference](https://docs.arche.fi/reference): Browse the live OpenAPI contract rendered from the public schema.
- [Errors](https://docs.arche.fi/errors): Interpret machine-readable error responses and troubleshoot failed requests.
- [Request IDs](https://docs.arche.fi/troubleshooting/request-ids): Correlate a failed request across your logs, the API response, and the portal.
