Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.interchange.io/llms.txt

Use this file to discover all available pages before exploring further.

The v2 API uses three pagination styles, each fitting the underlying data model. Knowing which style applies to which endpoint lets you build a uniform client that handles all three.
PatternQuery paramsUsed by
Offsettake, skipMost list endpoints (audit logs, reporting, creatives, test cohorts, …)
Custom offsetgroupOffset, groupLimit, productOffset, productsPerGroupDiscovery (POST /discovery/.../discover-products)
Cursorlimit, starting_afterStorefront billing endpoints proxied to Stripe

Offset pagination (take / skip)

The default for v2 list endpoints. take is the page size; skip is the offset from the start.

Request

GET /api/v2/buyer/audit-logs?take=50&skip=100
Authorization: Bearer <SCOPE3_API_KEY>
ParamTypeDefaultNotes
takenumbervariesPage size. Most endpoints cap at 100 or 200
skipnumber0Records to skip

Response

The response includes a meta.pagination block alongside data:
{
  "data": {
    "logs": [/* ...50 items... */],
    "total": 1342
  },
  "error": null,
  "meta": {
    "pagination": {
      "skip": 100,
      "take": 50,
      "total": 1342,
      "hasMore": true
    }
  }
}
FieldTypeMeaning
skipnumberEchoed offset
takenumberEchoed page size
totalnumberTotal matching rows across all pages
hasMorebooleantrue if skip + returned < total

Example: paging through audit logs

async function* listAllAuditLogs() {
  const take = 100;
  let skip = 0;

  while (true) {
    const res = await fetch(
      `https://api.interchange.io/api/v2/buyer/audit-logs?take=${take}&skip=${skip}`,
      { headers: { Authorization: `Bearer ${process.env.SCOPE3_API_KEY}` } },
    );
    const { data, meta } = await res.json();
    yield* data.logs;
    if (!meta.pagination.hasMore) return;
    skip += take;
  }
}

Custom offset (discovery)

Discovery returns nested results — product groups, each containing several products — so it exposes two independent offsets. This lets you page through groups without re-fetching products you’ve already seen, and vice versa.

Request

POST /api/v2/buyer/discovery/{discoveryId}/discover-products
Content-Type: application/json

{
  "advertiserId": 123,
  "groupLimit": 5,
  "groupOffset": 0,
  "productsPerGroup": 10,
  "productOffset": 0
}
ParamTypeMeaning
groupLimitnumberHow many product groups to return
groupOffsetnumberSkip this many product groups
productsPerGroupnumberProducts to include within each returned group
productOffsetnumberSkip this many products within each group

Response

{
  "data": {
    "discoveryId": "disc_01HX…",
    "productGroups": [/* ... */],
    "totalGroups": 23,
    "hasMoreGroups": true,
    "summary": { "totalProducts": 412 }
  },
  "error": null
}
hasMoreGroups tells you whether to advance groupOffset for another page of groups. summary.totalProducts is the full denormalized total across all groups.

Cursor pagination (starting_after)

Used by Stripe-Connect billing endpoints, which proxy directly to Stripe and inherit Stripe’s cursor convention.

Endpoints

  • GET /storefront/billing/transactions
  • GET /storefront/billing/payouts

Request

GET /api/v2/storefront/billing/transactions?limit=25&starting_after=txn_1Nq…
ParamTypeDefaultMeaning
limitnumber25Page size, max 100
starting_afterstringObject ID returned in a previous page’s meta.cursor

Response

{
  "data": [/* ...transactions... */],
  "meta": {
    "count": 25,
    "limit": 25,
    "hasMore": true,
    "cursor": "txn_1NqAbc123"
  }
}
meta.cursor is the value to pass as starting_after on the next request. When hasMore is false, you’ve reached the end.
async function* listAllTransactions() {
  let cursor: string | undefined;

  while (true) {
    const url = new URL(
      "https://api.interchange.io/api/v2/storefront/billing/transactions",
    );
    url.searchParams.set("limit", "100");
    if (cursor) url.searchParams.set("starting_after", cursor);

    const res = await fetch(url, {
      headers: { Authorization: `Bearer ${process.env.SCOPE3_API_KEY}` },
    });
    const body = await res.json();
    yield* body.data;

    if (!body.meta.hasMore) return;
    cursor = body.meta.cursor;
  }
}

Per-endpoint quick reference

EndpointStylePage params
GET /buyer/audit-logsOffsettake, skip
GET /reporting/metricsOffsettake, skip
GET /creativesOffsettake, skip
GET /test-cohortsOffsettake, skip
GET /human-feedbackOffsettake, skip
GET /measurement-engine/*Offsettake, skip
GET /advertiser-accountsOffsettake, skip
POST /discovery/:id/discover-productsCustom offsetgroupLimit, groupOffset, productsPerGroup, productOffset
GET /storefront/billing/transactionsCursorlimit, starting_after
GET /storefront/billing/payoutsCursorlimit, starting_after
When in doubt, inspect the response. Offset endpoints expose meta.pagination.hasMore; cursor endpoints expose meta.hasMore plus meta.cursor. Discovery exposes hasMoreGroups directly on data.
Don’t assume total is cheap to compute. For high-cardinality endpoints (audit logs, reporting metrics) prefer to consume pages with hasMore rather than computing Math.ceil(total / take) and looping — the count may be an estimate.