# Лимиты

**Это ваш сервер.** Лимиты защищают его от того, чтобы интеграция положила школу,
а не вас — от того, чтобы пользоваться своими данными. Отсюда и значения,
и возможность их менять.

## Значения по умолчанию

| Класс операций | Лимит | Настраивается |
|---|---|---|
| Чтение | 600 запросов в минуту на ключ | да |
| Запись | 120 запросов в минуту на ключ | да |
| Массовые операции | 10 одновременных задач | да |
| Экспорт | 20 задач в час | да |
| Отправка сообщений | по лимиту транспорта | нет |

Считается **на ключ**, а не на школу: одна интеграция, упёршаяся в лимит,
не мешает другой. Это ещё один довод заводить [отдельный ключ на каждую
интеграцию](/api/keys/).

Последняя строка не настраивается, потому что лимит не наш: почтовый провайдер
и Telegram считают отправку сами, и обойти их мы не можем, даже если очень
захотим.

Для сравнения: у GetCourse экспорт — **100 запросов за 2 часа на весь аккаунт**,
без возможности изменить, причём каждый опрос «готов ли файл» тоже расходует
квоту. Разница здесь не в цифре, а в том, что у нас это параметр вашей установки.

## Заголовки в каждом ответе

```
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 587
X-RateLimit-Reset: 1785790800
```

| Заголовок | Что значит |
|---|---|
| `X-RateLimit-Limit` | Лимит для этого класса операций |
| `X-RateLimit-Remaining` | Сколько осталось в текущем окне |
| `X-RateLimit-Reset` | Когда окно обнулится, время в секундах эпохи |

Они приходят **в каждом ответе**, а не только в отказе. Это позволяет притормозить
до того, как упрётесь, а не после: если `Remaining` подошёл к нулю за десять секунд
до `Reset`, разумнее подождать эти десять секунд, чем получить `429` и ждать
столько же.

## Что делать при 429

```
HTTP/1.1 429 Too Many Requests
Retry-After: 12
```

**Уважайте `Retry-After`.** Он в секундах, и это не рекомендация: игнорирование
и повтор в цикле — самый быстрый способ получить более жёсткий лимит и подозрение
на неисправную интеграцию.

Правильный обработчик:

```
1. Получили 429 — прочитать Retry-After.
2. Заголовка нет — подождать с ростом: 1, 2, 4, 8 секунд, не больше минуты.
3. Повторить ТОТ ЖЕ запрос. При обходе списка — с тем же курсором,
   иначе обход начнётся сначала и не закончится никогда.
4. Операция записи — повторять с тем же ключом идемпотентности,
   см. [Идемпотентность](/api/idempotency/).
```

Пункт 4 — то, ради чего ключ идемпотентности вообще существует: после `429`
вы точно знаете, что операция не выполнилась, но после **таймаута** — не знаете,
и повтор без ключа создаёт второй заказ.

## Как не упираться

**Не опрашивайте по расписанию — подпишитесь.** Опрос «не появилось ли чего»
каждую минуту тратит лимит на ответы «ничего». Для этого есть
[вебхуки](/api/webhooks/), и они же дают реакцию в секунду вместо минуты.

**Берите список, а не объекты по одному.** Один запрос со `limit=100` вместо
ста запросов по одному — та же работа за один процент лимита.

**Разворачивайте связи одним запросом.** `expand` вместо N+1 запросов —
см. [Пагинация](/api/pagination/).

**Просите только нужные поля.** `fields` не экономит лимит, но экономит время
ответа, а значит вы успеваете больше в то же окно.

**Экспорт — для объёмов.** Выгрузить сто тысяч человек обходом списка можно,
но это тысяча запросов; задача экспорта делает то же одним вызовом.

## Как поменять

Все лимиты, кроме отправки сообщений, — **параметры установки** и меняются
в настройках школы.

Прежде чем поднимать, стоит посмотреть на причину. Лимит в 600 запросов в минуту
на чтение — это десять запросов в секунду непрерывно; интеграция, которой этого
не хватает, обычно опрашивает то, на что можно подписаться. Поднятый лимит
в этом случае просто переносит проблему на базу.

Случай, когда поднимать правильно: разовая миграция. Завели ключ с высоким
лимитом и сроком действия на неделю, перенесли данные, ключ истёк сам.

## Чего здесь ещё нет

**Проверенных на живой нагрузке цифр.** Значения выше — проектные. Они выбраны
из соображения «интеграция не должна класть школу», а не измерены на установке
с реальным трафиком: публичного API пока нет, измерять нечего.
