Пагинация
Списки возвращаются страницами. Переход к следующей странице выполняется по курсору, без номеров страниц и смещений.
#Ответ со списком
Все списки имеют одинаковую оболочку:
{
"object": "list",
"data": [ { "object": "lead", "id": "5a7c3e92-1f4b-4d68-9c2e-7b0a6d4f8e31" } ],
"has_more": true,
"next_cursor": "v1.k1.Qm9hcmRfMWZ4.c3RyaWN0bHlfb3BhcXVl.dGFnX2V4YW1wbGU.bWFjX2V4YW1wbGVfdmFsdWU"
}| Поле | Описание |
|---|---|
object | Всегда list. |
data | Объекты текущей страницы. |
has_more | true, если после этой страницы есть еще данные. |
next_cursor | Курсор следующей страницы. null, если страниц больше нет. |
meta | Дополнительные сведения. Есть у переписки и ленты событий. |
Общего числа записей (total) и смещения (offset) нет.
#Параметры
limit: сколько объектов вернуть. По умолчанию 50, от 1 до 100. У ленты событий до 200.cursor: значениеnext_cursorиз предыдущего ответа.
Порядок записей фиксирован: от старых к новым по времени создания, при равном времени по id. Параметра сортировки нет. Новые записи попадают в конец списка.
#Как листать
- Отправьте первый запрос с нужными фильтрами.
- Обработайте
data. - Если
has_moreравенtrue, повторите запрос с теми же фильтрами иcursor=<next_cursor>. - Остановитесь, когда
has_moreравенfalse.
async function listAll(path, params = {}) {
const items = [];
let cursor = null;
do {
const url = new URL(`https://api.melioralab.io/v1${path}`);
for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v);
if (cursor) url.searchParams.set('cursor', cursor);
const res = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.MELIORA_API_KEY}` },
});
if (!res.ok) throw new Error(`Meliora API: ${res.status} ${res.headers.get('Request-Id')}`);
const page = await res.json();
items.push(...page.data);
cursor = page.has_more ? page.next_cursor : null;
} while (cursor);
return items;
}
const leads = await listAll('/leads', { campaign_id: '4d8b2f73-6e1a-4c95-b7d0-8a3f5e2c9b14', limit: 100 });import os
import requests
def list_all(path, params=None):
session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['MELIORA_API_KEY']}"
params = dict(params or {})
items = []
while True:
resp = session.get(f"https://api.melioralab.io/v1{path}", params=params, timeout=15)
resp.raise_for_status()
page = resp.json()
items.extend(page["data"])
if not page["has_more"]:
return items
params["cursor"] = page["next_cursor"]
leads = list_all("/leads", {"campaign_id": "4d8b2f73-6e1a-4c95-b7d0-8a3f5e2c9b14", "limit": 100})#Правила курсора
- Курсор непрозрачен. Не разбирайте и не собирайте его сами, только передавайте обратно.
- Курсор привязан к запросу. Фильтры следующего запроса должны совпадать с первым. Если фильтры другие, ключ получил другие права или охват, ответ будет 400
cursor_mismatch. limitможно менять между страницами, это не ошибка.- Курсор списка живет 15 минут. Просроченный курсор дает 410
cursor_expired: начните обход заново с первой страницы. Курсор ленты событий живет 30 суток, см. Опрос событий. - Страница может быть короче
limit. Ответ не превышает 1 МиБ, поэтому длинная страница сокращается, а невыданные записи придут на следующей. Ориентируйтесь наhas_more, а не на число записей.
#Списки не снимок
Список читается постранично, и между запросами данные могут меняться: лиды добавляются, статусы обновляются. Записи не пропадают и не повторяются из-за смещений, но статус лида на первой странице мог измениться к моменту чтения последней. Если нужно согласованное состояние, сочетайте обход списка с лентой событий: события после якоря покажут, что изменилось.