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

# Pagination

> Walk through long lists with cursors and filter offers by status.

## How it works

The two list endpoints return one page at a time:

* `GET /offers`
* `GET /offers/{id}/events`

Each list response contains `next_cursor`:

```json theme={null}
{
  "data": [ ... ],
  "next_cursor": "eyJjIjoi..."
}
```

If `next_cursor` is not `null`, there is another page. Pass the value as the `cursor` query parameter to get it. On the last page `next_cursor` is `null`. A list without items returns `"data": []` and `"next_cursor": null`.

The cursor is an opaque string. Pass it back unchanged and do not build or edit it. Use a cursor only with the list it came from. A cursor that the API cannot read returns `400`.

## Parameters

<ParamField query="limit" type="integer">
  Page size. A whole number from 1 to 100 for offers and from 1 to 200 for events.
</ParamField>

<ParamField query="cursor" type="string">
  The `next_cursor` of the previous page. 1 to 512 characters.
</ParamField>

| Endpoint | `limit` range | Default |
| - | - | - |
| `GET /offers` | 1 to 100 | 25 |
| `GET /offers/{id}/events` | 1 to 200 | 50 |

`limit` takes digits only (up to four). Values such as `0`, `-1`, `1e2` or `abc` return `400` with `"param": "limit"`. A value above the maximum also returns `400`. The API does not reduce it to the maximum.

Unknown query parameters are ignored. If you send a parameter twice, the first value is used.

## Ordering

| Endpoint | Order |
| - | - |
| `GET /offers` | Newest first, by `created_at` (descending), then by `id` (descending) |
| `GET /offers/{id}/events` | Oldest first, by `sequence` (ascending) |

## Filters

`GET /offers` accepts one filter:

<ParamField query="status" type="string">
  Only offers with this status. One of `draft`, `sent`, `accepted`, `declined`, `expired`, `withdrawn`.
</ParamField>

The filter uses the same value as the `status` field of an offer. A sent offer whose `expires_at` has passed has the status `expired`, so `status=sent` does not return it.

Send the same `status` on every page of a walk. `GET /offers/{id}/events` has no filter.

An unknown `status` value returns `400` with `"param": "status"`.

## Invalid cursor

A cursor that the API cannot read, an empty cursor and a cursor longer than 512 characters return `400`:

```json theme={null}
{
  "error": {
    "code": "invalid_request",
    "message": "Invalid value for query parameter \"cursor\".",
    "param": "cursor"
  }
}
```

## Walk through all pages

This script reads every offer with the status `sent`, 100 per page. It needs `jq`.

```bash theme={null}
BASE="https://app.hocdoc.com/api/v1"
CURSOR=""

while true; do
  if [ -z "$CURSOR" ]; then
    URL="$BASE/offers?status=sent&limit=100"
  else
    URL="$BASE/offers?status=sent&limit=100&cursor=$CURSOR"
  fi

  PAGE=$(curl -s "$URL" -H "Authorization: Bearer $HOCDOC_API_KEY")
  echo "$PAGE" | jq -c '.data[]'

  CURSOR=$(echo "$PAGE" | jq -r '.next_cursor // empty')
  [ -z "$CURSOR" ] && break
done
```

For events, use `/offers/{id}/events` and the parameters `limit` and `cursor` in the same way.

Each request counts against your [rate limits](/api/rate-limits).


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