> ## Documentation Index
> Fetch the complete documentation index at: https://docs.brelio.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Pagination and filters

> Every list is paginated with an opaque cursor: pass next_cursor back as cursor while has_more is true.

Every list answers the same shape:

```json theme={null}
{
  "data": [ … ],
  "next_cursor": "eyJvIjoyfQ",
  "has_more": true
}
```

| Parameter | Meaning |
| - | - |
| `limit` | Page size, 1 to 100. Default 25. |
| `cursor` | The `next_cursor` of the previous page. Omit it for the first page. |

`next_cursor` is `null` on the last page. The cursor is opaque: do not build or decode it. A cursor Brelio did not issue answers `422 OPERATION_INPUT_INVALID` with `params.issues[0].path` = `"cursor"`.

## Read every page

```bash theme={null}
curl "https://api.brelio.app/v1/patients?limit=2" \
  -H "Authorization: Bearer $BRELIO_API_KEY"
# → { "data": [ …2 patients… ], "next_cursor": "eyJvIjoyfQ", "has_more": true }

curl "https://api.brelio.app/v1/patients?limit=2&cursor=eyJvIjoyfQ" \
  -H "Authorization: Bearer $BRELIO_API_KEY"
# → { "data": [ …1 patient… ], "next_cursor": null, "has_more": false }
```

```javascript Node.js theme={null}
async function* listAll(path, params = {}) {
  let cursor
  do {
    const query = new URLSearchParams({ ...params, limit: '100', ...(cursor ? { cursor } : {}) })
    const response = await fetch(`https://api.brelio.app/v1${path}?${query}`, {
      headers: { Authorization: `Bearer ${process.env.BRELIO_API_KEY}` },
    })
    if (!response.ok) throw new Error(JSON.stringify(await response.json()))
    const page = await response.json()
    yield* page.data
    cursor = page.has_more ? page.next_cursor : undefined
  } while (cursor)
}

for await (const professional of listAll('/professionals', { status: 'active' })) {
  console.log(professional.firstName, professional.lastName)
}
```

```python Python theme={null}
import os
import requests

def list_all(path, **params):
    cursor = None
    while True:
        query = {**params, "limit": 100, **({"cursor": cursor} if cursor else {})}
        response = requests.get(
            f"https://api.brelio.app/v1{path}",
            params=query,
            headers={"Authorization": f"Bearer {os.environ['BRELIO_API_KEY']}"},
            timeout=10,
        )
        response.raise_for_status()
        page = response.json()
        yield from page["data"]
        if not page["has_more"]:
            return
        cursor = page["next_cursor"]

for professional in list_all("/professionals", status="active"):
    print(professional["firstName"], professional["lastName"])
```

## Filters

Filters are query parameters. They accept the same field names and values as the tables in Brelio's web app. Each list documents its own in the [API reference](https://docs.brelio.com/en/reference/operations.md):

| List | Filters |
| - | - |
| `GET /v1/patients` | `search` (name, email, phone, patient code, internal code or ID number, like the web search), `status` (`active`, `inactive`, `discharged`) |
| `GET /v1/professionals` | `status` (`pending`, `active`, `inactive`) |
| `GET /v1/services` | `search`, `status` (`active`, `inactive`) |
| `GET /v1/agenda` | `from`, `to` (required, at most 31 days apart), `professionalId`, `centerId` |

Patients are sorted by last name. Archived patients do not appear in lists.

Pages of patient records count toward the hourly [patient-record ceiling](https://docs.brelio.com/en/concepts/rate-limits.md) by the records they return.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.