# Limits and errors

> Rate limits, budget protection, X-RateLimit headers and the error catalog with fix lines. Every error says what to do.

## Rate limits

| Surface | Limit | Bucket |
|---|---|---|
| Business API `api/v1` | 60 per second, 10,000 per day | per key |
| Platform API `platform/v1` | 30 per second, 50,000 per day | per key |
| Keyless documentation | 10 per minute | per IP |

Buckets follow the key you present: another partner's traffic never slows you down.

Your remaining quota arrives in every response's `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` headers. Branch on the HTTP status code: on a **429**, wait and retry. A `Retry-After` header can also appear on successful responses and is not a slow-down signal on its own.

## Platform budgets

Write endpoints pass two extra protections:

- At most **1,000** units of magnitude per write: anything larger answers `422`, split it.
- **100,000** units per day in total: breach answers `429 budget_exceeded` and resets at midnight UTC.

This is your insurance too: code stuck in a loop cannot mint unlimited balance. If a limit is tight for your case, ask [biz@sadakio.com](mailto:biz@sadakio.com) to raise it.

## Error shape

Every error comes in the same envelope and the `fix` field says what to DO:

```json
{"error": {"code": "not_found",
           "message": "No such guest in this business.",
           "fix": "Use a guest id returned by POST .../guests."}}
```

## Error catalog

| Code | HTTP | Meaning | What to do |
|---|---|---|---|
| `unauthorized` | 401 | Key missing, unknown, revoked or suspended | Check the key, issue a new one if needed |
| `insufficient_scope` | 403 | The key lacks the needed scope | Issue a key with the `write` scope |
| `not_found` | 404 | No such record in YOUR tenancy | Use ids returned by your own list calls |
| `validation_failed` | 422 | The body failed validation | Correct the field named in `fix` |
| `budget_exceeded` | 429 | The daily write budget is spent | Wait for the midnight reset or ask for a raise |
| `rate_limited` | 429 | Rate limit | Wait `Retry-After` and retry |

Important: access to a foreign resource also answers `not_found`, not `forbidden`. The API is not an existence oracle.
