# Pagination

> How cursor-based pagination works across MailBlastr list endpoints: limit, after, before, and has_more.

Several MailBlastr list endpoints support **cursor-based pagination** so you can browse large datasets reliably. Because the cursor is an object `id` rather than an offset, paging stays stable even when records are created or deleted while you are still requesting pages.

## Response shape

Every paginated response is a `list` object with these fields:

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `object` | string | No | Always `list` for a paginated response. |
| `has_more` | boolean | No | Whether more items exist beyond the current page. |
| `data` | array | No | The items for the current page. |

```json
{
  "object": "list",
  "has_more": true,
  "data": [
    { "id": "4ef9a417-02e9-4d39-ad75-9611e0fcc33c", "subject": "Hello World" },
    { "id": "3a9f8c2b-1e5d-4f8a-9c7b-2d6e5f8a9c7b", "subject": "Welcome aboard" }
  ]
}
```

## Parameters

Navigate the results with these query parameters. Use an object's `id` as the cursor — the cursor itself is always **excluded** from the returned page.

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `limit` | number | No | Items to return per page. Default `20`, maximum `100`, minimum `1`. |
| `after` | string | No | Return the page that **starts after** the object with this id (the next page). Use the id of the last item on the current page. |
| `before` | string | No | Return the page that **ends before** the object with this id (the previous page). Use the id of the first item on the current page. |

> **Warning:** You can use either `after` or `before`, but not both in the same request.

## Optional vs. always-paginated

For older list endpoints the `limit` parameter is **optional** — omit it and all items are returned in a single response:

- [List Received Emails](https://www.mailblastr.com/docs/api/emails-received-list) — `GET /emails/receiving`
- [List Received Attachments](https://www.mailblastr.com/docs/api/emails-received-list-attachments) — `GET /emails/receiving/:id/attachments`
- [List Attachments](https://www.mailblastr.com/docs/api/emails-list-attachments) — `GET /emails/:id/attachments`

Newer list endpoints are **always paginated** (they return at most `limit`, default `20`, items per page):

- [List Sent Emails](https://www.mailblastr.com/docs/api/emails-list) — `GET /emails`
- [List Logs](https://www.mailblastr.com/docs/api/logs-list) — `GET /logs`

## Forward pagination

To page forward (newer to older), take the `id` of the **last item** on the current page and pass it as `after`. Stop when `has_more` is `false`.

**cURL**

```bash
# First page
curl -X GET 'https://api.mailblastr.com/emails?limit=50' \
  -H 'Authorization: Bearer mb_xxxxxxxxx'

# Next page — pass the id of the last item from the previous page
curl -X GET 'https://api.mailblastr.com/emails?limit=50&after=LAST_ID_FROM_PREVIOUS_PAGE' \
  -H 'Authorization: Bearer mb_xxxxxxxxx'
```

**Node.js**

```js
import { MailBlastr } from 'mailblastr';

const mb = new MailBlastr('mb_xxxxxxxxx');

// First page
const { data: firstPage } = await mb.emails.list({ limit: 50 });

// Next page (if there is one) — pass the id of the last item as `after`
if (firstPage.has_more) {
  const lastId = firstPage.data[firstPage.data.length - 1].id;
  const { data: secondPage } = await mb.emails.list({ limit: 50, after: lastId });
}
```

**Python**

```python
import requests

auth = {"Authorization": "Bearer mb_xxxxxxxxx"}

# First page
first_page = requests.get("https://api.mailblastr.com/emails", params={"limit": 50}, headers=auth).json()

# Next page (if there is one)
if first_page["has_more"]:
    last_id = first_page["data"][-1]["id"]
    second_page = requests.get(
        "https://api.mailblastr.com/emails",
        params={"limit": 50, "after": last_id},
        headers=auth,
    ).json()
```

## Backward pagination

To page backward (older to newer), take the `id` of the **first item** on the current page and pass it as `before`.

```bash
curl -X GET 'https://api.mailblastr.com/emails?limit=50&before=FIRST_ID_FROM_CURRENT_PAGE' \
  -H 'Authorization: Bearer mb_xxxxxxxxx'
```

## Best practices

- Always check `has_more` before fetching another page — it tells you when you have reached the end.
- Pick a `limit` that fits your workload: small pages for interactive UIs, larger pages (up to 100) for bulk processing.
- Mind [rate limits](https://www.mailblastr.com/docs/api/introduction) when paging through large datasets — add backoff between pages if needed.

## Errors

Pagination requests return `validation_error` (422) if the cursor format is invalid, if `limit` is outside 1–100, or if both `before` and `after` are supplied. See the [error reference](https://www.mailblastr.com/docs/api/errors).
