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

# Rate limits

> Per-endpoint limits on sensitive routes and how to handle 429 responses.

Sensitive routes apply conservative per-endpoint rate limits to protect against abuse. The limits use a sliding-window counter backed by Valkey. When the limit is exceeded the API returns `429 Too Many Requests`.

## Rate-limited endpoints

| Route                                                                          | Limit       | Window | Key                                   |
| ------------------------------------------------------------------------------ | ----------- | ------ | ------------------------------------- |
| `POST /api/v1/hub/{hub_id}/checkout`                                           | 10 requests | 60 s   | per IP (per contact if authenticated) |
| `GET /api/v1/hubs/{hub_id}/search/community` (community search)                | 30 requests | 60 s   | per contact                           |
| `POST /api/v1/conversations/{conversation_id}/messages` (DM send)              | 30 requests | 60 s   | per contact                           |
| `POST /api/v1/comments` (comment create)                                       | 30 requests | 60 s   | per contact                           |
| `POST /api/v1/hubs/{hub_id}/spaces/{space_id}/discussions` (discussion create) | 20 requests | 60 s   | per contact                           |
| `POST /api/v1/discussions/{discussion_id}/reactions` (reaction add)            | 60 requests | 60 s   | per contact                           |

## 429 response

When the limit is exceeded the response body follows the standard JSON:API error envelope:

```json theme={null}
{
  "errors": [
    {
      "status": "429",
      "code": "rate_limited",
      "title": "Too Many Requests",
      "detail": "Rate limit exceeded. Retry after 42 seconds.",
      "meta": {
        "request_id": "01J..."
      }
    }
  ]
}
```

The `Retry-After` response header contains the number of seconds until the window resets. Read this header to back off correctly instead of polling.

## Fail-open behaviour

Rate limiting is DoS mitigation, not authorization. If the Valkey backing store is temporarily unavailable the API **fails open** — requests pass through rather than returning `500`. A structured log event (`valkey.connection_error`) is emitted for alerting. A Valkey misconfiguration (bad credentials) is treated as a hard error and is not swallowed.

## Handling 429 in your client

### mio CLI (automatic)

The `mio` CLI handles 429 automatically from **v0.4.4**. When a request is
rate-limited the CLI reads the `Retry-After` header and retries up to **2 times**,
waiting the indicated number of seconds between attempts (capped at 60 s). If
all retries are exhausted the command exits with code **6** (`ExitRateLimited`)
and a message that includes the suggested wait time.

No configuration is needed. If you are scripting `mio` commands and branching
on the exit code, `exit 6` now means "rate limit not recovered after retries"
rather than "first 429 received".

### Direct API callers (curl / SDK)

```bash theme={null}
# Check Retry-After before retrying
retry_after=$(curl -s -o /dev/null -D - \
  -X POST "$MIO_BASE_URL/api/v1/hub/$HUB_ID/checkout" \
  -H "Authorization: Bearer $TOKEN" \
  -d '...' | grep -i "retry-after" | awk '{print $2}')

sleep "$retry_after"
```

Do not immediately retry on 429 without honouring `Retry-After`. Back-to-back retries consume the same window and will be rate-limited again.
