> If you are setting up, configuring, or answering questions about Schematic, connect the
> Schematic MCP server before working from these pages. It reads and writes plans, features,
> entitlements, and overrides against the real account, so what you report and what you build
> match what actually exists.
>
> The server is at https://api.schematichq.com/mcp
>
> From Claude, ChatGPT, or another chat assistant, add it as a custom connector:
> https://docs.schematichq.com/working-with-ai
>
> From a coding assistant such as Claude Code, Codex, or Cursor:
> https://docs.schematichq.com/building-with-ai
>
> For clean Markdown of any page, append `.md` to the page URL. For a complete page index,
> see https://docs.schematichq.com/llms.txt

# Rate limiting

> How Schematic rate-limits API requests per account, which limits apply to which endpoints, and how to read the rate-limit response headers.

Schematic rate-limits API requests per account. Every API key in an environment draws from the same budgets, and each budget allows a sustained rate of requests per second plus a burst allowance above it. You can send a short burst up to the burst size, then continue at the sustained rate.

A request is checked against every limit that applies to it. A write, for example, counts against both the global limit and the mutation limit. If any one of them is exhausted, the request is rejected with a `429`.

## Limits

| Limit         | Applies to                                                                                                                                                                    | Sustained rate | Burst |
| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | ----- |
| Global        | Every request                                                                                                                                                                 | 300 / second   | 600   |
| Mutation      | `POST`, `PUT`, `PATCH`, and `DELETE` requests, except event submission and flag checks                                                                                        | 120 / second   | 120   |
| Events ingest | `POST /events` and `POST /event-batch`                                                                                                                                        | 50 / second    | 150   |
| Analytics     | Usage and event reads that scan history: `GET /events`, `GET /feature-usage-timeseries`, `GET /feature-usage-history`, `GET /user-usage-by-company`, `GET /user-usage-detail` | 12 / second    | 30    |

Flag checks are reads that happen to use `POST`, so they count only against the global limit. Event submission counts against the events-ingest limit instead of the mutation limit.

## Environments

The global and mutation limits are tracked separately for production and non-production environments. Your production environment has its own budget; development and staging environments share a second budget with the same numbers. Load testing in staging cannot starve production, and adding more non-production environments does not add capacity.

The events-ingest and analytics limits are account-wide: one budget covers every environment.

## Per-key caps

You can cap an individual API key at a percentage of your account's limits. The cap applies to every limit above: a key capped at 25% gets 25% of the global, mutation, events-ingest, and analytics budgets, rate and burst alike. A cap only lowers a key's share; it never grants budget beyond the account's.

A key under its cap consumes the account budget as usual. A key over its cap is rejected with a `429` and consumes none of the account budget, so a capped key can take at most its share from the keys that share the account. Give a scanner, backfill, or batch-job key a small cap so it cannot starve the keys serving production traffic.

The `429` body names the limit the key exceeded (`mutation`, for example), and the `X-RateLimit-*` headers describe the key's own capped limit rather than the account's.

To set a cap in the dashboard, open the API key's create or edit dialog, turn on **Limit throughput**, and enter a percentage from 1 to 99. Through the API, pass `rate_limit_percent` when you create or update a key:

| Value             | On create                      | On update                      |
| ----------------- | ------------------------------ | ------------------------------ |
| `1` to `99`       | Cap the key at that percentage | Set the cap to that percentage |
| `0`               | Leave the key uncapped         | Remove the cap                 |
| Omitted or `null` | Leave the key uncapped         | Leave the cap unchanged        |

API key responses include `rate_limit_percent`, which is `null` for an uncapped key.

## Response headers

Every response from a rate-limited endpoint carries three headers describing the tightest limit the request was subject to:

| Header                  | Meaning                                                                                               |
| ----------------------- | ----------------------------------------------------------------------------------------------------- |
| `X-RateLimit-Limit`     | The burst size of the limit. `X-RateLimit-Remaining` counts down from this value.                     |
| `X-RateLimit-Remaining` | Requests you can send right now before being rejected.                                                |
| `X-RateLimit-Reset`     | Seconds until the limit refills to `X-RateLimit-Limit`. This is a relative duration, not a timestamp. |

These headers are present on successful responses too, so you can watch `X-RateLimit-Remaining` and slow down before you are rejected. They are exposed to browsers through CORS.

When a request is rejected, the response is a `429 Too Many Requests` with a `Retry-After` header (in seconds) and a body naming the limit you hit:

```json
{
  "bucket": "mutation",
  "error": "Rate limit exceeded"
}
```

The `bucket` value is one of `global`, `mutation`, `events_ingest`, or `analytics`.

## Handling limits

* **Honor `Retry-After`.** When you receive a `429`, wait at least that many seconds before retrying. For retries beyond the first, back off exponentially and add jitter so that many workers do not retry in lockstep.
* **Batch events.** Send events through `POST /event-batch` instead of one request per event. One batch request counts once against the events-ingest limit regardless of how many events it carries.
* **Cache flag checks.** Use the SDKs, which cache flag values locally and refresh them in the background, rather than calling the API on every evaluation. Use the bulk flag-check endpoint when you need many flags at once.
* **Pace analytics reads.** `GET /events` and the feature-usage endpoints scan history and have the lowest limit. Fetch them on a schedule rather than on demand, and page through results with a delay between requests.
* **Watch for fan-out.** Limits are per account, not per worker or per API key. A job that runs on many machines in parallel shares one budget; coordinate or throttle it centrally.
* **Spread out bulk updates.** A nightly job that upserts every company or user at once will exhaust the mutation limit. Spread the work over a longer window, or send trait updates as identify events instead.

> **Info**
>
> Schematic's SDKs handle most of this for you. The server-side SDKs cache flag checks and queue events; the front-end SDKs cache flag values, batch checks, and keep values fresh over a WebSocket connection.

## Need a higher limit?

If your workload needs more than the limits above, [contact support](mailto:support@schematichq.com) and describe the use case. Limits can be raised per account.