Skip to content
Browse documentation

Pagination

Page through lists with opaque keyset cursors, page through reports with limit and offset, and pull large data sets without gaps or duplicates.

Last updated 2 min read

On this page

The API uses two kinds of paging:

KindEndpointsParameters
Cursor (keyset)/offers, /affiliates, /advertisers, /postbacks, /clicks, /conversionslimit, cursor → next_cursor
Offset/reportslimit, offset → next_offset

/deals, /product-feeds and /offers/{id}/creatives return everything in one response (next_cursor is always null). A click lookup by gclid / gbraid / wbraid returns up to limit rows (default 100, max 1,000) without a cursor.

Cursor pagination#

A list response contains a page of results and an opaque cursor:

JSON
{
  "data": [ { "id": 12, "…": "…" } ],
  "next_cursor": "eyJrIjoib2ZmZXJzIiwidCI6IjIwMjYtMTAtMDFUMDk6MzA6MDAuMDAwMDAwWiIsImkiOiIxMiJ9"
}
  1. Request the first page with your filters and a limit.
  2. If next_cursor is not null, request the same URL with &cursor=<next_cursor>.
  3. Stop when next_cursor is null.

Rules:

  • Keep the filters identical between pages. The cursor stores only a position.
  • Results are newest first — by creation time, or by click time (clicks) and conversion time (conversions).
  • The cursor is a keyset position, so rows created while you page never shift or repeat results.
  • A cursor from one list is refused by another: 400 invalid_cursor. Treat cursors as opaque strings; their format may change.

Page sizes#

EndpointDefault limitMaximum
/offers, /affiliates, /advertisers, /postbacks50200
/clicks1001,000
/conversions100500

Time-ranged lists#

/clicks and /conversions page within a date range (from/to/preset). Defaults: today for clicks (at most 93 days per request) and the last 7 days for conversions (at most 400 days). For longer click periods, use reports, or request one range at a time.

Offset pagination (reports)#

GET /reports returns aggregated rows with limit (default 1,000, max 10,000) and offset:

Shell
curl "https://app.trackcrown.com/api/v1/reports?preset=last30&group_by=date,offer&metrics=clicks,conversions,revenue&limit=10000&offset=0" \
  -H "Authorization: Bearer $ATK"

The response carries total_rows and next_offset — the offset of the next page, or null on the last page (and always null for a report without group_by). Totals in totals are computed by the database for the whole report, not per page.

For a full export use format=csv instead: it streams every row (up to 200,000) plus a totals line in one request.

Example: nightly conversion sync (bash)#

Shell
#!/usr/bin/env bash
# Pull yesterday's approved conversions page by page (needs curl and jq).
set -euo pipefail
ATK="atk_…"
URL="https://app.trackcrown.com/api/v1/conversions?preset=yesterday&status=approved&limit=500"
CURSOR=""
while :; do
  PAGE=$(curl -sf "$URL${CURSOR:+&cursor=$CURSOR}" -H "Authorization: Bearer $ATK")
  echo "$PAGE" | jq -c '.data[]'
  CURSOR=$(echo "$PAGE" | jq -r '.next_cursor // empty')
  [ -z "$CURSOR" ] && break
done

Example: all clicks of a day (Python)#

PYTHON
import os, requests

BASE = "https://app.trackcrown.com/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['ATK']}"}

def clicks(day: str):
    params = {"from": day, "to": day, "limit": 1000}
    while True:
        r = requests.get(f"{BASE}/clicks", params=params, headers=HEADERS, timeout=60)
        r.raise_for_status()
        page = r.json()
        yield from page["data"]
        if not page["next_cursor"]:
            return
        params["cursor"] = page["next_cursor"]

for c in clicks("2026-10-11"):
    print(c["click_id"], c["gclid"], c["ext_campaign_id"])

Example: a large report (Node.js)#

JavaScript
async function report(query) {
  const rows = [];
  let offset = 0;
  while (offset !== null) {
    const res = await fetch(`https://app.trackcrown.com/api/v1/reports?${query}&limit=10000&offset=${offset}`, {
      headers: { Authorization: `Bearer ${process.env.ATK}` },
    });
    const page = await res.json();
    rows.push(...page.data);
    offset = page.next_offset;
  }
  return rows;
}

const rows = await report("preset=last_month&group_by=date,affiliate&metrics=clicks,conversions,payout");

See also: Rate limits & errors · API reference.

Something unclear or missing in this article?Tell us