Pagination
Offset pages with per_page and page, or cursors for large exports.
Every list endpoint returns an items array and a meta object. Two modes share the same
endpoint: offset pagination by default, cursor pagination as soon as you send a cursor. A
numbered page also carries the next_cursor that continues it, so a cursor walk starts with a
plain request. meta tells you which mode answered: current_page in offset mode, prev_cursor
in cursor mode.
https://api.wajub.com/paymentsParameters
per_pageintegeroptionaldefault : 25422. Tax lists default to 50.pageintegeroptionaldefault : 1422.cursorstringoptionalmeta.next_cursor of a previous response, a numbered page included. Sending it switches the endpoint to cursor mode and page is ignored.Filters are per resource, not global. On GET /payments:
statusstringoptionalpending, processing, succeeded, expired, partial, failed, cancelled, refunded, partially_refunded.date_fromdateoptionalcreated_at, as YYYY-MM-DD.date_todateoptionalcreated_at. Must not precede date_from.amount_minnumberoptionalamount_maxnumberoptionalamount_min.searchstringoptionalOffset pagination
The default. Ask for a page number, get a meta that tells you how many there are in total, and
where to go on with a cursor:
https://api.wajub.com/payments?per_page=2&status=succeededcurl "https://api.wajub.com/payments?per_page=2&status=succeeded" \
-H "Authorization: $WAJUB_API_KEY"has_more says whether a page follows this one, and next_cursor is the cursor of the last item
of the page: null on the last page. Both are there so that you can
continue with a cursor. To stay in offset mode, ignore them.
Loop until current_page reaches last_page. Use the largest page size you can, 100, so a
137-item history is two requests rather than six:
async function allPayments(filters = {}) {
const all = [];
let page = 1;
while (true) {
const params = new URLSearchParams({ ...filters, per_page: '100', page: String(page) });
const res = await fetch(`https://api.wajub.com/payments?${params}`, {
headers: { Authorization: process.env.WAJUB_API_KEY },
});
const { items, meta } = await res.json();
all.push(...items);
if (page >= meta.last_page) break;
page += 1;
}
return all;
}Cursor pagination
Offset pages shift when new items arrive while you walk them, and get slower on long histories.
Cursors avoid both: pass cursor and each page is anchored on the last item you saw.
There is no cursor to send on the first request, so start without one. The first page is a
numbered page, and its meta carries next_cursor. Send it back as cursor and the walk goes on
in cursor mode, right after the last item you received, with no item skipped or served twice. It
works from any numbered page, not only the first.
meta changes shape in cursor mode. current_page, last_page and total are gone, replaced
by:
Feed next_cursor back as cursor and stop when has_more is false:
async function* streamPayments() {
// No cursor on the first request: that page already carries next_cursor.
let cursor = null;
do {
const params = new URLSearchParams({ per_page: '100' });
if (cursor) params.set('cursor', cursor);
const res = await fetch(`https://api.wajub.com/payments?${params}`, {
headers: { Authorization: process.env.WAJUB_API_KEY },
});
const { items, meta } = await res.json();
yield* items;
cursor = meta.has_more ? meta.next_cursor : null;
} while (cursor);
}Which endpoints support what
Cursor mode is available wherever the list has a stable sort on created_at. Three lists are
offset only: their meta has no next_cursor and no has_more.
| Endpoint | Offset | Cursor | List key |
|---|---|---|---|
GET /payments | Yes | Yes | items |
GET /transfers | Yes | Yes | items |
GET /refunds | Yes | Yes | items |
GET /customers | Yes | Yes | items |
GET /beneficiaries | Yes | Yes | items |
GET /invoices | Yes | Yes | items |
GET /links | Yes | Yes | items |
GET /accounts | Yes | Yes | items |
GET /disputes | Yes | Yes | items |
GET /webhooks | Yes | Yes | items |
GET /events | Yes | No | events |
GET /tax/codes, /tax/jurisdictions | Yes | No | see Tax |
GET /invoices and GET /links accept sort_by and sort_dir. Cursor mode needs their default
order, newest first: with another sort, cursor is ignored and the numbered page carries neither
next_cursor nor has_more. Page by number there.
Events is the exception
GET /events returns its items under events, not items, and ignores cursor. A client that
reads items on every list will silently see an empty page there.
Prefer webhooks over polling
To react to a status change, subscribe to webhooks instead of paginating in a loop. Pagination is for exports, reconciliation and history screens.