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

# Pagination

> Learn how to paginate through large result sets in the Smartbills API

## Overview

All Smartbills API list endpoints return paginated results. Pagination allows you to efficiently retrieve large datasets by splitting them into smaller pages.

## Request Parameters

All list endpoints accept these pagination and sorting parameters:

<ParamField query="page" type="integer" default="1">
  Page number to retrieve (starts at 1)
</ParamField>

<ParamField query="pageSize" type="integer" default="20">
  Number of items per page. Minimum: 1, Maximum: 100, Default: 20
</ParamField>

<ParamField query="sortBy" type="string" default="createdAt">
  Field to sort results by (e.g., `createdAt`, `date`, `amount`)
</ParamField>

<ParamField query="sortOrder" type="string" default="desc">
  Sort direction: `asc` (ascending) or `desc` (descending)
</ParamField>

## Response Format

Paginated responses include both the data array and a pagination metadata object:

```json theme={null}
{
  "data": [
    {
      "id": 1,
      "merchant": "Office Depot",
      "amount": 45.99
    },
    {
      "id": 2,
      "merchant": "Staples",
      "amount": 32.50
    }
  ],
  "pagination": {
    "page": 1,
    "pageSize": 20,
    "totalPages": 5,
    "totalCount": 95,
    "hasNext": true,
    "hasPrevious": false
  }
}
```

### Pagination Object

<ResponseField name="pagination" type="object">
  Pagination metadata

  <Expandable title="Properties">
    <ResponseField name="page" type="integer">
      Current page number
    </ResponseField>

    <ResponseField name="pageSize" type="integer">
      Number of items per page
    </ResponseField>

    <ResponseField name="totalPages" type="integer">
      Total number of pages available
    </ResponseField>

    <ResponseField name="totalCount" type="integer">
      Total number of items across all pages
    </ResponseField>

    <ResponseField name="hasNext" type="boolean">
      Whether there is a next page available
    </ResponseField>

    <ResponseField name="hasPrevious" type="boolean">
      Whether there is a previous page available
    </ResponseField>
  </Expandable>
</ResponseField>

## Basic Examples

### First Page

Retrieve the first page of results with sorting:

<CodeGroup>
  ```bash cURL theme={null}
  curl --request GET \
    --url 'https://api.smartbills.io/v1/expenses?page=1&pageSize=20&sortBy=date&sortOrder=desc' \
    --header 'Authorization: Bearer YOUR_API_KEY' \
    --header 'x-tenant-id: 123'
  ```

  ```javascript JavaScript theme={null}
  import { SmartbillsClient } from '@smartbills/sdk';

  const client = new SmartbillsClient({
    accessToken: 'YOUR_API_KEY',
    businessId: 123
  });

  const result = await client.expenses.list({
    page: 1,
    pageSize: 20,
    sortBy: 'date',
    sortOrder: 'desc'
  });

  console.log(`Page ${result.pagination.page} of ${result.pagination.totalPages}`);
  console.log(`Total items: ${result.pagination.totalCount}`);
  ```

  ```python Python theme={null}
  from smartbills import SmartbillsClient

  client = SmartbillsClient(access_token="YOUR_API_KEY", business_id=123)

  result = client.expenses.list(
      page=1,
      page_size=20,
      sort_by="date",
      sort_order="desc"
  )

  print(f"Page {result.pagination.page} of {result.pagination.total_pages}")
  print(f"Total items: {result.pagination.total_count}")
  ```
</CodeGroup>

### Next Page

Navigate to the next page by checking `hasNext`:

<CodeGroup>
  ```bash cURL theme={null}
  curl --request GET \
    --url 'https://api.smartbills.io/v1/expenses?page=2&pageSize=20&sortBy=date&sortOrder=desc' \
    --header 'Authorization: Bearer YOUR_API_KEY' \
    --header 'x-tenant-id: 123'
  ```

  ```javascript JavaScript theme={null}
  if (result.pagination.hasNext) {
    const nextPage = result.pagination.page + 1;
    const nextResult = await client.expenses.list({
      page: nextPage,
      pageSize: 20,
      sortBy: 'date',
      sortOrder: 'desc'
    });
  }
  ```

  ```python Python theme={null}
  if result.pagination.has_next:
      next_page = result.pagination.page + 1
      next_result = client.expenses.list(
          page=next_page,
          page_size=20,
          sort_by="date",
          sort_order="desc"
      )
  ```
</CodeGroup>

## Iterating Through All Pages

### Simple Iteration

Loop through all pages to retrieve all results:

<CodeGroup>
  ```bash cURL theme={null}
  #!/bin/bash
  PAGE=1
  HAS_MORE=true

  while [ "$HAS_MORE" = "true" ]; do
    RESPONSE=$(curl --silent --request GET \
      --url "https://api.smartbills.io/v1/expenses?page=${PAGE}&pageSize=100" \
      --header 'Authorization: Bearer YOUR_API_KEY' \
      --header 'x-tenant-id: 123')
    
    echo "Page ${PAGE}: $(echo $RESPONSE | jq '.data | length') items"
    
    HAS_MORE=$(echo $RESPONSE | jq '.pagination.hasNext')
    PAGE=$((PAGE + 1))
  done
  ```

  ```javascript JavaScript theme={null}
  async function getAllExpenses(client) {
    const allExpenses = [];
    let page = 1;
    let hasMore = true;

    while (hasMore) {
      const result = await client.expenses.list({
        page: page,
        pageSize: 100
      });

      allExpenses.push(...result.data);
      hasMore = result.pagination.hasNext;
      page++;
    }

    return allExpenses;
  }

  const client = new SmartbillsClient({
    accessToken: 'YOUR_API_KEY',
    businessId: 123
  });

  const expenses = await getAllExpenses(client);
  console.log(`Retrieved ${expenses.length} expenses`);
  ```

  ```python Python theme={null}
  def get_all_expenses(client):
      all_expenses = []
      page = 1
      has_more = True

      while has_more:
          result = client.expenses.list(page=page, page_size=100)
          all_expenses.extend(result.data)
          has_more = result.pagination.has_next
          page += 1

      return all_expenses

  client = SmartbillsClient(access_token="YOUR_API_KEY", business_id=123)

  expenses = get_all_expenses(client)
  print(f"Retrieved {len(expenses)} expenses")
  ```
</CodeGroup>

### With Error Handling and Rate Limiting

Production-ready pagination with error handling:

<CodeGroup>
  ```javascript JavaScript theme={null}
  async function getAllExpensesSafely(client) {
    const allExpenses = [];
    let page = 1;
    const maxPages = 100; // Safety limit

    while (page <= maxPages) {
      try {
        const result = await client.expenses.list({
          page: page,
          pageSize: 100
        });

        allExpenses.push(...result.data);
        console.log(`Fetched page ${page}/${result.pagination.totalPages}`);

        if (!result.pagination.hasNext) {
          break;
        }

        page++;

        // Small delay to respect rate limits
        await new Promise(resolve => setTimeout(resolve, 100));
      } catch (error) {
        console.error(`Error fetching page ${page}:`, error);
        throw error;
      }
    }

    return allExpenses;
  }
  ```

  ```python Python theme={null}
  import time

  def get_all_expenses_safely(client):
      all_expenses = []
      page = 1
      max_pages = 100  # Safety limit

      while page <= max_pages:
          try:
              result = client.expenses.list(page=page, page_size=100)
              all_expenses.extend(result.data)
              print(f"Fetched page {page}/{result.pagination.total_pages}")

              if not result.pagination.has_next:
                  break

              page += 1
              time.sleep(0.1)  # Small delay to respect rate limits
          except Exception as e:
              print(f"Error fetching page {page}: {e}")
              raise

      return all_expenses
  ```
</CodeGroup>

## Pagination with Filters

Combine pagination with filtering to narrow your results:

<CodeGroup>
  ```bash cURL theme={null}
  curl --request GET \
    --url 'https://api.smartbills.io/v1/expenses?page=1&pageSize=50&status=pending&sortBy=amount&sortOrder=desc' \
    --header 'Authorization: Bearer YOUR_API_KEY' \
    --header 'x-tenant-id: 123'
  ```

  ```javascript JavaScript theme={null}
  const result = await client.expenses.list({
    page: 1,
    pageSize: 50,
    status: 'pending',
    sortBy: 'amount',
    sortOrder: 'desc'
  });
  ```

  ```python Python theme={null}
  result = client.expenses.list(
      page=1,
      page_size=50,
      status="pending",
      sort_by="amount",
      sort_order="desc"
  )
  ```
</CodeGroup>

## Pagination Limits

### Maximum Page Size

* **Maximum**: 100 items per page
* **Default**: 20 items per page
* **Minimum**: 1 item per page

<Warning>
  Requesting more than 100 items per page will result in a 400 Bad Request validation error.
</Warning>

## Best Practices

<AccordionGroup>
  <Accordion title="Use Appropriate Page Sizes" icon="list">
    * **Small pages (20-50)**: Better for UI pagination and faster initial response
    * **Large pages (100)**: Better for batch processing and fewer API calls
    * **Default (20)**: Good balance for most use cases
  </Accordion>

  <Accordion title="Always Check hasNext" icon="check">
    Always check `hasNext` before fetching the next page. This prevents unnecessary API calls when you have reached the end of the results.
  </Accordion>

  <Accordion title="Handle Rate Limits" icon="gauge">
    Add small delays between requests when fetching multiple pages to avoid hitting rate limits. See [Rate Limits](/api-reference/rate-limits) for details.
  </Accordion>

  <Accordion title="Cache Results When Possible" icon="database">
    Cache paginated results to reduce redundant API calls, especially for data that does not change frequently.
  </Accordion>

  <Accordion title="Use Filters to Reduce Data" icon="filter">
    Apply filters to narrow results before paginating, reducing the total number of pages and API calls needed.
  </Accordion>
</AccordionGroup>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Empty results on a valid page" icon="circle-question">
    **Possible causes**: Data was deleted between requests, filters are too restrictive, or a race condition with concurrent modifications. **Solution**: Refetch from the beginning or adjust your filters.
  </Accordion>

  <Accordion title="Inconsistent page counts" icon="calculator">
    **Reason**: This is normal. Data can be added or removed while you are paginating. **Solution**: Use `hasNext` instead of relying on `totalPages` for iteration logic.
  </Accordion>

  <Accordion title="Slow pagination performance" icon="hourglass">
    **Solutions**: Use larger page sizes (up to 100), add filters to reduce the total dataset, cache results, or use webhooks for real-time updates instead of polling.
  </Accordion>
</AccordionGroup>

## Related Resources

<CardGroup cols={2}>
  <Card title="Rate Limits" icon="gauge" href="/api-reference/rate-limits">
    Understand API rate limiting
  </Card>

  <Card title="Error Handling" icon="triangle-exclamation" href="/api-reference/errors">
    Handle pagination errors
  </Card>

  <Card title="List Expenses" icon="receipt" href="/api-reference/introduction">
    Expenses list endpoint
  </Card>

  <Card title="Webhooks" icon="webhook" href="/api-reference/webhooks">
    Real-time event notifications
  </Card>
</CardGroup>
