Skip to content

Pagination

How lists are paged in the Baiyar API: the page size, the cursor, and how to know there is another page.


Lists use cursor-based pagination: you ask for a page, and the id of the last item you have is the cursor for the next one. Lists are newest first.

GET /v1/payments, GET /v1/payment-links, GET /v1/invoices, GET /v1/webhook-deliveries and GET /v1/events return a page in data, and say how the list goes on in meta.pagination:

{
"data": [
{ "id": "pay_01k7q3m8x2v5t9n4b6c1d0e7fa", "status": "paid", "amount": 1000000, "currency": "IDR" },
{ "id": "pay_01k7q3m8x2v5t9n4b6c1d0e7f9", "status": "pending", "amount": 500000, "currency": "IDR" }
],
"meta": {
"request_id": "req_Hy3k9QzT2LmN8vXa",
"pagination": {
"per_page": 20,
"has_more": true,
"next": "https://api.baiyar.com/v1/payments?limit=20&starting_after=pay_01k7q3m8x2v5t9n4b6c1d0e7f9"
}
}
}
Field Type Meaning
data array The items of this page, newest first
meta.pagination.per_page integer The page size that was used
meta.pagination.has_more boolean Whether another page exists.
meta.pagination.next string or null The address of the next page, with the filters of this request, or null when there is none

There is no total count: ask for pages until has_more is false.

Pass limit to set the page size. It is 20 by default, from 1 to 100.

Fetch meta.pagination.next as it is: it has your filters and the cursor in it. Or pass starting_after, the id of the last item of the page you have, yourself:

GET /v1/payments?limit=50&starting_after=pay_01k7q3m8x2v5t9n4b6c1d0e7f9

Keep the same filters while you page. Stop when has_more is false.

A list with nothing in it answers 200 with "data": [] and "has_more": false.

const headers = { Authorization: `Bearer ${process.env.BAIYAR_KEY}` };
async function* allPayments() {
let url = "https://api.baiyar.com/v1/payments?limit=100";
while (url) {
const { data, meta } = await (await fetch(url, { headers })).json();
yield* data;
// `next` carries the filters of this request and the cursor of its last item
url = meta.pagination.has_more ? meta.pagination.next : null;
}
}
for await (const payment of allPayments()) console.log(payment.id, payment.status);

GET /v1/balance/movements pages with a different cursor, because its entries are ordered by a number and not by an id. meta.pagination.next has the cursor in it, as before; you can pass before yourself too. meta.account says which balance the movements are about:

{
"data": [ … ],
"meta": {
"request_id": "req_Hy3k9QzT2LmN8vXa",
"account": "available",
"pagination": {
"per_page": 20,
"has_more": true,
"next": "https://api.baiyar.com/v1/balance/movements?account=available&before=184"
}
}
}

next is null when there is no more.

GET /v1/event-types and GET /v1/webhook-endpoints return everything in data: there are few of them (up to five endpoints per scope). They have no pagination.

  • Page through a list once and keep what you read, instead of reading it again and again.
  • Use the largest limit that suits you to make fewer requests. See Rate limits.
  • A new item can appear while you page. Because the cursor is an id, you do not see an item twice and do not skip one.
  • Change nothing in the filters between two pages of the same read.