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

# Sivutus

> Käy läpi Living Security Public API -luettelot käyttämällä luettelovastausrakennetta, limit-parametria ja läpinäkyviä page_token-kursoreita.

Jokainen `/v1`-luettelopäätepiste palauttaa saman vastausrakenteen ja sivuttaa
tulokset samalla tavalla: tokenipohjaisesti ja vain eteenpäin.

## Luettelon vastausrakenne

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

<ResponseField name="data" type="array">
  Resurssien sivu vakaassa, deterministisessä järjestyksessä.
</ResponseField>

<ResponseField name="next_page_token" type="string | null">
  Välitä tämä arvona `page_token` seuraavassa pyynnössä, jotta saat seuraavan sivun.
  `null`, kun tuloksia ei ole enää jäljellä.
</ResponseField>

<ResponseField name="has_more" type="boolean">
  Kertoo, onko tämän sivun jälkeen vielä lisää tuloksia.
</ResponseField>

## Pyyntöparametrit

<ParamField query="limit" type="integer" default="25">
  Sivun koko. Kokonaisluku väliltä 1–500. Tämän alueen ulkopuoliset arvot
  palauttavat virheen `400` koodilla `invalid_request`.
</ParamField>

<ParamField query="page_token" type="string">
  Edellisen vastauksen arvo `next_page_token`. Jätä pois ensimmäisen sivun yhteydessä.
</ParamField>

Siirtymäparametreja (offset), paluuta edelliselle sivulle tai tarkkoja
kokonaismääriä ei ole. Jos haluat laskea kokoelman alkiot, käy kokoelma läpi.

<Note>
  **Poikkeus:** `GET /v1/cohorts/{id}/members` palauttaa enintään 50 riviä sivua kohden, vaikka
  pyytäisit suuremman arvon `limit`. `has_more` ja
  `next_page_token` kuvaavat silti Kohortin todellista jäljellä olevaa osuutta.
</Note>

Todennus onnistuu kummalla tahansa Basic-tunnistetietomuodolla – katso
[Pikaopas](/fi/developers/v1/quickstart) (`apikey:secret` tai `secret:`).
Aseta `BASE` organisaation alueelliseksi isännäksi – katso
[Alueet](/fi/developers/v1/regions).

## Luettelon läpikäynti

Lataa `LIVING_SECURITY_API_KEY` salaisuuksienhallinnastasi pikaoppaan tavoin.
Tämä esimerkki edellyttää `entity:read`. Ensimmäisen pyynnön oppaassa käytetty avain
saattaa tarvita tämän lisäoikeuden. Asenna `requests` Pythonia varten tai `jq`
komentoriviesimerkkiä varten. Molemmat esimerkit kiinnittävät version, pitävät
suodattimet muuttumattomina ja hylkäävät uudelleenohjaukset. Ne pysähtyvät
virheisiin ja 1 000 sivun jälkeen sen sijaan, että raportoisivat osittaisen
läpikäynnin valmiiksi. Pythonin lukuaikakatkaisu rajaa toimettomuusaikaa, ei
työn kokonaiskestoa; aseta integraatiossasi työlle määräaika.

Pyydä sivuja, kunnes `next_page_token` on `null`. Käytä mieluiten arvoa `limit=500` massalukuihin,
jolloin kokoelma katetaan pienemmällä pyyntömäärällä – katso
[käyttörajat](/fi/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>

## Pidä suodattimet muuttumattomina sivutuksen aikana

Sivutokenit ovat läpinäkyviä, allekirjoitettuja kursoreita. Jokainen token on
sidottu organisaatioon, todennettuun palvelutiliin, rajapintasopimuksen
versioon ja täsmälleen sen pyynnön suodatinjoukkoon, joka sen tuotti. Jos
muutat mitä tahansa näistä kesken läpikäynnin – esimerkiksi lisäät
`type`-suodattimen toisella sivulla – saat vastauksena `400` koodilla
`invalid_request` parametrissa `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
}
```

Jos haluat muuttaa suodattimia, aloita uusi läpikäynti ensimmäiseltä sivulta.

<Tip>
  Käsittele arvoa `page_token` läpinäkyvänä merkkijonona: välitä se takaisin täsmälleen
  sellaisena kuin sait sen. Älä pura, säilytä pitkäaikaisesti tai muodosta
  tokeneita itse – niiden sisäinen muoto voi muuttua milloin tahansa ilman
  ilmoitusta.
</Tip>

<Note>
  Luettelopäätepisteet hylkäävät tukemattomat kyselyparametrit vastauksella
  `400` `invalid_request`, jossa parametri mainitaan nimeltä. Tarkista tuetut suodattimet
  kunkin päätepisteen viitedokumentaatiosta.
</Note>

## Keskeytyneen tai muuttuvan kokoelman käsittely

Nämä esimerkit eivät yritä virheitä automaattisesti uudelleen. Noudata
virheen `429` yhteydessä otsaketta `Retry-After` ja seuraa rajattua
[uudelleenyrityksiä koskevaa ohjeistusta](/fi/developers/v1/rate-limits). Käytä
lukua uudelleen yritettäessä samaa aluetta, palvelutiliä, versiota ja
suodattimia sekä viimeisintä onnistuneesti käsiteltyä sivutokenia.
Tunnistetietojen vaihtaminen ei myönnä käyttöoikeutta: jokaisessa pyynnössä
tarkistetaan edelleen voimassa olevat käyttöoikeudet.

Usean sivun luku ei ole kokoelman tilannekuva. Rivit voivat muuttua
läpikäynnin aikana. Käsittele resursseja niiden vakaan tunnisteen `id`
perusteella ja tee jatkokirjoituksista idempotentteja; uudelleenkäynnistys voi
palauttaa saman rivin uudelleen. Jos token hylätään, tarkista virhe `page_token` ja
korjaa pyyntö ennen kuin aloitat alusta ensimmäiseltä sivulta. Älä hylkää
aiemmin käsiteltyjä tietoja huomaamatta äläkä merkitse keskeytynyttä vientiä
valmiiksi. Jos tarvitset tarkat raportoinnin kokonaismäärät, käytä
asianmukaista raportointiresurssia sen sijaan, että pitäisit live-luettelon
läpikäyntiä tilannekuvana.


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