# Ошибки и лимиты

Формат ошибки, все коды с HTTP-статусами, лимиты запросов и размеров и какие запросы стоит повторять.

## Формат ошибки {#format}

Любая ошибка приходит одним и тем же JSON:

```json Ответ 409
{
  "error": {
    "code": "sold_out",
    "message": "Билеты этого тарифа закончились",
    "details": {"tariff_id": "0193e0b2-4a7d-7a21-8c44-6e2f9a0b3c71"}
  }
}
```

- `code` не меняется. Пишите логику по нему.
- `message` написан по-русски для человека, и текст может меняться. Его можно показать оператору, но разбирать не стоит.
- `details` есть не всегда. В нём поле, тариф, место или причина: набор ключей зависит от ошибки.

## Коды {#codes}

| Код | HTTP | Когда | Что делать |
|---|---|---|---|
| `invalid_request` | 400 | Неверный JSON, поле или параметр. Причина в `message`, иногда поле в `details` | Исправить запрос. Тот же запрос снова не пройдёт |
| `unauthorized` | 401 | Нет ключа, ключ неверный или отозван, организация приостановлена | Проверить ключ |
| `forbidden` | 403 | Действие доступно только в кабинете: `details.reason: "session_only"` | Сделать это в кабинете |
| `not_found` | 404 | Нет такого объекта или метода. Объект другой организации или режима тоже не найдётся | Проверить id и режим ключа |
| `event_not_on_sale` | 409 | Событие не опубликовано, или продажа тарифа и мест ещё открывается после публикации | Опубликовать событие. После публикации подождать несколько секунд |
| `sold_out` | 409 | Билеты тарифа закончились, `details.tariff_id` | Предложить другой тариф или вернуть деньги |
| `seat_taken` | 409 | Место уже в резерве у другого заказа или продано, `details.seat_id` и `details.seat` | Предложить другое место |
| `reservation_expired` | 409 | Резерв истёк, а свободных билетов уже нет | Вернуть деньги покупателю |
| `idempotency_conflict` | 409 | `Idempotency-Key` занят другим запросом, ещё выполняется или результат неизвестен | См. [идемпотентность](https://budu.events/docs/idempotency/#outcomes) |
| `limit_exceeded` | 422 | В заказе больше 20 билетов или больше, чем разрешает тариф на один заказ | Уменьшить заказ или разбить его |
| `rate_limited` | 429 | Превышен лимит запросов | Подождать `Retry-After` секунд |
| `internal` | 500 | Сбой на нашей стороне | Повторить с тем же `Idempotency-Key` |

Пока сервис перезапускается, прокси может ответить `502`, `503` или `504` без JSON. Повторите такой запрос с тем же `Idempotency-Key`.

## Лимиты {#limits}

| Что | Сколько |
|---|---|
| Запросы с одного ключа | 600 в минуту |
| Тело JSON | 1 МБ |
| Файл мест | 5 МБ, до 50 000 мест |
| Файл гостей | 1 МБ, до 2 000 строк |
| Обложка | 5 МБ, до 25 мегапикселей |
| Билетов в одном заказе | 20 |
| Резерв | от 60 до 1 800 секунд, по умолчанию 900 |
| `Idempotency-Key` | до 255 символов, хранится 24 часа |
| Повторное письмо с билетом | раз в минуту на билет |
| Страница списка | билеты до 200, места до 5 000 |

Лимит запросов считается по календарным минутам. Превышение даёт `429 rate_limited` с заголовком `Retry-After` и `details.retry_after_s`: сколько секунд осталось до новой минуты.

## Какие запросы повторять {#retries}

- `429`: через `Retry-After` секунд.
- `5xx`, таймаут и обрыв связи: с тем же `Idempotency-Key`, с паузой, которая растёт с каждой попыткой (1, 2, 4 секунды…).
- `400`, `401`, `403`, `404`, `422`: не повторять, сначала исправить запрос.
- `409`: зависит от кода. `sold_out`, `seat_taken` и `reservation_expired` окончательные. `event_not_on_sale` сразу после публикации проходит через несколько секунд. Про `idempotency_conflict` см. [идемпотентность](https://budu.events/docs/idempotency/#outcomes).
