> ## Documentation Index
> Fetch the complete documentation index at: https://docs.endstate.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Pagination

> All list endpoints use cursor-based pagination. Learn how to request pages and iterate through large result sets.

## Overview

Every list endpoint in the Endstate API — `GET /v1/units`, `GET /v1/collections`, and `GET /v1/chips` — uses cursor-based pagination. You receive a page of results and a cursor you can use to fetch the next page. This approach is stable under concurrent writes: inserting a new record never causes you to see a duplicate or skip an item mid-iteration.

***

## Request parameters

<ParamField query="limit" type="integer">
  The maximum number of records to return. Defaults to `50`. Maximum is `100`.
</ParamField>

<ParamField query="cursor" type="string">
  An opaque cursor pointing to the start of the next page. Omit this parameter
  on the first request. On subsequent requests, pass the `next_cursor` value
  from the previous response.
</ParamField>

***

## Response envelope

Every list response wraps results in a consistent envelope. The resource key matches the endpoint — `units`, `collections`, or `chips`. (Unit objects are trimmed throughout this page for brevity; see [Units](/concepts/units) for the full shape.)

```json theme={null}
{
  "units": [
    {
      "id": "8e1a7f50-90ab-4cde-f012-3456789abcde",
      "external_id": "sku-hoodie-black-m-001",
      "name": "Black Hoodie — Size M",
      "created_at": "2026-05-14T10:30:00.000Z"
    }
  ],
  "pagination": {
    "limit": 50,
    "has_more": true,
    "next_cursor": "eyJjcmVhdGVkX2F0IjoiMjAyNi0wNS0xNFQxMDozMDowMC4wMDBaIiwiaWQiOiI4ZTFhN2Y1MCJ9"
  }
}
```

<ResponseField name="pagination.limit" type="integer">
  The limit that was applied to this page.
</ResponseField>

<ResponseField name="pagination.has_more" type="boolean">
  `true` if there are additional records beyond this page. `false` when you have
  reached the last page.
</ResponseField>

<ResponseField name="pagination.next_cursor" type="string | null">
  The cursor to pass as `cursor` on the next request. `null` when `has_more` is
  `false`.
</ResponseField>

***

## How to page through results

When `has_more` is `true`, take the `next_cursor` value and pass it as the `cursor` query parameter on your next request. Repeat until `has_more` is `false`.

<Warning>
  Cursors are opaque strings. Never parse, decode, or construct them — their
  format may change without notice. Treat each cursor as an opaque, indivisible
  value.
</Warning>

***

## Worked example

The following two requests fetch the first page of units and then the second page.

**First request — no cursor**

```bash theme={null}
curl "https://api2.endstate.io/v1/units?limit=2" \
  -H "Authorization: Bearer end_sk_test_..."
```

```json theme={null}
{
  "units": [
    {
      "id": "8e1a7f50-90ab-4cde-f012-3456789abcde",
      "external_id": "sku-hoodie-black-m-001",
      "name": "Black Hoodie — Size M",
      "created_at": "2026-05-14T10:30:00.000Z"
    },
    {
      "id": "1c2d3e4f-5a6b-7c8d-9e0f-a1b2c3d4e5f6",
      "external_id": "sku-hoodie-black-l-001",
      "name": "Black Hoodie — Size L",
      "created_at": "2026-05-14T11:00:00.000Z"
    }
  ],
  "pagination": {
    "limit": 2,
    "has_more": true,
    "next_cursor": "eyJjcmVhdGVkX2F0IjoiMjAyNi0wNS0xNFQxMTowMDowMC4wMDBaIiwiaWQiOiIxYzJkM2U0ZiJ9"
  }
}
```

**Second request — pass `next_cursor` as `cursor`**

```bash theme={null}
curl "https://api2.endstate.io/v1/units?limit=2&cursor=eyJjcmVhdGVkX2F0IjoiMjAyNi0wNS0xNFQxMTowMDowMC4wMDBaIiwiaWQiOiIxYzJkM2U0ZiJ9" \
  -H "Authorization: Bearer end_sk_test_..."
```

```json theme={null}
{
  "units": [
    {
      "id": "7f8e9d0c-1b2a-3c4d-5e6f-7a8b9c0d1e2f",
      "external_id": "sku-jacket-navy-m-001",
      "name": "Navy Jacket — Size M",
      "created_at": "2026-05-15T09:00:00.000Z"
    }
  ],
  "pagination": {
    "limit": 2,
    "has_more": false,
    "next_cursor": null
  }
}
```

`has_more` is `false` and `next_cursor` is `null`, so this is the last page.

***

## Combining filters with pagination

Some list endpoints accept exact-match filters that narrow the result set before pagination is applied:

| Endpoint              | Supported filters                                                 |
| --------------------- | ----------------------------------------------------------------- |
| `GET /v1/units`       | `external_id` — return only the unit with this external ID        |
| `GET /v1/collections` | `external_id` — return only the collection with this external ID  |
| `GET /v1/chips`       | `is_test` — filter to test chips (`true`) or real chips (`false`) |

Pass filters alongside `limit` and `cursor` as additional query parameters:

```bash theme={null}
curl "https://api2.endstate.io/v1/units?external_id=sku-hoodie-black-m-001" \
  -H "Authorization: Bearer end_sk_test_..."
```

```bash theme={null}
curl "https://api2.endstate.io/v1/chips?is_test=true&limit=100" \
  -H "Authorization: Bearer end_sk_test_..."
```

<Note>
  When filtering by `external_id` on units or collections, the API returns at
  most one result because external IDs are unique within your organization. You
  can still use the standard pagination envelope — `has_more` will be `false`.
</Note>

***

## Next steps

<CardGroup cols={2}>
  <Card title="Errors" icon="circle-x" href="/conventions/errors">
    Understand the error envelope and how to branch on stable error codes.
  </Card>

  <Card title="API reference" icon="book-open" href="/api-reference/introduction">
    Full request and response schemas for every endpoint.
  </Card>
</CardGroup>
