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.
| Pattern | Query params | Used by |
|---|
| Offset | take, skip | Most list endpoints (audit logs, reporting, creatives, test cohorts, …) |
| Custom offset | groupOffset, groupLimit, productOffset, productsPerGroup | Discovery (POST /discovery/.../discover-products) |
| Cursor | limit, starting_after | Storefront billing endpoints proxied to Stripe |
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>
| Param | Type | Default | Notes |
|---|
take | number | varies | Page size. Most endpoints cap at 100 or 200 |
skip | number | 0 | Records 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
}
}
}
| Field | Type | Meaning |
|---|
skip | number | Echoed offset |
take | number | Echoed page size |
total | number | Total matching rows across all pages |
hasMore | boolean | true 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
}
| Param | Type | Meaning |
|---|
groupLimit | number | How many product groups to return |
groupOffset | number | Skip this many product groups |
productsPerGroup | number | Products to include within each returned group |
productOffset | number | Skip 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.
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…
| Param | Type | Default | Meaning |
|---|
limit | number | 25 | Page size, max 100 |
starting_after | string | — | Object 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
| Endpoint | Style | Page params |
|---|
GET /buyer/audit-logs | Offset | take, skip |
GET /reporting/metrics | Offset | take, skip |
GET /creatives | Offset | take, skip |
GET /test-cohorts | Offset | take, skip |
GET /human-feedback | Offset | take, skip |
GET /measurement-engine/* | Offset | take, skip |
GET /advertiser-accounts | Offset | take, skip |
POST /discovery/:id/discover-products | Custom offset | groupLimit, groupOffset, productsPerGroup, productOffset |
GET /storefront/billing/transactions | Cursor | limit, starting_after |
GET /storefront/billing/payouts | Cursor | limit, 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.