# Rate limits

The actual per-token limits, the per-supplier and per-IP ceilings, and the headers on every response. Sandbox limits are identical to production.

Limits are per rolling 60-second window. Every response carries the three `X-RateLimit-*` headers below — read them and back off before you are throttled. When you exceed a limit you get a `429` with `rate_limited` and a `Retry-After` header. Sandbox limits are identical to production.

## Per-token classes

| Class | Limit | Config key |
|---|---|---|
| reads — every GET, per token | 600 / min | `reads` |
| writes — mutating endpoints not otherwise classed, per token | 120 / min | `writes` |
| sync — PUT …/price, PUT …/stock, per token | 300 / min | `sync` |
| bulk — POST …/inventory, per token | 12 / min | `bulk` |
| simulate — POST /pricing/simulate, per token | 120 / min | `simulate` |
| test — webhook test-fire, fulfilment check, sandbox simulator, per token | 6 / min | `test` |

## Ceilings

| Scope | Limit | Config key |
|---|---|---|
| Per-supplier, across all of a supplier's tokens | 1200 / min | `per_supplier` |
| Unauthenticated requests, per IP | 30 / min | `unauthenticated` |

## Response headers

| Header | Meaning |
|---|---|
| `X-RateLimit-Limit` | The class ceiling for this request. |
| `X-RateLimit-Remaining` | Requests left in the current window. |
| `X-RateLimit-Reset` | Unix seconds when the window resets. |

## When you hit a limit

```json
{
  "type": "https://pay.mmokick.com/developers/errors#rate_limited",
  "title": "Rate limited",
  "status": 429,
  "detail": "Write limit of 120 requests per minute exceeded; back off per Retry-After.",
  "code": "rate_limited",
  "trace_id": "req_9f3b2c61a8d44e0f"
}
```

## Staying under the limits

Batch instead of looping: upload up to 1,000 inventory items in one `POST …/inventory` request rather than sending 1,000 requests. If your integration genuinely needs higher limits, contact support — limits are raisable on request.
