# Pagination

Source: https://modulify.ai/docs/api/pagination

How methods that return long lists hand them over a page at a time, and how to walk every page.

Methods that list things return a fixed number of items per call. Each method's page says how many and which of the patterns below it uses. There are four.

## Offset and count

Most lists take an `offset`, the number of items to skip, and answer with `count`, the total. Start at 0 and add the page size until you have collected `count` items.

```bash
curl -X POST https://api.modulify.ai/v1/list_sites \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{"workspaceId":"WORKSPACE_ID","offset":8}'
```

`list_sites`, `list_templates` and `list_deployments` work this way. A few, such as `list_rows`, `list_workspace_members` and `get_publish_logs`, also take a `limit` for the page size, up to a maximum their page states. Some answer with `hasMore` and the offset to send next instead of a total, such as `list_workspace_credit_activity`, so keep going while `hasMore` is true.

```javascript
const ListAllSites = async (workspaceId) => {
    const sites = []

    while (true) {
        const response = await fetch('https://api.modulify.ai/v1/list_sites', {
            method: 'POST',
            headers: { Authorization: `Bearer ${process.env.MODULIFY_TOKEN}` },
            body: JSON.stringify({ workspaceId, offset: sites.length })
        })

        const result = await response.json()

        if (!result.success) throw new Error(result.message)

        sites.push(...result.data)

        if (result.data.length === 0 || sites.length >= result.count) return sites
    }
}
```

## Next cursor

Logs and histories that grow all the time answer with a `nextCursor`. Send it back as `cursor` for the next page, and stop when it comes back `null`. Keep every other argument the same between pages, since the cursor belongs to that exact query.

`list_deploy_hook_calls`, `list_site_cron_runs`, `list_webhook_deliveries`, `list_site_email_sends`, `list_site_email_engagement` and `list_site_email_suppressions` work this way.

```bash
curl -X POST https://api.modulify.ai/v1/list_site_cron_runs \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{"projectId":"SITE_ID","cursor":"NEXT_CURSOR"}'
```

`list_storage_files` follows the same idea with a different name: it answers with `nextToken`, which you send back as `token` to read the next page of the same folder.

## Page numbers

The backup histories, `list_code_backups`, `list_storage_backups` and `list_database_backups`, take a `page` that starts at 1.

## Reading backwards

Two methods page through something that only grows, from the newest end:

- `list_messages` returns the newest chat messages with `hasMore` and `oldestCursor`. Send `oldestCursor` back as `cursor` to get the page before, and stop when `hasMore` is false.
- `get_job` returns the events of a generation after a numeric `cursor`. Send back the `cursor` from each answer to receive only new events. See [Long-running work](https://modulify.ai/docs/api/long-running-work).

## Tips

- **Read `count`, not the page length.** The last page is usually shorter than the rest, and an empty page means you have read everything.
- **Expect change while you page.** Items added or removed between two calls can shift an offset by one. For a stable copy of one collection, `export_collection` returns its rows in a single call.
- **Pace yourself.** Every page is one call against the token's per-minute budget. See [Rate limits](https://modulify.ai/docs/api/rate-limits).

## Next

- [Requests and responses](https://modulify.ai/docs/api/requests-and-responses) for where `count` lives.
- [All methods](https://modulify.ai/docs/api/methods) to find a method's page size.