# Rate limits

> The caps that govern provisioning and submission, and the response headers that report live quota so you can self-throttle.

Lanes Forms tells you where you stand instead of making you find out by being refused.
Every response on a throttled path carries its quota policy, and where the server has
actually counted, the live remaining quota too.

## The headers

```http
RateLimit-Policy: "submissions";q=10;w=60
RateLimit: "submissions";r=7;t=42
RateLimit-Limit: 10
RateLimit-Remaining: 7
RateLimit-Reset: 42
```

`RateLimit-Policy` and `RateLimit` are the fields from the IETF
`draft-ietf-httpapi-ratelimit-headers` specification. In `RateLimit-Policy`, `q` is the
quota and `w` is the window in seconds. In `RateLimit`, `r` is the quota you have left
and `t` is the seconds until the window resets. The quoted name ties the two together,
so a response can carry more than one policy without ambiguity.

`RateLimit-Limit`, `RateLimit-Remaining`, and `RateLimit-Reset` are the older spelling
of the same three numbers. The draft supersedes them, but plenty of clients only
understand those, so both are sent.

**Read the headers, do not hard-code the numbers**

The values below are the defaults. The headers are authoritative, and they are what an
agent should pace itself against.

## On a 429

Every `429` carries `Retry-After` with a number of seconds:

```http
HTTP/1.1 429 Too Many Requests
Retry-After: 60
Content-Type: application/json

{"error": {"code": "submission_rate_limited", "message": "...", "docs_url": "..."}}
```

Wait that long, then retry. If a response carries both `Retry-After` and `RateLimit`,
`Retry-After` wins.

For a limit measured over 24 hours, `Retry-After` is capped at one hour rather than
reporting the full window. Telling a caller to sleep for a day is not actionable, and
retrying after an hour costs one refused request and gets you a fresh number.

## The documented limits

| Limit | Default |
|---|---|
| Submissions per minute, per IP, per form | 10 |
| Submissions held on an unclaimed form | 25 |
| Bytes per submission | 65,536 (64 KB) |
| Bytes per request body | 1,048,576 (1 MB) |
| Forms created per IP per 24h | 20 |
| Forms created per email domain per 24h | 100 |
| Claim emails per address per 24h | 5 |
| Active API keys per workspace | 10 |
| MCP requests per minute, anonymous | 60 |
| MCP requests per minute, with a workspace key | 300 |

Exceeding one of these returns a `429` with a specific `code`:
`submission_rate_limited`, `unclaimed_submission_cap`, `form_creation_rate_limited`,
`domain_rate_limited`, `claim_email_rate_limited`, `pending_claim_exists`, or
`api_key_limit_reached`. Each is listed with its fix in [Errors](/docs/forms/errors).

Anonymous form creation also has a service-wide ceiling that answers `503`
`anonymous_provisioning_paused` rather than `429`. It is a temporary pause, not a
per-caller throttle, so it is not something backing off harder will clear.

## Staying under them

- Read `RateLimit-Remaining` and slow down before it reaches zero, rather than sprinting
  into a `429`.
- Use a workspace API key. It raises the MCP transport limit from 60 to 300 requests per
  minute and provisions forms already claimed, skipping the anonymous-path gates.
- Batch nothing that does not need batching. The per-IP submission limit is per form, so
  spreading real traffic across forms is fine; hammering one is not.
- Quote `x-request-id` from the response if you believe a limit is wrong.
