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

# Pagination

> Lists, cursors and totals, plus choosing fields and expanding related objects

Every endpoint that returns many items returns a list, and you page through it with a cursor. A list has the same shape everywhere:

```json theme={null}
{
  "object": "list",
  "data": [],
  "has_more": true,
  "next_cursor": "eyJ2IjoxLCJrIjoi...",
  "total": { "value": 10000, "relation": "gte" }
}
```

* **`data`.** The items on this page.
* **`has_more`.** `true` when another page follows.
* **`next_cursor`.** Pass it as `cursor` to get the next page. `null` on the last page.
* **`total`.** How many items match. Counting stops at 10,000, and `relation` is then `gte`, meaning at least that many.

## Parameters

<ParamField query="limit" type="integer" default="25">
  The page size, at most 100. A few sub-lists use fixed pages of 25 rows, and `limit` can only make those smaller: a shop's products, a shop's reviews, a product's reviews and related keywords.
</ParamField>

<ParamField query="cursor" type="string">
  The `next_cursor` of the previous page. A cursor works only with the request that made it: the same endpoint, filters, sort, key and workspace. Change any of them and the answer is `400 invalid_cursor`. Start again without a cursor.
</ParamField>

## Example

Fetch the first page, then pass `next_cursor` back with the same query:

```bash theme={null}
curl -G https://api.alura.io/v1/research/products \
  -H "Authorization: Bearer $ALURA_API_KEY" \
  --data-urlencode "q=mug" \
  --data-urlencode "limit=100" \
  --data-urlencode "cursor=eyJ2IjoxLCJrIjoi..."
```

[Find products](/guides/find-products) has a paging loop in curl, TypeScript and Python.

## Limits

* **10,000 results.** Paging stops there with `400 cursor_window_exceeded`. Narrow the query to reach further.
* **Stable order.** Ties are broken by ID, so a page boundary never splits two equal items. Values can change between pages, so an item can still move.
* **MCP pages.** Through MCP, pages default to 20 items and hold at most 50.
* **Cost.** A search costs the same per page whatever `limit` is. Use `limit=100` when you want many results.

## Choosing fields

`fields` takes comma-separated dotted paths and returns only those. `id`, `object` and `as_of` always come back.

```text theme={null}
GET /v1/research/products?q=mug&fields=title,price.usd,sales.est_monthly_avg
```

An unknown path answers `400 invalid_request`, with the valid paths in `valid_fields`. `fields` never changes the price of a call.

## Expanding related objects

Some endpoints take `expand` to include related objects in the same response, for free:

| Endpoint | `expand` values |
| - | - |
| `GET /v1/research/products/{id}` | `shop`: the full shop instead of its ID and name |
| `GET /v1/research/shops/{id}` | `top_products`: its 5 best-selling products. `about`: its announcement, languages, custom requests and where it ships from |

An expansion never fetches anything from Etsy. When Alura has no data for it, the field is `null`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.