> ## 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.

# Pagination

> Paging conventions across v2 list endpoints

The v2 API uses two pagination styles, each fitting the underlying data model. Knowing which style applies to which endpoint lets you build a uniform client that handles both.

| 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`)                     |

## Offset pagination (`take` / `skip`)

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

### Request

```http theme={null}
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`:

```json theme={null}
{
  "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

<CodeGroup>
  ```typescript TypeScript theme={null}
  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;
    }
  }
  ```

  ```python Python theme={null}
  def list_all_audit_logs():
      take, skip = 100, 0
      while True:
          res = requests.get(
              "https://api.interchange.io/api/v2/buyer/audit-logs",
              params={"take": take, "skip": skip},
              headers={"Authorization": f"Bearer {SCOPE3_API_KEY}"},
          )
          body = res.json()
          for log in body["data"]["logs"]:
              yield log
          if not body["meta"]["pagination"]["hasMore"]:
              return
          skip += take
  ```
</CodeGroup>

## 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

```http theme={null}
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

```json theme={null}
{
  "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.

## 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` |

<Tip>
  When in doubt, inspect the response. Offset endpoints expose
  `meta.pagination.hasMore`; discovery exposes `hasMoreGroups` directly on
  `data`.
</Tip>

<Warning>
  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.
</Warning>
