
# 错误与分页

本页面涵盖了每个集成都需要处理的两件事：失败请求的表现形式，以及如何对返回列表的端点进行分页。

---

## 错误封装

当请求失败时，响应始终是具有相同结构的 JSON — 一个设置为 `false` 的 `success` 标志、一条人类可读的 `error` 消息，以及一个与 HTTP 状态码相匹配的数字 `error_code`：

```json
{
  "success": false,
  "error": "Invalid cursor",
  "error_code": 400
}
```

由于 `success` 和 `error_code` 始终存在，如果您愿意，可以根据它们进行分支处理，而无需检查原始 HTTP 状态码。成功的响应始终包含 `success: true`。

---

## 状态码

| Status | `error_code` | Meaning | What to do |
|---|---|---|---|
| `200` | — | Success | Read the response data. |
| `201` | — | Resource created | Save the returned ID (e.g. `campaign_id`, `contactId`). |
| `400` | `400` | Bad request | A parameter is missing or invalid. Read the `error` message and fix the request. |
| `401` | `401` | Unauthorized | Your API key is missing or invalid. Check the key and how you are sending it — see [Authentication](authentication.md). |
| `403` | `403` | Forbidden | Your plan does not include API access. See [API Access](../integrations/api-access.md) or contact [<span data-t="supportEmail">hi@youraiconnector.com</span>](mailto:hi@youraiconnector.com). |
| `404` | `404` | Not found | The resource (e.g. a contact, campaign, or task ID) does not exist on your account. |
| `409` | `409` | Conflict | The resource already exists — for example, creating a contact whose phone number is already on your account. |
| `429` | `429` | Rate limited | You have exceeded 300 requests per minute (or the wider 1,200/minute account ceiling). Back off and retry shortly. |
| `500` | `500` | Server error | Something went wrong on our side. Retry after a short wait; email [<span data-t="supportEmail">hi@youraiconnector.com</span>](mailto:hi@youraiconnector.com) if it persists. |

以下是这些错误在实际应用中的几个示例：

```json
{
  "success": false,
  "error_code": 401,
  "error": "Invalid API key"
}
```

```json
{
  "success": false,
  "error": "A contact with this phone number already exists",
  "error_code": 409
}
```

```json
{
  "success": false,
  "error_code": 429,
  "error": "Rate limit exceeded. Please try again later."
}
```

### 妥善处理错误

- **在读取数据之前检查 `success`（或状态码）。** 不要假设响应主体一定包含您预期的字段。
- **对于 `429` 和 `500`，请在短暂延迟后重试** — 等待片刻，然后再次尝试。不要重试 `400`、`401`、`403`、`404` 或 `409`；在您更改请求之前，这些错误将持续存在。
- **阅读 `error` 消息。** 它通常会准确告诉您哪个字段有误。

---

## 分页

列表端点（例如 `GET /contacts`、`GET /campaigns` 和 `GET /tasks`）以分页形式返回结果，因此单次调用无需加载您的整个账户数据。分页使用不透明的游标（cursor）。

两个查询参数对其进行控制：

| 参数 | 描述 |
|---|---|
| `limit` | 每页返回的项目数量。默认值因端点而异（通常为 50）；最大值为 **100**。 |
| `cursor` | 指向下一页的不透明指针。第一页请留空。 |

每一页的响应中都包含一个 `next_cursor` 字段：

- 如果 `next_cursor` 是一个字符串，则表示还有更多结果——请在下一次请求中将其作为 `cursor` 传入。
- 如果 `next_cursor` 为 `null`，则说明已到达最后一页。请停止请求。

单页联系人列表如下所示：

```json
{
  "success": true,
  "contacts": [
    { "id": "abc123", "first_name": "Jane", "phone_number": "+15551234567" },
    { "id": "def456", "first_name": "John", "phone_number": "+15557654321" }
  ],
  "next_cursor": "eyJsYXN0IjoiZGVmNDU2In0"
}
```

::: note
**注意：** 游标（cursor）是不透明的——请勿尝试解析、构建或修改它。仅可将从先前响应中收到的 `next_cursor` 值传回。
:::


---

## 遍历所有联系人

要获取完整列表，请从无游标开始，并持续调用，直到 `next_cursor` 返回 `null` 为止。

**cURL**

此示例手动演示了前两页的遍历过程。运行第一次调用，将响应中的 `next_cursor` 复制到 `CURSOR` 中，然后运行第二次调用。重复此过程，直到 `next_cursor` 为 `null`。

```bash
# First page
curl "https://api.youraiconnector.com/v1/contacts?apiKey=YOUR_API_KEY&limit=100"

# Next page — paste the next_cursor from the previous response
CURSOR="eyJsYXN0IjoiZGVmNDU2In0"
curl "https://api.youraiconnector.com/v1/contacts?apiKey=YOUR_API_KEY&limit=100&cursor=$CURSOR"
```

**JavaScript**

```javascript
async function getAllContacts() {
  const all = [];
  let cursor = null;

  do {
    const url = new URL("https://api.youraiconnector.com/v1/contacts");
    url.searchParams.set("limit", "100");
    if (cursor) url.searchParams.set("cursor", cursor);

    const res = await fetch(url, {
      headers: { "X-API-Key": "YOUR_API_KEY" },
    });
    const data = await res.json();

    if (!data.success) throw new Error(data.error);

    all.push(...data.contacts);
    cursor = data.next_cursor;
  } while (cursor);

  return all;
}
```

**Python**

```python
import requests

def get_all_contacts():
    all_contacts = []
    cursor = None

    while True:
        params = {"limit": 100}
        if cursor:
            params["cursor"] = cursor

        res = requests.get(
            "https://api.youraiconnector.com/v1/contacts",
            params=params,
            headers={"X-API-Key": "YOUR_API_KEY"},
        )
        data = res.json()

        if not data["success"]:
            raise Exception(data["error"])

        all_contacts.extend(data["contacts"])
        cursor = data["next_cursor"]

        if not cursor:
            break

    return all_contacts
```

同样的循环适用于任何分页端点——只需更改路径以及您从响应中读取的字段（`campaigns`、`tasks` 等）。

---

## 后续步骤

- [身份验证](authentication.md) —— 发送密钥的四种方式。
- [联系人](contacts.md) —— 上述示例中使用的完整联系人端点。
- [API 密钥](api-keys.md) —— 查看您的实时速率限制使用情况，以避免 `429`。
