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