---
title: "Pagination"
description: "Pagination style varies by API and version:"
url: "https://developer.agentsync.io/api-pagination"
image: "https://developer.agentsync.io/_og/d/c_Ocean.takumi,title_Pagination,description_~UGFnaW5hdGlvbiBzdHlsZSB2YXJpZXMgYnkgQVBJIGFuZCB2ZXJzaW9uOg,props_eyJ0aGVtZSI6eyJtb2RlIjoibGlnaHQiLCJjb2xvcnMiOnsicHJpbWFyeSI6IiMxODdFRkYifX19,p_Ii9hcGktcGFnaW5hdGlvbiI,s_Ttmuh1q2H_0HRl4R.png"
---

# Pagination

Pagination style varies by API and version:

| API                              | Style              | Collection key | Next page                         |
| :------------------------------- | :----------------- | :------------- | :-------------------------------- |
| Contracting & Hierarchies API v2 | Token              | items          | page.nextToken → page_token param |
| Identity API (v2)                | Token              | items          | page.nextToken → page_token param |
| ProducerSync API v2              | Continuation token | embedded       | links.next.href                   |

---

## [v2 Token Pagination — Contracting, Hierarchies & Identity](#v2-token-pagination-contracting-hierarchies-identity)

All v2 list endpoints share one envelope: results in a top-level `items` array, pagination metadata in `page`.

```json
{
  "items": [ { ... }, { ... } ],
  "page": {
    "size": 25,
    "nextToken": "eyJ2IjoxLCJrIjp7Li4ufX0"
  }
}
```

| Field          | Description                                                                      |
| :------------- | :------------------------------------------------------------------------------- |
| items          | The page of results. Empty array when nothing matches.                           |
| page.size      | Number of items actually returned in this response — equal to items.length, so an empty result reports 0. |
| page.nextToken | Opaque token for the next page, or null on the last page. The token format is server-controlled — never parse or construct it. |

**Query parameters:**

| Parameter  | Description                                                        |
| :--------- | :----------------------------------------------------------------- |
| page_token | The previous response's page.nextToken. Omit on the first request. |
| page_size  | Requested items per page. Defaults to 25 on all v2 endpoints.      |

> **The request parameter and the response field have different names.** You read the token out of `page.nextToken` and send it back as `page_token` — there is no `nextToken` query parameter. Response bodies are `camelCase`; query parameters are `lower_snake_case`. See [Data Formats and Field Types](https://developer.agentsync.io/api-data-formats-and-field-types).

**Page size limits:** `page_size` must be between `1` and `250` on Contracting, Hierarchies and Identity alike. Out-of-range values are rejected with a `400` rather than clamped, so you get an explicit error instead of a silently truncated page.

Read `page.size` back off the response rather than assuming it equals what you asked for: `page.size` is the number of items actually returned, so it is smaller than your requested size on the last page and `0` on an empty result.

**Walking from the first page to the second:**

```bash
# First request — no token.
curl -H "Authorization: Bearer $ACCESS_TOKEN" \
  "https://api.sandbox.agentsync.io/contracting/v2/producers?page_size=25"
```

```json
{
  "items": [ { ... } ],
  "page": {
    "size": 25,
    "nextToken": "eyJ2IjoxLCJrIjp7ImlkIjoiMDBiNjc5NWUtYmNmNS0xNDZhLWEzMGYtYmI4NTk0MzBhMTk5In19"
  }
}
```

```bash
# Second request — the value of page.nextToken, sent as page_token.
curl -H "Authorization: Bearer $ACCESS_TOKEN" \
  "https://api.sandbox.agentsync.io/contracting/v2/producers?page_size=25&page_token=eyJ2IjoxLCJrIjp7ImlkIjoiMDBiNjc5NWUtYmNmNS0xNDZhLWEzMGYtYmI4NTk0MzBhMTk5In19"
```

Keep `page_size` (and any filters) identical across every request in a run. Repeat until `page.nextToken` is `null`.

**Troubleshooting: the same page keeps coming back**

Check the parameter name first. Only `page_token` advances the page — `nextToken`, `next_token`, and `pageToken` are all ignored, and unrecognized parameters are dropped rather than rejected. So the wrong name returns `200` with the first page every time, not an error.

A `400` actually tells you the opposite: the name was right and the _token_ couldn't be decoded. Send the token back exactly as received — it is opaque and URL-safe, so it needs no encoding, trimming, or base64 decoding.

**Iterating all pages (Python):**

```python
def paginate_v2(access_token, base_url, path, params=None):
    """Fetch every item from a v2 token-paginated list endpoint."""
    items = []
    params = dict(params or {})

    while True:
        response = requests.get(
            f"{base_url}{path}",
            headers={"Authorization": f"Bearer {access_token}"},
            params=params,
        )
        response.raise_for_status()
        data = response.json()
        items.extend(data["items"])

        next_token = data["page"]["nextToken"]
        if next_token is None:
            return items
        params["page_token"] = next_token
```

> **No totals by design.** v2 responses do not include `totalElements` or `totalPages` — iterate until `nextToken` is `null`. A pagination run is not a point-in-time snapshot: records updated mid-run may first appear on a later poll.

---

## [ProducerSync API v2 — Continuation Token Pagination](#producersync-api-v2-continuation-token-pagination)

ProducerSync v2 endpoints paginate with continuation tokens carried in a `links` object.

### [First Page](#first-page)

Your initial request returns:

-   a set of results under the `embedded` key
-   a `links` object, including a `next` URL for retrieving the next page

> **Note:** The ProducerSync API uses `embedded` and `links`, which differs from the `items` envelope used by Contracting, Hierarchies, and Identity. If you integrate multiple APIs, handle each collection key separately.

**Example Response:**

```json
{
  "embedded": {
    "licenses": [
      { ... },
      { ... }
    ]
  },
  "links": {
    "self": {
      "href": "https://api.agentsync.io/v2/licenses?continuationToken=0"
    },
    "next": {
      "href": "https://api.agentsync.io/v2/licenses?continuationToken=11862929"
    }
  }
}
```

### [Iterating with `links.next`](#iterating-with-linksnext)

Follow the URL in `links.next.href` until the end of the dataset:

```http
GET https://api.agentsync.io/v2/licenses?continuationToken=11862929
```

### [End of Results](#end-of-results)

On the final page:

-   the response will NOT include an `embedded` object
-   `links.next.href` will include `continuationToken=0`

```json
{
  "links": {
    "self": {
      "href": "https://api.agentsync.io/v2/licenses?continuationToken=11862929"
    },
    "next": {
      "href": "https://api.agentsync.io/v2/licenses?continuationToken=0"
    }
  }
}
```

A query with no results at all returns the same shape — no `embedded` key and `continuationToken=0` on both links. Handle this case gracefully.

### [Controlling Page Size](#controlling-page-size)

Pass `size` to control results per page (default 250):

-   Minimum: 1
-   Maximum: 1000

```http
GET /v2/licenses?size=500
```