# Pagination

> Several pagination styles coexist in the Emelia API. Which route uses which, where pages start at 0 or at 1, and a loop for each.

Source: https://docs.emelia.io/docs/pagination

There is no single convention: check the route page. This table covers every paginated route.

| Routes                                                                                                      | First page | Page size                                | Response                                           |
| ----------------------------------------------------------------------------------------------------------- | ---------- | ---------------------------------------- | -------------------------------------------------- |
| [Lists](https://docs.emelia.io/docs/lists.md): `GET /lists/list`                                                                     | `page=1`   | `limit`, 25 by default, 100 at most      | `items`, `total`, `page`, `pageSize`, `totalLeads` |
| [Contacts of a list](https://docs.emelia.io/docs/lists.md): `GET /lists/list/{listId}/rows`                                          | `page=1`   | `pageSize`, 50 by default, 100 at most   | `items`, `total`, `page`, `pageSize`               |
| [Contacts of a campaign](https://docs.emelia.io/docs/campaigns.md): `GET /advanced/campaign/contacts`                                | `page=0`   | `perPage`, 20 by default, 10,000 at most | `list`, `count`                                    |
| [Campaign activities](https://docs.emelia.io/docs/replies.md): `GET /advanced/campaigns/{campaignId}/activities`                     | `page=0`   | 30, fixed                                | `activities`                                       |
| Contacts of a step: `GET /advanced/campaigns/{campaignId}/progress/contacts`                                | `page=1`   | 20, fixed                                | `contacts`, `total`, `totalPages`                  |
| [Enrichment history](https://docs.emelia.io/docs/enrichment.md): `GET /tools/find/email`, `/tools/find/phone`, `/tools/verify/email` | `page=1`   | `limit`, 15 by default, 100 at most      | `data`, `total`                                    |
| [Credit history](https://docs.emelia.io/docs/credits.md): `GET /credits/usage`                                                       | `page=1`   | `limit`, 50 by default, 200 at most      | `items`, `total`                                   |
| [LinkedIn scrapers](https://docs.emelia.io/docs/linkedin-scraper.md): `GET /linkedin-scrappers`                                      | `page=0`   | 20, fixed                                | `scraps`                                           |

Not paginated: the campaigns (`GET /advanced/campaigns`), the email providers and the warmups. They return everything in one response.

## Lists and contacts

Read every contact of a list, 100 at a time:

[`GET /lists/list/{listId}/rows`](https://docs.emelia.io/docs/lists/list-or-find-contacts.md)

**curl**

```bash
curl "https://api.emelia.io/lists/list/66f1c0ffee5a7d3b2c4e8a10/rows?page=1&pageSize=100" \
  -H "Authorization: $EMELIA_API_KEY"
```

**JavaScript**

```js
async function* allContacts(listId) {
  for (let page = 1; ; page++) {
    const url = `https://api.emelia.io/lists/list/${listId}/rows?page=${page}&pageSize=100`;
    const res = await fetch(url, { headers: { Authorization: process.env.EMELIA_API_KEY } });
    const { items, total } = await res.json();
    yield* items;
    if (items.length === 0 || page * 100 >= total) return;
  }
}

for await (const contact of allContacts('66f1c0ffee5a7d3b2c4e8a10')) console.log(contact.email);
```

**Python**

```python
import os
import requests

session = requests.Session()
session.headers["Authorization"] = os.environ["EMELIA_API_KEY"]

def all_contacts(list_id):
    page = 1
    while True:
        res = session.get(
            f"https://api.emelia.io/lists/list/{list_id}/rows",
            params={"page": page, "pageSize": 100},
        ).json()
        yield from res["items"]
        if not res["items"] or page * 100 >= res["total"]:
            return
        page += 1

for contact in all_contacts("66f1c0ffee5a7d3b2c4e8a10"):
    print(contact.get("email"))
```

> **Two traps of the list routes.** `GET /lists/list` without `page` and `limit` returns a plain array of all your lists instead of the envelope: send both parameters to get a stable shape. And deep pages of a large list get slower, because each page sorts every row before it: page through with `pageSize=100` without restarting from page 1, or use the [CSV export](https://docs.emelia.io/docs/lists.md).

## Campaign activities

`GET /advanced/campaigns/{campaignId}/activities` returns 30 activities per page, newest first, and `page` starts at **0**. An empty `activities` array means you reached the end.

[`GET /advanced/campaigns/{campaignId}/activities`](https://docs.emelia.io/docs/replies/list-campaign-activities.md)

**curl**

```bash
curl "https://api.emelia.io/advanced/campaigns/6650f1a2b839d80011c5a0f1/activities?types=MAIL_REPLIED,LINKEDIN_REPLIED&page=0" \
  -H "Authorization: $EMELIA_API_KEY"
```

**JavaScript**

```js
const campaignId = '6650f1a2b839d80011c5a0f1';
for (let page = 0; ; page++) {
  const url = `https://api.emelia.io/advanced/campaigns/${campaignId}/activities?types=MAIL_REPLIED,LINKEDIN_REPLIED&page=${page}`;
  const res = await fetch(url, { headers: { Authorization: process.env.EMELIA_API_KEY } });
  const { activities } = await res.json();
  if (!activities?.length) break;
  for (const activity of activities) console.log(activity.event, activity.date);
}
```

**Python**

```python
campaign_id = "6650f1a2b839d80011c5a0f1"
page = 0
while True:
    res = session.get(
        f"https://api.emelia.io/advanced/campaigns/{campaign_id}/activities",
        params={"types": "MAIL_REPLIED,LINKEDIN_REPLIED", "page": page},
    ).json()
    if not res.get("activities"):
        break
    for activity in res["activities"]:
        print(activity["event"], activity["date"])
    page += 1
```

Activities are cached for up to 10 minutes. For a large campaign, the CSV export is faster than paging: `GET /advanced/campaigns/{campaignId}/export?type=replies` returns every reply in one file. See [Replies](https://docs.emelia.io/docs/replies.md).
