> ## 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.

# Pagination

> Parcourez les listes de l'API publique de Living Security à l'aide de l'enveloppe de liste, du paramètre limit et des curseurs opaques page_token.

Chaque point de terminaison de liste `/v1` renvoie la même enveloppe et pagine de la même manière : par jeton, en avant seulement.

## L'enveloppe de liste

```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">
  Toujours `"list"`.
</ResponseField>

<ResponseField name="data" type="array">
  La page de ressources, dans un ordre stable et déterministe.
</ResponseField>

<ResponseField name="next_page_token" type="string | null">
  Transmettez cette valeur comme `page_token` dans la requête suivante pour obtenir la page suivante.
  `null` lorsqu'il n'y a plus de résultats.
</ResponseField>

<ResponseField name="has_more" type="boolean">
  Indique si d'autres résultats existent au-delà de cette page.
</ResponseField>

## Paramètres de requête

<ParamField query="limit" type="integer" default="25">
  Taille de la page. Un entier compris entre 1 et 500. Les valeurs hors de cette plage renvoient une
  `400` avec le code `invalid_request`.
</ParamField>

<ParamField query="page_token" type="string">
  La valeur `next_page_token` de la réponse précédente. Omettez-la pour la première page.
</ParamField>

Il n'existe aucun paramètre de décalage, aucun parcours vers la page précédente et aucun total exact. Pour dénombrer une collection, il faut la parcourir.

<Note>
  **Exception :** `GET /v1/cohorts/{id}/members` renvoie au plus 50 lignes par
  page, même si vous demandez une valeur `limit` supérieure. `has_more` et
  `next_page_token` reflètent toujours le reste réel de la cohorte.
</Note>

Authentifiez-vous avec l'une ou l'autre des formes d'identifiants Basic — consultez le
[Guide de démarrage rapide](/fr-CA/developers/v1/quickstart) (`apikey:secret` ou `secret:`).
Définissez `BASE` selon l'hôte régional de l'organisation — consultez
[Régions](/fr-CA/developers/v1/regions).

## Parcourir une liste

Chargez `LIVING_SECURITY_API_KEY` depuis votre gestionnaire de secrets, comme dans le guide de démarrage rapide.
Cet exemple nécessite `entity:read`; la clé utilisée pour le guide de la première requête peut
avoir besoin de cette autorisation supplémentaire. Installez `requests` pour Python ou `jq` pour l'exemple
en shell. Les deux exemples épinglent une version, conservent les filtres inchangés et rejettent les
redirections. Ils s'arrêtent en cas d'erreur et après 1 000 pages plutôt que de présenter un
parcours partiel comme complet. Le délai de lecture de Python limite l'inactivité, et non la durée totale
de la tâche; imposez une échéance à la tâche dans votre intégration.

Demandez des pages jusqu'à ce que `next_page_token` soit `null`. Privilégiez `limit=500` pour les lectures
en masse afin de couvrir la collection en moins de requêtes — consultez les
[limites de débit](/fr-CA/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>

## Conserver des filtres stables pendant la pagination

Les jetons de page sont des curseurs opaques et signés. Chaque jeton est lié à l'organisation, au compte de service authentifié, à la version du contrat et à l'ensemble exact de filtres de la requête qui l'a produit. Modifier l'un de ces éléments en cours de parcours — par exemple, ajouter un filtre `type` à la deuxième page — renvoie une `400` avec le code
`invalid_request` sur le paramètre `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
}
```

Pour modifier les filtres, recommencez un nouveau parcours depuis la première page.

<Tip>
  Traitez `page_token` comme une chaîne opaque : retransmettez-la exactement telle que vous l'avez reçue.
  Ne la décodez pas, ne la conservez pas à long terme et ne construisez pas de jetons — leur format interne
  peut changer à tout moment sans préavis.
</Tip>

<Note>
  Les points de terminaison de liste rejettent les paramètres de requête qu'ils ne prennent pas en charge avec un `400`
  `invalid_request` nommant le paramètre. Consultez la référence de chaque point de terminaison pour connaître
  ses filtres pris en charge.
</Note>

## Gérer une collection interrompue ou en évolution

Ces exemples ne relancent pas automatiquement les erreurs. Pour `429`, respectez `Retry-After`
et suivez les [directives de nouvelle tentative](/fr-CA/developers/v1/rate-limits) bornées. Conservez la
même région, le même compte de service, la même version, les mêmes filtres et le dernier jeton de page
traité avec succès lors de la nouvelle tentative d'une lecture. Changer d'identifiants n'accorde pas d'accès :
chaque requête vérifie toujours les autorisations actuelles.

Une lecture sur plusieurs pages n'est pas un instantané de la collection. Les lignes peuvent changer pendant
que vous la parcourez. Traitez les ressources selon leur `id` stable et rendez les écritures en aval
idempotentes; un redémarrage peut renvoyer de nouveau une ligne. Si un jeton est rejeté,
examinez l'erreur `page_token` et corrigez la requête avant de recommencer à la
première page. N'écartez pas silencieusement des données déjà traitées et ne marquez pas une
exportation interrompue comme terminée. Pour obtenir des totaux de rapport exacts, utilisez la ressource de rapport appropriée plutôt que de considérer un parcours de liste en direct comme un instantané.


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