Пагинация, фильтры, сортировка
Всё, что относится к получению списков. Синтаксис одинаковый у всех ресурсов: выучив его на пользователях, вы знаете его для заказов и вебинаров.
Курсор, а не смещение #
curl -X GET 'https://school.example.com/api/v1/users?limit=100' \
-H 'Authorization: Bearer lms_live_a8f3c1e9d2b74a5f8e0c1d3b5a7f9e2c' \
-H 'Accept: application/json'
Ответ:
{
"data": [ { "id": "usr_01HQZX3M8K4N2P", "email": "student@example.com" } ],
"has_more": true,
"next_cursor": "eyJpZCI6InVzcl8wMUhRWlgzTThLNE4yUCJ9"
}
Следующая страница — тот же запрос с курсором:
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 стоит в пятьсот раз дороже первой.
Неверно. Пока вы обходите список, в него добавляются записи. Со смещением объекты дублируются или теряются: вставка сдвигает всё, что после неё, и один объект показывается на двух страницах подряд, а другой не показывается вовсе. Ошибка тихая — выгрузка выглядит успешной, просто в ней не тот набор людей.
Курсор от этого свободен: он запоминает позицию, а не количество пропущенного.
Курсор непрозрачен. Это закодированная позиция; разбирать его на своей стороне не нужно и не следует — устройство может измениться. Хранить между запусками можно: именно так делают докачку прерванного обхода.
Фильтры #
Единый синтаксис: поле[оператор]=значение. Без оператора подразумевается
равенство.
# Оплаченные заказы с июля, дороже 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 |
Пусто или не пусто |
Ещё примеры:
# Ученики с тегом 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'
# Ответы, ждущие проверки у конкретного куратора
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'
Фильтр по сегменту #
Самое полезное, что здесь есть.
curl -X GET 'https://school.example.com/api/v1/users?segment=seg_01HQZX8D9JTS4B' \
-H 'Authorization: Bearer lms_live_a8f3c1e9d2b74a5f8e0c1d3b5a7f9e2c' \
-H 'Accept: application/json'
Сегмент собирается в интерфейсе визуальным конструктором — двадцать пять типов условий, «и», «или», отрицание, — и сразу доступен как фильтр API.
Что это меняет: логику отбора не надо повторять в своём коде. «Купил курс А и не открывал уроки две недели» описывается один раз в школе, а не второй раз у вас — а значит две реализации не разойдутся через месяц, когда условие уточнят в интерфейсе и забудут уточнить в интеграции.
Про то, как сегменты устроены со стороны школы, — Ученики.
Сортировка #
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 запросов: вместо «получить заказ, потом пользователя, потом каждый тариф» — один запрос.
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'
Ограничения: глубина не больше двух уровней, максимум пять путей за раз. Не из вредности — развернуть всё вглубь значит выгрузить половину базы одним запросом и получить таймаут вместо ответа.
Выбор полей #
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с новым запросом вместо того же. Обход начнётся сначала, и вы никогда не дойдёте до конца. Подробнее — Лимиты.
Чего здесь ещё нет #
Списка полей, по которым можно фильтровать у каждого ресурса. Синтаксис и операторы решены и не изменятся, а какие поля есть у заказа — это то, что выводится из валидаторов, а их пока нет.