Pagination
Cursor pagination on every list endpoint: the envelope, limits and a copy-paste iteration loop.
Updated August 13, 2026
Every list endpoint pages with an opaque cursor. Cursors are stable under inserts and deletes: unlike page numbers, they never skip or duplicate rows while you iterate.
The envelope
{
"data": [ { "id": "lnk_…" }, { "id": "lnk_…" } ],
"pagination": {
"next_cursor": "eyJpZCI6Imxua19hYmMiLCJjcmVhdGVkX2F0IjoiMjAyNi0uLi4ifQ",
"has_more": true
}
}
limit— page size, 1–100, default 50.cursor— thenext_cursorof the previous page, passed verbatim.next_cursorisnullon the last page.
Treat the cursor as an opaque string: its format may change, only round-tripping
it is guaranteed. A malformed cursor answers 400 invalid_request rather than
silently restarting from the first page.
Iterating
curl -s "$LINKPROFIT_API_BASE/links?limit=100" \
-H "Authorization: Bearer $LINKPROFIT_API_KEY"
# then with the next_cursor value from the response:
curl -s "$LINKPROFIT_API_BASE/links?limit=100&cursor=CURSOR_FROM_PREVIOUS_PAGE" \
-H "Authorization: Bearer $LINKPROFIT_API_KEY"
async function* allLinks(base, key) {
let cursor = null;
do {
const url = new URL(`${base}/links`);
url.searchParams.set("limit", "100");
if (cursor) url.searchParams.set("cursor", cursor);
const response = await fetch(url, {
headers: { Authorization: `Bearer ${key}` },
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const page = await response.json();
yield* page.data;
cursor = page.pagination.next_cursor;
} while (cursor);
}
Where cursors apply
Cursor pagination is available on /links, /events, /partner/clients and
/webhooks/{id}/deliveries. Short lists — domains, plans, webhooks — return in
one page with has_more: false; payments and payouts are filtered by
period and capped at 100 rows per request.
Ordering is newest-first everywhere except /partner/clients, which pages in
stable id order.