> ## Documentation Index
> Fetch the complete documentation index at: https://docs.joinmarkt.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Rate Limiting

> Per-client request windows, 429 responses, and endpoint budgets.

Public and checkout routes use per-client sliding windows via `rateLimit(identifier, { maxRequests, windowMs })` in `lib/rateLimit.ts`. Identity comes from `getClientIdentifier(req)` (IP / forwarded headers).

## Integrator budgets

| Endpoint                         | Max              | Window       |
| -------------------------------- | ---------------- | ------------ |
| `POST /api/embed/checkout`       | 30               | 60s          |
| `POST /api/checkout/start`       | 15               | 60s          |
| `POST /api/checkout/client-gate` | 30               | 60s          |
| `POST /api/orders/create`        | 10               | 60s          |
| `POST /api/coupons/validate`     | 20               | route window |
| `POST /api/track`                | 120              | 60s          |
| `GET /api/feedback`              | 60               | 60s          |
| `POST /api/feedback/create`      | 5                | 60s          |
| Payment create routes            | typically 60–180 | per route    |
| Auth login                       | 20               | route window |
| Auth register / forgot           | 3                | route window |

Public catalog GETs (`…/storefront`, `…/products`, `GET /api/store/{subdomain}`) currently have **no** route-level rate limit — still cache-friendly; do not hammer them from scrapers.

## 429 shape

Embed example:

```json theme={null}
{
  "error": "Too many requests. Please try again later."
}
```

Some routes also emit `Retry-After` / `X-RateLimit-*` headers via `rate-limit-guard` helpers.

## Guidance

* Debounce buy buttons.
* Call `Markt.init()` once per page.
* Cache catalog responses client-side for short TTL.
* Payment verify polling should back off — prefer webhooks for settlement.
