Лимиты и ошибки
HTTP-коды
| Код | Значение |
|---|---|
| 200 | Успех |
| 201 | Объект создан |
| 204 | Успех, тело ответа пустое |
| 400 | Невалидный запрос (нет обязательных полей и т.п.) |
| 401 | API-ключ отсутствует или невалиден |
| 403 | Нет доступа к ресурсу |
| 404 | Ресурс не найден |
| 429 | Превышен лимит запросов |
| 5xx | Ошибка на стороне сервера — повторите запрос позже |
Структура ошибки
{
"error_code": "VALIDATION_ERROR",
"error_message": "contact must include at least one of internal_id, telegram_id, email",
"detail": null
}error_code — UPPER_SNAKE_CASE строка стабильная для программного матчинга на вашей стороне. error_message — человекочитаемое описание (на английском, без локализации).detail — опциональный контекст в JSON, специфичный для ошибки (например {"field": "email"} для VALIDATION_ERROR). Полный список кодов и категории — в backend app.core.errors.ErrorCode (несколько сотен значений; стабильны от релиза к релизу — переименование = breaking).
Rate limiting
Default-лимит — 300 запросов в минуту на IP клиента (≈5 req/s sustained, бёрсты до 300 в окне). Применяется ко всем endpoint'ам без своего @limiter.limit. Для критичных flow (auth, password reset) limits жёстче — HTTP 429 вернётся сразу, заголовки Retry-After и X-RateLimit-Resetподскажут когда повторить.
Long-polling endpoint /api/v1/updates не тарифицируется пока соединение «висит» — учитывается только сам момент открытия запроса, не время ожидания событий.
Storage — Redis (стратегия fixed-window). Лимит шарится между процессами / pods. При недоступности Redis limiter не блокирует — это безопасный fallback, чтобы кратковременная проблема инфры не ронияла API.