# Pagination

All list endpoints use **forward-only, cursor-based pagination**. This approach is reliable for large volumes of data and handles concurrent inserts well -- unlike offset-based pagination, you will never see duplicate or missing records.

## How It Works

Every list response includes a `data` array and a `has_more` boolean:


```json
{
  "data": [
    { "id": "cust_aaa111", "name": "Alice" },
    { "id": "cust_bbb222", "name": "Bob" },
    { "id": "cust_ccc333", "name": "Carlos" }
  ],
  "has_more": true
}
```

When `has_more` is `true`, pass the **`id` of the last item** as `starting_after` to fetch the next page.

## Parameters

| Parameter | Type | Default | Description |
|  --- | --- | --- | --- |
| `starting_after` | string | -- | Prefixed ID of the last item from the previous page. Returns items after this one. |
| `limit` | integer | 25 | Number of items to return. Min: 1, Max: 100. |


## Basic Example


```typescript Node.js
let hasMore = true;
let startingAfter: string | undefined;

while (hasMore) {
  const page = await dinie.customers.list({
    limit: 25,
    starting_after: startingAfter,
  });

  for (const customer of page.data) {
    console.log(customer.id, customer.name);
  }

  hasMore = page.has_more;
  if (page.data.length > 0) {
    startingAfter = page.data[page.data.length - 1].id;
  }
}
```


```ruby Ruby
has_more = true
starting_after = nil

while has_more
  page = dinie.customers.list(limit: 25, starting_after: starting_after)

  page.data.each { |customer| puts "#{customer.id} #{customer.name}" }

  has_more = page.has_more
  starting_after = page.data.last&.id
end
```


```python Python
has_more = True
starting_after = None

while has_more:
    page = dinie.customers.list(limit=25, starting_after=starting_after)

    for customer in page.data:
        print(customer.id, customer.name)

    has_more = page.has_more
    if page.data:
        starting_after = page.data[-1].id
```


```bash cURL
curl "https://sandbox.api.dinie.com.br/v3/customers?limit=2" \
  -H "Authorization: Bearer dinie_at_..."
```


```json
{
  "data": [
    { "id": "cust_aaa111", "name": "Alice" },
    { "id": "cust_bbb222", "name": "Bob" }
  ],
  "has_more": true
}
```

Next page -- pass the last ID:


```bash
curl "https://sandbox.api.dinie.com.br/v3/customers?limit=2&starting_after=cust_bbb222" \
  -H "Authorization: Bearer dinie_at_..."
```


```json
{
  "data": [
    { "id": "cust_ccc333", "name": "Carlos" }
  ],
  "has_more": false
}
```

## Auto-Pagination with SDKs

The SDKs provide auto-pagination iterators that manage the `starting_after` cursor for you. This is the recommended approach for iterating over all items in a collection.


```typescript Node.js
for await (const customer of dinie.customers.list({ limit: 100 })) {
  console.log(customer.id, customer.name);
}
```


```ruby Ruby
dinie.customers.list(limit: 100).auto_paging_each do |customer|
  puts "#{customer.id} #{customer.name}"
end
```


```python Python
for customer in dinie.customers.list(limit=100).auto_paging_iter():
    print(customer.id, customer.name)
```

> **Tip:** Auto-pagination makes sequential requests under the hood, fetching the next page only when you consume all items from the current page. Set `limit: 100` to minimize the number of HTTP requests.


## Sorting

Some endpoints support sorting with the `sort` and `order` parameters:


```bash
curl "https://sandbox.api.dinie.com.br/v3/customers?sort=created_at&order=asc&limit=25" \
  -H "Authorization: Bearer dinie_at_..."
```

| Parameter | Default | Values |
|  --- | --- | --- |
| `sort` | `created_at` | Depends on the endpoint (see the endpoint documentation) |
| `order` | `desc` | `asc`, `desc` |


Pagination always traverses the current sort order. When you use `starting_after`, the API returns items that come after the specified ID in the selected sort order.

## Empty Results

An empty collection returns an empty `data` array:


```json
{
  "data": [],
  "has_more": false
}
```

> **Info:** Endpoints with naturally small collections (webhook endpoints, API credentials) still use the standard `{ "data": [...], "has_more": false }` envelope for consistency.