Skip to content
LinkProfit

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 — the next_cursor of the previous page, passed verbatim.
  • next_cursor is null on 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.