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

# Пагінація

> Обхід списків у Living Security Public API за допомогою обгортки списку, параметра limit та непрозорих курсорів page_token.

Кожна кінцева точка списку `/v1` повертає однакову обгортку й використовує однакову
пагінацію: на основі токенів, лише вперед.

## Обгортка списку

```json theme={null}
{
  "object": "list",
  "data": [
    {
      "id": "ent_8f3d2c1b0a9e",
      "object": "entity",
      "type": "person",
      "status": "active",
      "display_name": "Dana Rivera",
      "email": "dana.rivera@example.com",
      "created_at": "2025-11-03T09:21:44.000Z",
      "updated_at": "2026-07-28T16:02:10.000Z"
    }
  ],
  "next_page_token": "eyJvcmdhbml6YXRpb2...",
  "has_more": true
}
```

<ResponseField name="object" type="string">
  Завжди `"list"`.
</ResponseField>

<ResponseField name="data" type="array">
  Сторінка ресурсів у стабільному, детермінованому порядку.
</ResponseField>

<ResponseField name="next_page_token" type="string | null">
  Передайте це значення як `page_token` у наступному запиті, щоб отримати наступну сторінку.
  `null`, якщо результатів більше немає.
</ResponseField>

<ResponseField name="has_more" type="boolean">
  Чи існують результати за межами цієї сторінки.
</ResponseField>

## Параметри запиту

<ParamField query="limit" type="integer" default="25">
  Розмір сторінки. Ціле число від 1 до 500. Значення поза цим діапазоном повертають
  `400` із кодом `invalid_request`.
</ParamField>

<ParamField query="page_token" type="string">
  `next_page_token` з попередньої відповіді. Для першої сторінки пропустіть його.
</ParamField>

Параметрів зсуву (offset), повернення до попередньої сторінки та точної
загальної кількості немає. Щоб підрахувати елементи колекції, обійдіть її повністю.

<Note>
  **Виняток:** `GET /v1/cohorts/{id}/members` повертає щонайбільше 50 рядків на
  сторінку, навіть якщо ви запитуєте більше значення `limit`. `has_more` і
  `next_page_token` усе одно відображають справжній залишок Cohort.
</Note>

Виконуйте автентифікацію за допомогою будь-якого з двох типів облікових даних Basic — див.
[Quickstart](/uk/developers/v1/quickstart) (`apikey:secret` або `secret:`).
Установіть для `BASE` регіональний хост організації — див.
[Regions](/uk/developers/v1/regions).

## Обхід списку

Завантажте `LIVING_SECURITY_API_KEY` зі свого сховища секретів, як у кроках швидкого старту.
Для цього прикладу потрібен `entity:read`; ключу, який використовувався в посібнику з першого запиту,
може знадобитися цей додатковий дозвіл. Установіть `requests` для Python або `jq` для прикладу
в оболонці. В обох прикладах зафіксовано версію, фільтри залишаються незмінними, а
перенаправлення відхиляються. Вони зупиняються у разі помилок і після 1 000 сторінок, а не
повідомляють про неповний обхід як про завершений. Тайм-аут читання в Python обмежує
час бездіяльності, а не загальну тривалість завдання; установіть у своїй інтеграції
граничний термін виконання завдання.

Запитуйте сторінки, доки `next_page_token` не матиме значення `null`. Для масового
читання віддавайте перевагу `limit=500`, щоб охопити колекцію меншою кількістю запитів — див.
[rate limits](/uk/developers/v1/rate-limits).

<CodeGroup>
  ```bash cURL + jq theme={null}
  set -euo pipefail
  # US: BASE="https://api.us-east-1.livingsecurity.ai/v1"
  BASE="https://api.eu-west-1.livingsecurity.ai/v1"
  : "${LIVING_SECURITY_API_KEY:?Set LIVING_SECURITY_API_KEY}"
  TOKEN=""

  # Bound each run. Increase this page budget deliberately for larger collections.
  for ((page_number=1; page_number<=1000; page_number++)); do
    PARAMS=(--data-urlencode "limit=500" --data-urlencode "type=person")
    if [ -n "$TOKEN" ]; then
      PARAMS+=(--data-urlencode "page_token=$TOKEN")
    fi
    RESULT=$(curl --silent --show-error --fail-with-body \
      --connect-timeout 5 --max-time 30 --get \
      -u "apikey:$LIVING_SECURITY_API_KEY" \
      -H "Living-Security-Version: 2026-07-01" \
      --write-out '\n%{http_code}' "${PARAMS[@]}" "$BASE/entities")
    STATUS="${RESULT##*$'\n'}"
    PAGE="${RESULT%$'\n'*}"
    [ "$STATUS" = "200" ] || { echo "Unexpected HTTP status: $STATUS" >&2; exit 1; }
    jq -e 'has("next_page_token") and .object == "list" and (.data | type == "array") and
      ((.has_more == false and .next_page_token == null) or
       (.has_more == true and (.next_page_token | type == "string" and length > 0)))' \
      <<< "$PAGE" > /dev/null
    NEXT=$(jq -r '.next_page_token // empty' <<< "$PAGE")
    [ -z "$NEXT" ] || [ "$NEXT" != "$TOKEN" ] || { echo "Repeated page token" >&2; exit 1; }
    jq -r '.data[].id' <<< "$PAGE"
    [ -n "$NEXT" ] || exit 0
    TOKEN="$NEXT"
  done
  echo "Page budget exhausted; the collection is incomplete" >&2
  exit 1
  ```

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

  # US: BASE = "https://api.us-east-1.livingsecurity.ai/v1"
  BASE = "https://api.eu-west-1.livingsecurity.ai/v1"
  AUTH = ("apikey", os.environ["LIVING_SECURITY_API_KEY"])

  def iter_entities(*, entity_type="person", max_pages=1000):
      if type(max_pages) is not int or max_pages < 1:
          raise ValueError("max_pages must be a positive integer")
      params = {"limit": 500, "type": entity_type}
      for _ in range(max_pages):
          response = requests.get(
              f"{BASE}/entities",
              auth=AUTH,
              params=params,
              headers={"Living-Security-Version": "2026-07-01"},
              timeout=(5, 30),
              allow_redirects=False,
          )
          try:
              response.raise_for_status()
              if response.status_code != 200:
                  raise RuntimeError("Unexpected response; check your regional API URL")
              page = response.json()
          finally:
              response.close()
          if (not isinstance(page, dict) or page.get("object") != "list"
                  or not isinstance(page.get("data"), list)
                  or type(page.get("has_more")) is not bool
                  or "next_page_token" not in page):
              raise RuntimeError("Invalid list response")
          token = page["next_page_token"]
          if ((page["has_more"] and (not isinstance(token, str) or not token))
                  or (not page["has_more"] and token is not None)):
              raise RuntimeError("Inconsistent list response")
          if token is not None and token == params.get("page_token"):
              raise RuntimeError("Repeated page token")
          yield from page["data"]
          if token is None:
              return
          params = {**params, "page_token": token}
      raise RuntimeError("Page budget exhausted; the collection is incomplete")

  if __name__ == "__main__":
      for entity in iter_entities():
          print(entity["id"])
  ```
</CodeGroup>

## Зберігайте фільтри стабільними під час пагінації

Токени сторінок — це непрозорі підписані курсори. Кожен токен прив’язаний до
організації, автентифікованого службового облікового запису, версії контракту та
точного набору фільтрів запиту, що його створив. Зміна будь-якого з цих елементів під час обходу — наприклад,
додавання фільтра `type` на другій сторінці — повертає `400` із кодом
`invalid_request` для параметра `page_token`:

```json 400 Invalid request theme={null}
{
  "type": "https://docs.livingsecurity.com/developers/v1/errors/invalid_request",
  "title": "Invalid request",
  "status": 400,
  "detail": "page_token does not match this request's organization, credential, contract version, or filters.",
  "code": "invalid_request",
  "param": "page_token",
  "request_id": "req_...",
  "doc_url": "https://docs.livingsecurity.com/developers/v1/errors/invalid_request",
  "retryable": false
}
```

Щоб змінити фільтри, почніть новий обхід із першої сторінки.

<Tip>
  Розглядайте `page_token` як непрозорий рядок: передавайте його назад точно в тому вигляді, в якому його отримано.
  Не декодуйте, не зберігайте тривалий час і не конструюйте токени — їхній внутрішній формат
  може змінитися в будь-який момент без попередження.
</Tip>

<Note>
  Кінцеві точки списків відхиляють непідтримувані параметри запиту, повертаючи `400`
  `invalid_request` із назвою параметра. Підтримувані фільтри дивіться в довідці
  кожної кінцевої точки.
</Note>

## Обробка перерваної або змінюваної колекції

Ці приклади не повторюють запити автоматично у разі помилок. Для `429` дотримуйтеся `Retry-After`
і [настанов щодо обмежених повторів](/uk/developers/v1/rate-limits). Під час повторення читання зберігайте
той самий регіон, службовий обліковий запис, версію, фільтри та токен
останньої успішно опрацьованої сторінки. Зміна облікових даних не надає доступу:
кожен запит і надалі перевіряє поточні дозволи.

Читання кількох сторінок не є знімком колекції. Рядки можуть змінюватися, поки
ви її обходите. Обробляйте ресурси за їхнім стабільним `id` і робіть подальші
записи ідемпотентними; після перезапуску рядок може повернутися знову. Якщо токен відхилено,
перегляньте помилку `page_token` і виправте запит, перш ніж перезапускати з
першої сторінки. Не відкидайте мовчки раніше оброблені дані та не позначайте
перерваний експорт як завершений. Для точних підсумків звітності використовуйте відповідний
ресурс звітності замість того, щоб розглядати обхід живого списку як знімок.


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