ДокументацияВведение
Коды ошибок

Интеграция

Пагинация

Cursor-based списки

Списки в PayBot листаются курсором, а не номерами страниц. Каждый ответ — это конверт {data: [...], next_cursor: string|null, has_more: bool}: берите next_cursor из ответа и передавайте его в следующий запрос, пока has_more не станет false. Курсор — непрозрачная (opaque) строка, её нельзя парсить или собирать вручную.

Такой подход даёт стабильный обход даже когда данные постоянно добавляются, поэтому в PayBot нет нумерованных страниц и общего счётчика записей — только «Загрузить ещё» или бесконечная прокрутка.

Как устроен курсорный конверт

Любой курсорный endpoint возвращает три поля.

ПолеТипСмысл
dataмассивэлементы текущей страницы
next_cursorstring \nullкурсор для следующего запроса
has_moreboolесть ли ещё страницы
json
Скачать
{
  "data": [
    { "id": "op_7f3c9a12", "amount": 5000, "status": "paid" },
    { "id": "op_6e2b8d01", "amount": 12000, "status": "paid" }
  ],
  "next_cursor": "eyJpZCI6ICJvcF82ZTJiOGQwMSJ9",
  "has_more": true
}

Признак «есть ещё» — именно has_more, а не next_cursor != null. На практике они согласованы, но опирайтесь на has_more. Сортировка во всех списках фиксирована: created_at DESC, id DESC (сначала новые).

Курсорную пагинацию используют, среди прочих: /v2/payments, /v2/events, /v2/payment-links, /me/webhooks/{id}/deliveries, /partner/clients, /partner/commissions, /partner/balance/transactions, /partner/withdrawals, /partner/v1/clients, /partner/v1/payments.

Как обойти все страницы циклом

Запрашивайте страницы в цикле, подставляя next_cursor, пока has_more == true. Передавайте курсор дословно — не декодируйте и не изменяйте его.

python
Скачать
import requests

def fetch_all(url: str, api_key: str, params: dict | None = None) -> list:
    items = []
    cursor = None
    base_params = dict(params or {})
    base_params["limit"] = 100
    while True:
        page_params = dict(base_params)
        if cursor:
            page_params["cursor"] = cursor
        page = requests.get(url, headers={"X-API-Key": api_key}, params=page_params).json()
        items.extend(page["data"])
        if not page["has_more"]:
            break
        cursor = page["next_cursor"]
    return items

payments = fetch_all(
    "https://api.paybot.kz/v2/payments",
    "kp_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    {"status": "paid"},
)
print(len(payments))

Параметр limit — от 1 до 100 (по умолчанию 20 для /v2/payments и /v2/events, 25 для /v2/payment-links). Значение больше 100 сервер не примет.

Почему не offset-пагинация

Смещение (offset/page) ломается на живых данных: пока пользователь листает, в начало списка добавляются новые записи, всё сдвигается — и вы либо видите дубли, либо пропускаете элементы. Курсор указывает на конкретную позицию в фиксированной сортировке, поэтому обход остаётся консистентным независимо от новых записей.

Практические следствия для UI:

  • Не рисуйте пагинатор с номерами страниц и не показывайте «стр. 3 из 12» — общего числа страниц нет.
  • Не показывайте «Найдено N записей» — в курсорном конверте нет total.
  • Стройте интерфейс вокруг кнопки «Загрузить ещё» или бесконечного скролла по has_more.

Часть списков возвращается без курсора, с обычным limit: например /me/payments (≤100), /me/logs (по умолчанию 50, потолок 200), /me/notifications (≤100), /me/team, /me/kaspi/accounts, /me/webhooks. Полная карта endpoint'ов — в справочнике API.

FAQ

Как понять, что страницы закончились?

Смотрите на has_more. Когда он false — вы дошли до конца, next_cursor при этом будет null. Не завязывайтесь на проверку next_cursor != null как на основной сигнал, хотя обычно она совпадает с has_more.

Можно ли перескочить на произвольную страницу?

Нет. Курсор описывает последовательный обход от новых записей к старым; произвольный переход «на страницу 5» не поддерживается по устройству модели. Для навигации используйте фильтры (status, type), чтобы сузить выборку.

Что делать с курсором — декодировать?

Ничего. Курсор — непрозрачная base64-строка, её формат может меняться. Передавайте next_cursor в следующий запрос ровно как получили, без парсинга.

Смежные разделы