# Пагинация, фильтры, сортировка

Всё, что относится к получению списков. Синтаксис **одинаковый у всех ресурсов**:
выучив его на пользователях, вы знаете его для заказов и вебинаров.

## Курсор, а не смещение

```bash
curl -X GET 'https://school.example.com/api/v1/users?limit=100' \
  -H 'Authorization: Bearer lms_live_a8f3c1e9d2b74a5f8e0c1d3b5a7f9e2c' \
  -H 'Accept: application/json'
```

Ответ:

```json
{
  "data": [ { "id": "usr_01HQZX3M8K4N2P", "email": "student@example.com" } ],
  "has_more": true,
  "next_cursor": "eyJpZCI6InVzcl8wMUhRWlgzTThLNE4yUCJ9"
}
```

Следующая страница — тот же запрос с курсором:

```bash
curl -X GET 'https://school.example.com/api/v1/users?limit=100&cursor=eyJpZCI6InVzcl8wMUhRWlgzTThLNE4yUCJ9' \
  -H 'Authorization: Bearer lms_live_a8f3c1e9d2b74a5f8e0c1d3b5a7f9e2c' \
  -H 'Accept: application/json'
```

Обходить, пока `has_more` равно `true`.

`limit` — от 1 до 1000, по умолчанию 50.

### Почему не `offset`

Две причины, и вторая хуже первой.

**Дорого.** Смещение на большой таблице заставляет базу пройти все пропускаемые
строки. Страница 500 стоит в пятьсот раз дороже первой.

**Неверно.** Пока вы обходите список, в него добавляются записи. Со смещением
объекты **дублируются или теряются**: вставка сдвигает всё, что после неё, и один
объект показывается на двух страницах подряд, а другой не показывается вовсе.
Ошибка тихая — выгрузка выглядит успешной, просто в ней не тот набор людей.

Курсор от этого свободен: он запоминает позицию, а не количество пропущенного.

**Курсор непрозрачен.** Это закодированная позиция; разбирать его на своей стороне
не нужно и не следует — устройство может измениться. Хранить между запусками можно:
именно так делают докачку прерванного обхода.

## Фильтры

Единый синтаксис: `поле[оператор]=значение`. Без оператора подразумевается
равенство.

```bash
# Оплаченные заказы с июля, дороже 5000 рублей
curl -X GET 'https://school.example.com/api/v1/orders?status=paid&paid_at%5Bgte%5D=2026-07-01T00:00:00%2B03:00&amount%5Bgt%5D=500000' \
  -H 'Authorization: Bearer lms_live_a8f3c1e9d2b74a5f8e0c1d3b5a7f9e2c' \
  -H 'Accept: application/json'
```

Обратите внимание на кодирование: квадратные скобки и `+` в дате должны быть
закодированы (`%5B`, `%5D`, `%2B`). Незакодированный `+` в строке запроса читается
как пробел — и дата молча становится другой.

Операторы:

| Оператор | Что значит |
|---|---|
| `eq` | Равно. Подразумевается, если оператор не указан |
| `ne` | Не равно |
| `gt`, `gte` | Больше, больше или равно |
| `lt`, `lte` | Меньше, меньше или равно |
| `in`, `nin` | Входит в список, не входит |
| `contains` | Содержит подстроку |
| `starts_with` | Начинается с |
| `is_null` | Пусто или не пусто |

Ещё примеры:

```bash
# Ученики с тегом vip, заведённые до августа
curl -X GET 'https://school.example.com/api/v1/users?tag=vip&created_at%5Blt%5D=2026-08-01T00:00:00%2B03:00' \
  -H 'Authorization: Bearer lms_live_a8f3c1e9d2b74a5f8e0c1d3b5a7f9e2c' \
  -H 'Accept: application/json'
```

```bash
# Ответы, ждущие проверки у конкретного куратора
curl -X GET 'https://school.example.com/api/v1/submissions?status=pending&curator_id=usr_01HQZX3M8K4N2P' \
  -H 'Authorization: Bearer lms_live_a8f3c1e9d2b74a5f8e0c1d3b5a7f9e2c' \
  -H 'Accept: application/json'
```

## Фильтр по сегменту

Самое полезное, что здесь есть.

```bash
curl -X GET 'https://school.example.com/api/v1/users?segment=seg_01HQZX8D9JTS4B' \
  -H 'Authorization: Bearer lms_live_a8f3c1e9d2b74a5f8e0c1d3b5a7f9e2c' \
  -H 'Accept: application/json'
```

Сегмент собирается в интерфейсе визуальным конструктором — двадцать пять типов
условий, «и», «или», отрицание, — и **сразу доступен как фильтр API**.

Что это меняет: логику отбора не надо повторять в своём коде. «Купил курс А
и не открывал уроки две недели» описывается один раз в школе, а не второй раз
у вас — а значит две реализации не разойдутся через месяц, когда условие уточнят
в интерфейсе и забудут уточнить в интеграции.

Про то, как сегменты устроены со стороны школы, — [Ученики](/guide/students/).

## Сортировка

```bash
curl -X GET 'https://school.example.com/api/v1/orders?sort=-created_at,amount' \
  -H 'Authorization: Bearer lms_live_a8f3c1e9d2b74a5f8e0c1d3b5a7f9e2c' \
  -H 'Accept: application/json'
```

Поля через запятую, минус — по убыванию. Здесь: сначала новые, при равенстве —
по возрастанию суммы.

## Разворачивание связей

Избавляет от N+1 запросов: вместо «получить заказ, потом пользователя, потом каждый
тариф» — один запрос.

```bash
curl -X GET 'https://school.example.com/api/v1/orders/ord_01HQZX68HKYJ1X?expand=user,items.offer,payments' \
  -H 'Authorization: Bearer lms_live_a8f3c1e9d2b74a5f8e0c1d3b5a7f9e2c' \
  -H 'Accept: application/json'
```

Ограничения: **глубина не больше двух уровней, максимум пять путей за раз**.
Не из вредности — развернуть всё вглубь значит выгрузить половину базы одним
запросом и получить таймаут вместо ответа.

## Выбор полей

```bash
curl -X GET 'https://school.example.com/api/v1/users?fields=id,email,created_at' \
  -H 'Authorization: Bearer lms_live_a8f3c1e9d2b74a5f8e0c1d3b5a7f9e2c' \
  -H 'Accept: application/json'
```

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

## Как обойти большой список правильно

```
1. Запросить первую страницу с limit, который вы реально осилите обработать.
2. Обработать data.
3. has_more равно false — закончить.
4. Иначе подставить next_cursor и повторить.
5. Получили 429 — подождать столько, сколько сказано в Retry-After, и повторить
   ТОТ ЖЕ запрос с тем же курсором.
```

Три ошибки, которые делают чаще всего:

- **`limit=1000` «чтобы быстрее».** Быстрее не станет: упрётесь в лимит частоты
  или в свою же обработку. Начните с 100.
- **Обход в несколько потоков по одному курсору.** Курсор последовательный;
  параллельный обход одного списка не ускоряет, а путает.
- **Повтор после `429` с новым запросом вместо того же.** Обход начнётся сначала,
  и вы никогда не дойдёте до конца. Подробнее — [Лимиты](/api/limits/).

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

**Списка полей, по которым можно фильтровать у каждого ресурса.** Синтаксис
и операторы решены и не изменятся, а какие поля есть у заказа — это то, что
выводится из валидаторов, а их пока нет.
