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

# Pagination

Every collection `GET` in the API is a paginated list endpoint. They all behave the
same way: `cursor` and `limit` go in as query parameters, a
`{ data, next_cursor }` envelope comes back.

## The envelope

```json theme={"dark"}
GET /v2/supplier-products?limit=2
{
  "data": [
    { "id": "33333333-3333-3333-3333-333333333333", "name": "Portland Cement CEM I 42.5 R", … },
    { "id": "44444444-4444-4444-4444-444444444444", "name": "Limestone filler", … }
  ],
  "next_cursor": "Y3Vyc29yOjQ0NDQ0NDQ0"
}
```

* **`data`** — the items on this page, at most `limit` of them.
* **`next_cursor`** — pass it back as `cursor` to get the next page. It is `null`
  on the last page, and that is the **only** reliable end-of-collection signal: a
  page can be full and still be the last one.

## Walking a collection

Request the first page without a `cursor`, then keep feeding `next_cursor` back
until it comes back `null`:

```http theme={"dark"}
GET /v2/supplier-products?limit=100
GET /v2/supplier-products?limit=100&cursor=Y3Vyc29yOjQ0NDQ0NDQ0
GET /v2/supplier-products?limit=100&cursor=Y3Vyc29yOjc3Nzc3Nzc3
→ "next_cursor": null   ← done
```

Keep `limit` and any filters identical across the pages of one walk. A cursor
belongs to the query that produced it; changing the query mid-walk gives you
undefined coverage.

## Parameters

| Parameter | Default | Notes                                                                                                                                                                    |
| --------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `cursor`  | —       | Opaque token from the previous page's `next_cursor`. Omit it for the first page. Never construct or parse one — the encoding is not part of the contract and may change. |
| `limit`   | `50`    | Items per page, between `1` and `200`.                                                                                                                                   |

A `cursor` the API cannot decode, or a `limit` out of range, is rejected with
`422` (`err_not_valid`). See [Errors](/errors).

## Ordering

Items come back in a stable order, so walking a collection that is not changing
underneath you visits every item exactly once. The order is an implementation
detail — it is not a meaningful sort, so do not rely on it to present data or to
infer recency. Entities created or deleted while a walk is in progress may or may
not appear in it.

## Filtering by your own codes

Each list endpoint takes an optional `external_id` filter. This is how you resolve
one of your own codes to an Emidat `id`:

```http theme={"dark"}
GET /v2/supplier-products?external_id=CEMENT-SUPPLIER-DE-001
→ 200 {
    "data": [ { "id": "33333333-3333-3333-3333-333333333333", "external_id": "CEMENT-SUPPLIER-DE-001", … } ],
    "next_cursor": null
  }
```

An `external_id` is unique per entity type within your manufacturer, so `data`
holds at most one item; an empty `data` means no active entity carries that code.
See [External IDs](/external-ids).
