# API rate limits

Three separate limits apply, one per surface: 240 requests a minute on the public API, 120 on the MCP endpoint and 60 on the A2A endpoint, each counted per calling address over a rolling sixty seconds. No rate-limit headers are sent — no `Retry-After`, no `X-RateLimit-*` — so a client must carry its own backoff.

- **Status:** Available
- **Audience:** developer
- **Last verified:** 2026-09-10
- **Canonical:** https://connectbyjbrh.com/developers/api-rate-limits/

## The limits

| Surface | Limit | Window | Response when exceeded |
|---|---|---|---|
| `/api/public/…` | 240 requests | 60 seconds, rolling | HTTP `429`, `{"error": {"code": "rate_limited", …}}` |
| `POST /mcp` | 120 requests | 60 seconds, rolling | HTTP `429`, JSON-RPC code `-32603` |
| `POST /a2a` | 60 requests | 60 seconds, rolling | HTTP `429`, JSON-RPC code `-32603` |

The public API's allowance is the most generous on purpose: these are cheap in-memory reads, and a documentation API that throttles a crawler into failure defeats what it is for. The limit is there to stop a loop, not to ration access. The A2A allowance is the smallest because one call there does more work — a skill searches, fetches and formats a page.

Counting is per calling address and the window slides: requests older than sixty seconds fall out of the count by themselves, so an allowance recovers gradually rather than resetting on a clock boundary. The counter lives in the serving process, which means it is not a global quota you can reason about across every instance — treat the number as the ceiling for a single well-behaved client, not as a budget to spend exactly.

## The headers that are not there

None of the three surfaces sends a rate-limit header. There is no `Retry-After` on a `429`, no `X-RateLimit-Limit`, `-Remaining` or `-Reset`, and no `RateLimit` field of any kind. A client that reads one will read nothing, and a library that requires one to compute a delay will have to be given a default.

So the only signal you get is the `429` itself, plus the limit quoted in the message text. Do not parse the message: it is prose for a human reading a log. Configure the numbers from this page and treat a `429` as the instruction to wait.

> **Careful** Retrying a `429` immediately is worse than doing nothing. The window is still full, the retry becomes another counted request, and a tight loop can hold a client at the limit indefinitely while making no progress at all.

## Staying comfortably under

- **Cache on `updated`.** Every page carries a date that moves only when the page is regenerated, and successful public API responses ask you to cache for five minutes.
- **Fetch a manifest instead of a hundred pages.** `/docs-manifest.json` is every public page in one document; `/docs-data/status.json` is every capability's status. One request replaces a walk.
- **Search once, fetch once.** The summary in a search result is the page's answer-first paragraph and often answers the question without a fetch at all.
- **Widen the delay, and add jitter.** Several clients that all wait exactly one second arrive together and re-trip the limit.
- **Remember the address is shared.** Everything behind one gateway shares a counter, so a fan-out design spends the allowance far faster than its per-worker rate suggests.

## Other ceilings that are not rate limits

**Request body size** — 256 KiB on `/mcp` and 128 KiB on `/a2a`; over that the answer is `413`, immediately and regardless of your rate.
**Result count** — Search returns at most 25 results however large a `limit` you ask for; listing caps at 1,000 items.
**Body length** — A fetched page body is capped at 60,000 characters, and an A2A skill truncates further — 6,000 or 8,000 characters depending on the skill.
**Argument bounds** — `q` is 2–200 characters and `ref` at most 300; exceeding one is a validation failure, not a limit.

None of these move with usage or with an account, because there are no accounts. They are the same for every caller and they are the same on every request.

## Questions

### Can I ask for a higher limit?

There is no mechanism to raise one, because there is no identity to raise it for. If a use case genuinely needs more than 240 documentation reads a minute, the static manifests are the right answer — they are one request and they contain the corpus.

### Do the three limits share a counter?

No. Each surface counts its own requests, so a client using the public API and the MCP endpoint together has both allowances rather than one shared between them.

### How do I know how much of the allowance I have left?

You cannot, from the response — nothing is reported back. Count on your own side if it matters, or design so it does not: a cache and a manifest fetch make the question moot.

## Related

- [The public API](https://connectbyjbrh.com/developers/public-api/)
- [API error shapes](https://connectbyjbrh.com/developers/api-errors/)
- [Designing a resilient client](https://connectbyjbrh.com/developers/errors-and-retries/)
- [The machine-readable documentation](https://connectbyjbrh.com/developers/machine-manifests/)
- [The Connect MCP server](https://connectbyjbrh.com/developers/mcp-server/)
- [Crawler policy](https://connectbyjbrh.com/developers/crawler-policy/)

## What this page is based on

- `backend/app/public_developer_api.py` — RATE_MAX and _rate_ok
- `backend/app/mcp_server.py` — RATE_MAX, MAX_BODY_BYTES
- `backend/app/a2a_server.py` — RATE_MAX, MAX_BODY_BYTES
- `backend/app/public_docs.py` — MAX_RESULTS, MAX_BODY_CHARS
