# Pagination

> Page through Emailit API list endpoints with page and limit, read next_page_url, handle the per_page and offset formats and the email and event time windows.

List endpoints return results one page at a time. Most of them use page numbers with `page` and `limit`, templates and automations use `page` and `per_page`, and DMARC endpoints use `limit` and `offset`. This page explains each format, the time windows on the email and event lists, and how to loop through every page.

## Page and limit

Most list endpoints accept two query parameters:

| Parameter | Description |
| --- | --- |
| `page` | The page to return, starting at `1`. Default `1`. |
| `limit` | Objects per page, from `1` to `100`. Values outside that range return a `400` validation error. |

The default page size depends on the endpoint:

| Default `limit` | Endpoints |
| --- | --- |
| 10 | Domains, API keys, audiences, contacts, campaigns, forms, suppressions, webhooks, email verification lists |
| 25 | Emails, subscribers |
| 50 | Email verification list results |
| 100 | Events |

[List subscribers](/docs/api-reference/audiences/subscribers/list/) also accepts `per_page` as an alias for `limit`.

### Response

```json
{
  "data": [
    { "object": "domain", "id": "dom_4K468YrjOkR1wwdhqiO0G9XEUey", "name": "acme.com" }
  ],
  "next_page_url": "/v2/domains?page=3&limit=10",
  "previous_page_url": "/v2/domains?page=1&limit=10"
}
```

| Field | Description |
| --- | --- |
| `data` | The objects on this page, newest first unless you [sort](/docs/api-reference/filtering/) them. |
| `next_page_url` | Path of the next page, or `null` on the last page. |
| `previous_page_url` | Path of the previous page, or `null` on the first page. |

The page URLs are paths, not full URLs, and on most endpoints they only carry `page` and `limit`, not your filters. To fetch the next page, repeat your own request with `page` increased by one, and stop when `next_page_url` is `null`.

Some lists add fields next to `data`:

| Field | Endpoints | Contains |
| --- | --- | --- |
| `total_records` | Contacts, audiences, campaigns, forms | The number of objects that match, across all pages. |
| `usage` | Webhooks | `used` and `limit` webhook endpoints for your plan, and `filters_allowed`. |
| `usage` | Subscribers | `used` and `limit` subscribers for the audience, and the `plan`. |
| `domain_limit`, `domain_count`, `plan_name` | Domains | How many sending domains your plan allows (`null` means no limit), how many you have, and the plan name. |

## Page and per_page

[List templates](/docs/api-reference/templates/list/), [List automations](/docs/api-reference/automations/list/) and [List runs](/docs/api-reference/automations/runs/) use `per_page` instead of `limit` and return page counts instead of page URLs:

| Parameter | Description |
| --- | --- |
| `page` | The page to return, starting at `1`. Default `1`. |
| `per_page` | Objects per page, from `1` to `100`. Default `25`. |

```json
{
  "data": [],
  "total_records": 42,
  "per_page": 25,
  "current_page": 2,
  "total_pages": 2
}
```

Keep requesting pages while `current_page` is less than `total_pages`.

## Limit and offset

The DMARC endpoints skip a number of rows instead of counting pages:

| Parameter | Description |
| --- | --- |
| `limit` | Rows to return. Reports: default `25`, maximum `100`. Sources, countries, networks and reporters: default `50`, maximum `200`. |
| `offset` | Rows to skip. Default `0`. |

[List aggregate reports](/docs/api-reference/dmarc/list/) and [List forensic reports](/docs/api-reference/dmarc/forensic/) return the total count, so you know when to stop:

```json
{
  "data": [],
  "meta": { "total": 130, "limit": 25, "offset": 50 }
}
```

The breakdown endpoints return `meta` with `limit` and `offset` only. Stop when a page has fewer rows than `limit`.

## Time windows

Two lists only look at recent data unless you ask for more:

- **Emails.** [List emails](/docs/api-reference/emails/list/) returns emails from the last 14 days by default. Pass `date_from` (and optionally `date_to`) as a date such as `2026-08-01` to search further back. `created_at` filters don't widen the window, so use `date_from`.
- **Events.** [List events](/docs/api-reference/events/list/) returns events from the last two days by default. Any `created_at` filter, such as `created_at.after=2026-09-01`, replaces that window. The list also stops at an offset of 2,500: a page that starts beyond the 2,500th event returns `422` with the code `events_offset_too_large`. Narrow the results with `type` or `created_at` filters to reach older events.

## Fetch every page

Loop until the API says there's no next page. These examples collect every contact in an audience:

**cURL**

```bash
page=1
while : ; do
  response=$(curl -s -G https://api.emailit.com/v2/contacts \
    -H "Authorization: Bearer $EMAILIT_API_KEY" \
    --data-urlencode "audience_id=aud_4KbFmQPy1feMCGAY8F8pAhiZ1u2" \
    --data-urlencode "limit=100" \
    --data-urlencode "page=$page")

  echo "$response" | jq -c '.data[]'

  [ "$(echo "$response" | jq -r '.next_page_url')" = "null" ] && break
  page=$((page + 1))
done
```

**Node.js**

```javascript
async function* listAll(path, params = {}) {
  for (let page = 1; ; page++) {
    const query = new URLSearchParams({ ...params, limit: '100', page: String(page) });
    const response = await fetch(`https://api.emailit.com/v2${path}?${query}`, {
      headers: { Authorization: `Bearer ${process.env.EMAILIT_API_KEY}` },
    });
    if (!response.ok) throw new Error(`Emailit ${response.status}`);

    const body = await response.json();
    yield* body.data;
    if (!body.next_page_url) return;
  }
}

for await (const contact of listAll('/contacts', { audience_id: 'aud_4KbFmQPy1feMCGAY8F8pAhiZ1u2' })) {
  console.log(contact.email);
}
```

**Python**

```python
import os
import requests

def list_all(path, params=None):
    session = requests.Session()
    session.headers["Authorization"] = f"Bearer {os.environ['EMAILIT_API_KEY']}"
    page = 1
    while True:
        response = session.get(
            f"https://api.emailit.com/v2{path}",
            params={**(params or {}), "limit": 100, "page": page},
            timeout=30,
        )
        response.raise_for_status()
        body = response.json()
        yield from body["data"]
        if not body["next_page_url"]:
            return
        page += 1

for contact in list_all("/contacts", {"audience_id": "aud_4KbFmQPy1feMCGAY8F8pAhiZ1u2"}):
    print(contact["email"])
```

## Consistent results

Pages are computed when you request them. If objects are created or deleted while you page through a newest-first list, objects can move between pages, so you might see one twice or miss one. For a stable export of a list that keeps growing, sort oldest first with `order=created_at&direction=asc` (or `sort=created_at&order=asc` on contacts and suppressions), and skip IDs you've already seen.

## Related

  - [Filtering and sorting](/docs/api-reference/filtering/): Narrow and order list results.
  - [Rate limits](/docs/api-reference/rate-limits/): Keep bulk reads reasonable.

---
Source: https://emailit.com/docs/api-reference/pagination/
