# 错误处理

> 处理 Search1API 的认证、支付、校验、速率限制和上游服务错误。

Canonical URL: https://s1.dev/zh/docs/essentials/error-handling

所有 API 错误都是一个 JSON 对象，包含 `ok: false`、一个 `error` 标签，以及一条便于阅读的 `message`。校验错误还会包含 `errors` 数组。请根据状态码或 `error` 标签进行匹配，并把 `message` 展示给用户。

```json
{ "error": "Not Found", "message": "Task not found: task_123", "ok": false }
```

`POST /screenshot` 在成功时是个例外：它返回图片字节，Content-Type 为 `image/png`、`image/jpeg` 或 `image/webp`。它的错误响应仍然是 JSON，因此请先检查 `response.ok`，再决定如何读取响应体。

## 状态码

| 状态码 | 含义 | 处理方式 |
| --- | --- | --- |
| `400` | JSON 格式错误，或请求值无效（在 Schema 校验之外处理） | 修正请求体和请求头。 |
| `401` | Bearer 令牌无效、已吊销或格式错误 | 检查密钥和 `Authorization` 请求头。 |
| `402` | 支付质询，或账户积分不足 | 添加 Bearer 令牌、完成支付流程，或充值积分。 |
| `403` | 截图目标未通过 URL 安全检查或主机名解析检查 | 使用不含凭据的公开 HTTP 或 HTTPS URL。重试前确认其公网 DNS 能够解析。 |
| `404` | 未找到 deepcrawl 任务，或 `/crawl` 的目标服务器确认该 URL 不存在 | 对于 deepcrawl，再次轮询前先核对任务 ID。对于 `/crawl`，死链是经过验证的、已完成的结果（见下文），请修正 URL 而不是重试。`/search` 和 `/news` 不再返回 `404`，零结果会以 `200` 和空的 `results` 数组返回。 |
| `410` | 已停用的推理端点，或 `/crawl` 的目标服务器确认该 URL 已被永久移除 | 移除对 `/v1/chat/completions` 和 `/v1/models` 的调用。对于 `/crawl`，按已验证的死链处理。 |
| `422` | 请求体未通过 Schema 校验 | 阅读 `errors` 数组并修正其中指出的字段。 |
| `429` | 超出了该端点的单密钥速率限制 | 等待 `Retry-After` 指定的时长后再重试。 |
| `502` | 上游服务失败或超时 | 使用退避策略重试。 |
| `500` | 网关或服务出现意外错误 | 重试；如果持续出现，请联系支持。 |

## 理解 402 响应

付费端点可能因两种不同原因返回 `402`。

### 未提供 Bearer 令牌

Search1API 支持按请求付费的支付协议。未携带 Bearer 令牌的请求会收到类似下面的支付质询：

```json
{
  "type": "https://paymentauth.org/problems/payment-required",
  "title": "Payment Required",
  "status": 402,
  "detail": "Payment is required (Search1API - Search API)."
}
```

如果你本意是使用账户积分，请添加 `Authorization: Bearer YOUR_API_KEY`。如果携带了 Bearer 令牌但令牌无效，则会返回 `401`。

### 积分不足

当请求本身成功、但最终费用高于剩余余额时，计费系统会用 `402` 和积分不足的提示替换原本的成功响应。使用 `GET /usage` 查看余额，[充值积分](https://s1.dev/pricing#pay-as-you-go)，或在[仪表盘](https://app.s1.dev/)中开启自动充值。

## 修复校验错误

Search、News 和 Maps 的校验失败会返回 `422` 和 `errors` 数组：

```json
{
  "ok": false,
  "message": "Query cannot be empty",
  "errors": [
    { "field": "query", "message": "Query cannot be empty", "code": "too_small" }
  ]
}
```

常见原因包括 `query` 为空、`max_results` 小于 1，或 `crawl_results` 大于 `max_results`。完整的 Schema 请参阅对应端点的 API 参考。

## 零结果与服务失败

`/search` 和 `/news` 会区分“搜索没有找到结果”和“搜索无法执行”两种情况：

- **零结果**：当搜索完成且引擎确认没有匹配项时，响应为 `200`，`results` 数组为空。这是一次成功完成的搜索，会正常计费（1 积分）。可以放宽查询词，或去掉过窄的站点和时间过滤条件来获得匹配结果。
- **服务失败**：当搜索因我们这一侧的原因无法完成（上游服务中断、超时、反爬虫干扰）时，响应为 `502`，该请求**不**计费。请使用退避策略重试。

`/crawl` 对死链遵循同样的原则：

- **已验证的死链**：当目标服务器本身确认该 URL 不存在（`404`）或已被永久移除（`410`）时，抓取已完整执行，权威的结论就是“这里什么都没有”。响应会透传该状态码并附上说明信息，请求会正常计费（1 积分），与确认零结果的搜索一样。不要重试，请修正 URL。
- **服务失败**：当我们无法访问或处理目标页面（超时、上游服务中断、出口被拦截）时，响应为 `502`，该请求**不**计费。请使用退避策略重试。

## 安全地重试

Search、crawl 和 screenshot 请求没有写入类副作用。对于 `429`、`502`、`503`、`504` 和临时性的 `500` 响应，请使用带随机抖动的指数退避进行重试。对于 `400`、`401`、`402` 或 `422`，在修改请求或账户状态之前不要重试。不要重试 `/crawl` 返回的 `404` 或 `410`：目标服务器已经确认该 URL 失效，每次重试都是一次新的计费查询。对于 Screenshot 返回的 `403`，请先修正目标，或确认其公网 DNS 能够解析，再进行重试。

> **info:** 因服务无法完成而失败的请求不计费。有两种已完成的结果即使带有非 `200` 状态码或空载荷，也会计费：完成但零结果的搜索（`200`，`results` 数组为空），以及目标服务器确认 URL 已失效的 `/crawl`（`404`/`410`）。两者都是经过验证的结果，而不是失败。在批量请求中，成功的条目（包括确认零结果的搜索）会计费，失败条目的费用为零。

随时间变化的状态码语义（例如零结果搜索从 `404` 改为 `200`）都按日期记录在[更新日志](https://s1.dev/zh/docs/changelog)中。

如果 `500` 或 `502` 持续出现，请联系 [sys@search1api.com](mailto:sys@search1api.com)，并提供端点、时间戳和脱敏后的请求体。切勿发送你的 API 密钥。