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

## 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 récupérer 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 un
  `400` avec le code `invalid_request`.
</ParamField>

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

Il n'existe aucun paramètre de décalage (offset), aucun parcours vers la page précédente et aucun
total exact. Pour dénombrer une collection, parcourez-la.

<Note>
  **Exception :** `GET /v1/cohorts/{id}/members` renvoie au maximum 50 lignes par
  page, même si vous demandez une valeur `limit` plus élevée. `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
[Démarrage rapide](/fr/developers/v1/quickstart) (`apikey:secret` ou `secret:`).
Définissez `BASE` sur l'hôte régional de l'organisation — consultez
[Régions](/fr/developers/v1/regions).

## Parcourir une liste

Chargez `LIVING_SECURITY_API_KEY` depuis votre gestionnaire de secrets, comme dans le démarrage rapide.
Cet exemple nécessite `entity:read` ; la clé utilisée pour le guide de 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 d'expiration de lecture de Python borne l'inactivité, et non la durée
totale de la tâche ; imposez une échéance de 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/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 un `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, démarrez un nouveau parcours depuis la première page.

<Tip>
  Traitez `page_token` comme une chaîne opaque : renvoyez-la exactement telle que vous l'avez reçue.
  Ne la décodez pas, ne la stockez 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` indiquant le paramètre concerné. Consultez la référence de chaque point de terminaison pour
  connaître les filtres pris en charge.
</Note>

## Gérer une collection interrompue ou en cours de modification

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

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 à nouveau une ligne. Si un jeton est rejeté,
examinez l'erreur `page_token` et corrigez la requête avant de recommencer à partir de
la première page. Ne supprimez pas silencieusement les données déjà traitées et ne marquez pas un
export interrompu comme terminé. Pour obtenir des totaux de reporting exacts, utilisez la ressource de reporting 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.