# Pagination (/en/docs/pix-processamento/best-practices/pagination)

<QuickLinks>
  <QuickLink href="/docs/pix-processamento/endpoints/reports/get_user_transactions" title="GET /user/transactions" />

  <QuickLink href="/docs/pix-processamento/endpoints/reports/post_user_report" title="POST /user/report" />

  <QuickLink href="/docs/pix-processamento/endpoints/callbacks/get_user_callbacks" title="GET /user/callbacks" />
</QuickLinks>

Endpoints that list resources paginate with &#x2A;*`page` + `limit`**, but the response envelope differs per route. None of the three returns `hasNextPage` at the top level of the response.

| Endpoint                 | Response envelope                                                      | Stop condition                    |
| ------------------------ | ---------------------------------------------------------------------- | --------------------------------- |
| `GET /user/transactions` | `{ total, pages, transactions }`                                       | `page >= pages`                   |
| `GET /user/callbacks`    | `{ pagination: { page, limit, hasNextPage }, callbacks }`              | `pagination.hasNextPage` is false |
| `GET /user/infractions`  | `{ pagination: { page, limit, totalItems, totalPages }, infractions }` | `page >= pagination.totalPages`   |

## Loop pattern for `/user/transactions` [#loop-pattern-for-usertransactions]

<Tabs items="['curl', 'Node.js']">
  <Tab value="curl">
    ```bash
    PAGE=1
    LIMIT=100

    while : ; do
      RESP=$(curl -s "https://api.payzu.processamento.com/v1/user/transactions?dateFrom=2025-11-01&page=$PAGE&limit=$LIMIT" \
        -H "Authorization: Bearer $TOKEN" \
        -H "Content-Type: application/json")

      echo "$RESP" | jq -c '.transactions[]'

      PAGES=$(echo "$RESP" | jq -r '.pages')
      [ "$PAGE" -ge "$PAGES" ] && break
      PAGE=$((PAGE+1))
    done
    ```
  </Tab>

  <Tab value="Node.js">
    ```ts
    async function* iterateTransactions(filters: Record<string, string>) {
      let page = 1;
      const limit = 100;
      while (true) {
        const params = new URLSearchParams({ ...filters, page: String(page), limit: String(limit) });
        const res = await fetch(`https://api.payzu.processamento.com/v1/user/transactions?${params}`, {
          headers: {
            Authorization: `Bearer ${process.env.PAYZU_TOKEN}`,
            'Content-Type': 'application/json',
          },
        });
        const { pages, transactions } = await res.json();
        for (const tx of transactions) yield tx;
        if (page >= pages) return;
        page++;
      }
    }

    for await (const tx of iterateTransactions({ dateFrom: '2025-11-01' })) {
      await process(tx);
    }
    ```
  </Tab>
</Tabs>

On `/user/callbacks`, iterate over `callbacks` and stop when `pagination.hasNextPage` is `false`. On `/user/infractions`, iterate over `infractions` and stop when `page` reaches `pagination.totalPages`.

## Available filters [#available-filters]

| Filter                | When to use                                        |
| --------------------- | -------------------------------------------------- |
| `dateFrom` / `dateTo` | Time window (ISO 8601).                            |
| `clientReference`     | Finds the transaction matching your order.         |
| `virtualAccount`      | Filter by tenant (multi-store).                    |
| `status`              | CSV: `COMPLETED,PENDING`. Accepts multiple values. |
| `type`                | CSV: `DEPOSIT,WITHDRAW,COMMISSION`.                |
| `endToEndId`          | Unique Bacen identifier.                           |
| `document`, `name`    | Payer filters. `document` digits only (11 or 14).  |
| `amount`              | Filter by exact amount.                            |

## Page size and limit [#page-size-and-limit]

| Endpoint                 | `limit` max | Default |
| ------------------------ | ----------- | ------- |
| `GET /user/transactions` | 1000        | 10      |
| `GET /user/callbacks`    | 100         | 10      |
| `GET /user/infractions`  | 100         | 10      |

Above the route maximum, the request is rejected with `400` and `errorCode` `PZV001`. Oversized pages degrade latency. If you need a long period (month, year) or to export everything, **prefer the asynchronous report**.

## When to use the asynchronous report instead of paginating [#when-to-use-the-asynchronous-report-instead-of-paginating]

| Scenario                                   | Recommendation                                                                                        |
| ------------------------------------------ | ----------------------------------------------------------------------------------------------------- |
| On-screen listing (dashboard)              | `GET /user/transactions` with pagination.                                                             |
| Single lookup                              | `GET /user/transactions?clientReference=order-1234`.                                                  |
| Daily reconciliation (\< 10k transactions) | `GET /user/transactions` paginated.                                                                   |
| Monthly/yearly reconciliation              | [`POST /user/report`](/docs/pix-processamento/endpoints/reports/post_user_report) (asynchronous CSV). |
| BI/Data Warehouse                          | `POST /user/report` run daily, ingested via ETL.                                                      |

<Callout type="info">
  The asynchronous report generates a CSV file with a signed download URL. It has no row limit and runs in the background. See the [Reconciliation tutorial](/docs/pix-processamento/tutoriais/reconciliation).
</Callout>

## Common pitfalls [#common-pitfalls]

| Pitfall                                                         | Symptom                                     |
| --------------------------------------------------------------- | ------------------------------------------- |
| Fetching everything without `dateFrom` on a high-volume account | Slow response, possible timeout             |
| `limit` above the route maximum (`1000` on `/user/callbacks`)   | 400 error with `errorCode` `PZV001`         |
| Reading `hasNextPage` at the top level of the response          | Value is absent, the loop stops on page one |
| Reusing one stop condition across the three routes              | Pages dropped silently                      |
| Fixed page (`page=1` always)                                    | Only reads the first page, misses the rest  |
| Passing `document` with punctuation (`123.456.789-00`)          | 400 error, regex accepts digits only        |