<!-- Source: https://useworkloom.com/dev/pagination/ · Workloom API v1 -->

# Pagination

List endpoints return pages. Each page gives you a cursor for the next one, so a record added while you page through never makes you skip or repeat one. Records come oldest first, or in the order you ask for with `sort`.

| Parameter | What it does |
| --- | --- |
| `limit` | Records per page, 1 to 100. Default 25. |
| `cursor` | The `next_cursor` from the previous page. Leave it out for the first page. |
| `sort` | Contacts, companies and deals: one field, with `-` in front for newest or largest first. See [Sorting](https://useworkloom.com/dev/pagination/#sorting). |
| `updated_since` | Contacts only: records changed at or after this time (ISO 8601). |

## The list shape

**Response**

```json
{
  "data": [
    "…up to limit records…"
  ],
  "has_more": true,
  "next_cursor": "66f2a1c0e4b0a1b2c3d4e5f6"
}
```

200 OK

On the last page `has_more` is false and there is no `next_cursor`.

Without `sort`, records come in the order they were created in, and a cursor is the id of the last record on the page. A cursor never expires.

## Sorting

Lists and searches of contacts, companies and deals take `sort`: one field, oldest or smallest first, or with `-` in front for newest or largest first. Searches take it in the body. Records with the same value keep the order they were created in, and records with no value come first ascending and last descending.

| Records | Sort on |
| --- | --- |
| Contacts | `created_at`, `updated_at` |
| Companies | `created_at`, `updated_at`, `employee_count` |
| Deals | `created_at`, `updated_at`, `amount`, `expected_close_date`, `closed_at` |

**Biggest open deals first**

```bash
curl "https://api.useworkloom.com/api/public/v1/deals?status=open&sort=-amount&limit=10" \
  -H "Authorization: Bearer $WORKLOOM_KEY"
```

```javascript
const res = await fetch("https://api.useworkloom.com/api/public/v1/deals?status=open&sort=-amount&limit=10", {
  headers: { Authorization: `Bearer ${process.env.WORKLOOM_KEY}` },
});
const { data } = await res.json();
```

```python
import os, requests

res = requests.get(
    "https://api.useworkloom.com/api/public/v1/deals",
    headers={"Authorization": f"Bearer {os.environ['WORKLOOM_KEY']}"},
    params={"status": "open", "sort": "-amount", "limit": 10},
)
deals = res.json()["data"]
```

Keep the same `sort` while you follow `next_cursor`. A sorted cursor holds the last record’s value and id, so it only works with the sort that made it; one used with another sort is refused with `invalid_request`. Only dates and numbers sort. Sort names on your side.

**Read every contact**

```javascript
let cursor = null;
const all = [];
do {
  const qs = new URLSearchParams({ limit: "100", ...(cursor && { cursor }) });
  const res = await fetch(`https://api.useworkloom.com/api/public/v1/contacts?${qs}`, {
    headers: { Authorization: `Bearer ${process.env.WORKLOOM_KEY}` },
  });
  const page = await res.json();
  all.push(...page.data);
  cursor = page.has_more ? page.next_cursor : null;
} while (cursor);
```

```python
import os, requests

url = "https://api.useworkloom.com/api/public/v1/contacts"
headers = {"Authorization": f"Bearer {os.environ['WORKLOOM_KEY']}"}
params = {"limit": 100}
contacts = []
while True:
    page = requests.get(url, headers=headers, params=params).json()
    contacts += page["data"]
    if not page["has_more"]:
        break
    params["cursor"] = page["next_cursor"]
```

> For a nightly contact sync, store the time you started and pass it as `updated_since` next time, so you only fetch what changed.
