<!-- Carebit docs: Pagination and filtering -->

# Pagination and filtering

List endpoints in `/v1` return a JSON list envelope. Pagination fields
are at the top level alongside the `data` array.

```json
{
  "object": "list",
  "url": "https://api.carebit.co/v1/bookings",
  "data": [
    {
      "id": "92a3b4c5-d6e7-4f01-8234-56789abcdef0",
      "object": "booking",
      "...": "..."
    }
  ],
  "has_more": true,
  "next_cursor": "eyJpZCI6ImJrZ18uLi4ifQ"
}
```

## Requesting the next page

Send the value from `next_cursor` as the `cursor` query parameter on
the next request, together with the original filters. Diary Booking
queries require `start_time_from` and `start_time_to` on every page,
including `did_not_attend`. Recall-status lists omit that window; keep
`status` on every page instead. When there are no more results,
`has_more` is `false` and `next_cursor` is `null`.

<!-- code-tabs -->

### cURL

```bash
curl "https://api.carebit.co/v1/bookings?start_time_from=2026-08-01T00:00:00Z&start_time_to=2026-08-08T00:00:00Z&cursor=$NEXT_CURSOR" \
  -H "Authorization: Bearer $CAREBIT_ACCESS_TOKEN"
```

### JavaScript

```javascript
const params = new URLSearchParams({
  start_time_from: "2026-08-01T00:00:00Z",
  start_time_to: "2026-08-08T00:00:00Z",
  cursor: process.env.NEXT_CURSOR,
});
const response = await fetch(`https://api.carebit.co/v1/bookings?${params}`, {
  headers: {
    Authorization: `Bearer ${process.env.CAREBIT_ACCESS_TOKEN}`,
  },
});
const page = await response.json();
```

### Python

```python
import os
import requests

response = requests.get(
    "https://api.carebit.co/v1/bookings",
    headers={
        "Authorization": f"Bearer {os.environ['CAREBIT_ACCESS_TOKEN']}",
    },
    params={
        "start_time_from": "2026-08-01T00:00:00Z",
        "start_time_to": "2026-08-08T00:00:00Z",
        "cursor": os.environ["NEXT_CURSOR"],
    },
)
page = response.json()
```

### Ruby

```ruby
require "httparty"

response = HTTParty.get(
  "https://api.carebit.co/v1/bookings",
  headers: {
    "Authorization" => "Bearer #{ENV.fetch("CAREBIT_ACCESS_TOKEN")}"
  },
  query: {
    start_time_from: "2026-08-01T00:00:00Z",
    start_time_to: "2026-08-08T00:00:00Z",
    cursor: ENV.fetch("NEXT_CURSOR")
  }
)
page = response.parsed_response
```

### PHP

```php
$client = new GuzzleHttp\Client();

$response = $client->get("https://api.carebit.co/v1/bookings", [
    "headers" => [
        "Authorization" => "Bearer " . getenv("CAREBIT_ACCESS_TOKEN"),
    ],
    "query" => [
        "start_time_from" => "2026-08-01T00:00:00Z",
        "start_time_to" => "2026-08-08T00:00:00Z",
        "cursor" => getenv("NEXT_CURSOR"),
    ],
]);
$page = json_decode((string) $response->getBody(), true, flags: JSON_THROW_ON_ERROR);
```

<!-- /code-tabs -->

## Page size

Use `limit` to change the page size. The default is 25 and the maximum is 100. Requests with a larger `limit` receive a `422` validation error.

<!-- code-tabs -->

### cURL

```bash
curl "https://api.carebit.co/v1/bookings?start_time_from=2026-08-01T00:00:00Z&start_time_to=2026-08-08T00:00:00Z&limit=100" \
  -H "Authorization: Bearer $CAREBIT_ACCESS_TOKEN"
```

### JavaScript

```javascript
const params = new URLSearchParams({
  start_time_from: "2026-08-01T00:00:00Z",
  start_time_to: "2026-08-08T00:00:00Z",
  limit: "100",
});
const response = await fetch(`https://api.carebit.co/v1/bookings?${params}`, {
  headers: {
    Authorization: `Bearer ${process.env.CAREBIT_ACCESS_TOKEN}`,
  },
});
const page = await response.json();
```

### Python

```python
import os
import requests

response = requests.get(
    "https://api.carebit.co/v1/bookings",
    headers={
        "Authorization": f"Bearer {os.environ['CAREBIT_ACCESS_TOKEN']}",
    },
    params={
        "start_time_from": "2026-08-01T00:00:00Z",
        "start_time_to": "2026-08-08T00:00:00Z",
        "limit": 100,
    },
)
page = response.json()
```

### Ruby

```ruby
require "httparty"

response = HTTParty.get(
  "https://api.carebit.co/v1/bookings",
  headers: {
    "Authorization" => "Bearer #{ENV.fetch("CAREBIT_ACCESS_TOKEN")}"
  },
  query: {
    start_time_from: "2026-08-01T00:00:00Z",
    start_time_to: "2026-08-08T00:00:00Z",
    limit: 100
  }
)
page = response.parsed_response
```

### PHP

```php
$client = new GuzzleHttp\Client();

$response = $client->get("https://api.carebit.co/v1/bookings", [
    "headers" => [
        "Authorization" => "Bearer " . getenv("CAREBIT_ACCESS_TOKEN"),
    ],
    "query" => [
        "start_time_from" => "2026-08-01T00:00:00Z",
        "start_time_to" => "2026-08-08T00:00:00Z",
        "limit" => 100,
    ],
]);
$page = json_decode((string) $response->getBody(), true, flags: JSON_THROW_ON_ERROR);
```

<!-- /code-tabs -->

## Filtering

Every list endpoint documents its supported filters on the corresponding
[API reference](/api) page. Filters are query parameters. Date
filters are ISO-8601 timestamps in UTC. Enum filters use the values
documented by the corresponding API schema.

Combine filters with separate query parameters joined by `&`. Combining
filters narrows the result set because the filters are ANDed.

Recall Bookings (`awaiting_recall`, `overdue_for_recall`,
`recall_expired`, and `recall_canceled`) have no diary `start_time`.
List them by `status` without `start_time_from` or `start_time_to`.
Results are ordered by `recall_due_date`. `did_not_attend` still
requires the date window because those Bookings keep the missed
appointment time.

Lists (`GET /v1/lists`) accept an optional exact `name` filter,
case-insensitively. Results are ordered by name.

`GET /v1/organizations` searches other Carebit Organizations so you can
attach a PatientConnection, such as a GP practice. Provide `name` or
`postcode` (at least two characters). Optionally filter with
`organization_type`, for example `gp_practice`. The response uses the
Organization object. Email, subdomain, phone, currency, and time_zone
are null so a search cannot harvest contact details. This endpoint
does not create Organizations.

`GET /v1/invoices` and `GET /v1/payments` return every matching record
in the Organization. Filter invoices with `booking_id` or `patient_id`,
and payments with `patient_id`, `paid_at_from`, and `paid_at_to`.

<!-- code-tabs -->

### cURL

```bash
curl "https://api.carebit.co/v1/bookings?status=overdue_for_recall" \
  -H "Authorization: Bearer $CAREBIT_ACCESS_TOKEN"
```

### JavaScript

```javascript
const params = new URLSearchParams({
  status: "overdue_for_recall",
});
const response = await fetch(`https://api.carebit.co/v1/bookings?${params}`, {
  headers: {
    Authorization: `Bearer ${process.env.CAREBIT_ACCESS_TOKEN}`,
  },
});
const page = await response.json();
```

### Python

```python
import os
import requests

response = requests.get(
    "https://api.carebit.co/v1/bookings",
    headers={
        "Authorization": f"Bearer {os.environ['CAREBIT_ACCESS_TOKEN']}",
    },
    params={
        "status": "overdue_for_recall",
    },
)
page = response.json()
```

### Ruby

```ruby
require "httparty"

response = HTTParty.get(
  "https://api.carebit.co/v1/bookings",
  headers: {
    "Authorization" => "Bearer #{ENV.fetch("CAREBIT_ACCESS_TOKEN")}"
  },
  query: {
    status: "overdue_for_recall"
  }
)
page = response.parsed_response
```

### PHP

```php
$client = new GuzzleHttp\Client();

$response = $client->get("https://api.carebit.co/v1/bookings", [
    "headers" => [
        "Authorization" => "Bearer " . getenv("CAREBIT_ACCESS_TOKEN"),
    ],
    "query" => [
        "status" => "overdue_for_recall",
    ],
]);
$page = json_decode((string) $response->getBody(), true, flags: JSON_THROW_ON_ERROR);
```

<!-- /code-tabs -->

## Order

Every list endpoint documents its default sort. Unless stated otherwise,
results are ordered by `created_at` descending. Do not depend on an
implicit order across pages if you are filtering by a mutable field.
