Skip to content

Pagination

Three styles co-exist in the API. They're not interchangeable — each endpoint uses one, and the response envelope tells you which.

StyleUsed byWhy
Page-based (?page=N&limit=M)GET /transactions, GET /customers, GET /payment-linksStable, sortable by created_at, suits dashboard-style "page 1 of 7" UIs
Limit/Offset (?limit=M&offset=N)GET /subscriptionsLets background workers walk forward by chunks without holding state
Implicit / unpaginatedGET /customers/{id}/payment-tokens, GET /zapier/sample-paymentsAlways small (saved cards per customer, 3-record samples)

All paginated endpoints clamp limit to a max of 100 and default to 25 (or 20 for /subscriptions).

Page-based

http
GET /api/v1/transactions?page=2&limit=50&status=captured
json
{
  "data": [ /* up to 50 transactions */ ],
  "meta": {
    "current_page": 2,
    "last_page": 14,
    "per_page": 50,
    "total": 692
  }
}

Walk forward by incrementing page until current_page === last_page. Adding from / to date filters narrows the set and reduces last_page accordingly.

Limit/Offset

http
GET /api/v1/subscriptions?status=active&limit=50&offset=100
json
{
  "data": [ /* up to 50 subscriptions */ ],
  "total": 412,
  "limit": 50,
  "offset": 100
}

Walk forward by adding limit to offset until offset + limit >= total. Stable across calls as long as no new subscriptions are inserted during the walk; for nightly exports we recommend pinning a created_at upper bound via a date filter on the underlying resource if available.

Choosing a page size

  • For interactive UIs, default to 25 — keeps response payloads under ~200 KB.
  • For background workers / nightly exports, prefer 100 — minimum round-trips.
  • limit > 100 is clamped to 100; you'll get 100 rows and per_page: 100 in the meta.

Code samples

js
// Node — walk every active subscription
async function walkSubscriptions(merchantKey) {
  let offset = 0, limit = 100
  while (true) {
    const r = await fetch(
      `https://app.geniuscheckout.com/api/v1/subscriptions?status=active&limit=${limit}&offset=${offset}`,
      { headers: { Authorization: `Bearer ${merchantKey}` } },
    )
    const { data, total } = await r.json()
    for (const sub of data) yield sub
    offset += limit
    if (offset >= total) break
  }
}
php
// PHP — walk every transaction this month
$page = 1;
do {
    $r = json_decode(file_get_contents(
        "https://app.geniuscheckout.com/api/v1/transactions?page={$page}&limit=100&from=2026-05-01",
        false,
        stream_context_create(['http' => ['header' => "Authorization: Bearer {$key}"]])
    ), true);
    foreach ($r['data'] as $txn) {
        // …
    }
    $page++;
} while ($page <= $r['meta']['last_page']);

Why three styles?

Different endpoints have different consistency needs. Money-table reads (/transactions, /customers) want stable page numbers for merchant dashboards. Background-job reads (/subscriptions) want offset cursors a worker can walk past while new records appear without skipping rows. We unified on the two styles that match real usage rather than forcing a single style where it doesn't fit. New endpoints lean toward limit/offset.

Next

Released under the proprietary Genius Checkout license.