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

# 分页

> 通过列表信封、limit 参数和不透明的 page_token 游标遍历 Living Security Public API 列表。

每个 `/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 之间的整数。超出该范围的值将返回带有代码 `invalid_request` 的 `400`。
</ParamField>

<ParamField query="page_token" type="string">
  上一次响应中的 `next_page_token`。获取第一页时请省略该参数。
</ParamField>

不提供偏移量参数，不支持向前一页回溯，也不提供精确的总数。如需统计集合的数量，请遍历该集合。

<Note>
  \*\*例外情况：\*\*即使您请求了更大的 `limit`，`GET /v1/cohorts/{id}/members` 每页最多也只返回 50 行。`has_more` 和 `next_page_token` 仍会反映该群组的实际剩余数量。
</Note>

可使用任一种 Basic 凭据形式进行身份验证，请参阅[快速入门](/zh-Hans/developers/v1/quickstart)（`apikey:secret` 或 `secret:`）。
请将 `BASE` 设置为组织所在区域的主机，请参阅[区域](/zh-Hans/developers/v1/regions)。

## 遍历列表

按照快速入门中的方式，从您的密钥管理器中加载 `LIVING_SECURITY_API_KEY`。
此示例需要 `entity:read`；用于首次请求指南的密钥可能需要获得该额外授权。Python 示例请安装 `requests`，Shell 示例请安装 `jq`。两个示例都固定了版本，保持筛选条件不变，并拒绝重定向。它们会在出错时以及达到 1,000 页后停止，而不会把不完整的遍历报告为已完成。Python 的读取超时限制的是无活动时间，而不是作业总时长；请在您的集成中设置作业截止时间。

持续请求各页，直到 `next_page_token` 为 `null`。进行批量读取时，建议使用 `limit=500`，以便用更少的请求覆盖整个集合，请参阅[速率限制](/zh-Hans/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`，并参照有限度的[重试指南](/zh-Hans/developers/v1/rate-limits)。重试读取请求时，请保持相同的区域、服务账户、版本、筛选条件以及最近一次成功处理的页面令牌。更换凭据并不会授予访问权限：每个请求仍会检查当前权限。

多页读取并非集合的快照。在您遍历期间，行数据可能发生变化。请根据稳定的 `id` 处理资源，并确保下游写入具有幂等性；重新启动后可能会再次返回同一行。如果令牌被拒绝，请检查 `page_token` 错误并更正请求，然后再从第一页重新开始。请勿静默丢弃已处理的数据，也不要将中断的导出标记为已完成。如需精确的报表总数，请使用相应的报表资源，而不要将实时列表遍历视为快照。


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