# Google CSE 兼容参考

> 在 2027 年 1 月 1 日前迁移基础的 Google CSE 网页搜索。通过 Search1API 的兼容端点保留 cse().list() 调用和 items 解析；请检查 cx、分页和过滤方面的限制。

Canonical URL: https://s1.dev/zh/docs/compatibility/google

Google 的 Custom Search JSON API 已不再接受新客户，现有客户必须在 **2027 年 1 月 1 日**前完成迁移。请参阅 [Google 官方 API 公告](https://developers.google.com/custom-search/v1/overview)和 [Programmable Search Engine 公告](https://programmablesearchengine.googleblog.com/2026/01/updates-to-our-web-search-products.html)。

Search1API 为**第一页网页搜索**提供兼容端点。它使用 Search1API 的 Google 引擎，返回现有 CSE 客户端读取的 `items[].title`、`link` 和 `snippet` 字段。它不会调用即将停用的 Custom Search JSON API。

将 `https://www.googleapis.com/customsearch/v1` 替换为：

```text
https://api.search1api.com/compat/google/customsearch/v1
```

在 `key` 查询参数中发送 **Search1API 密钥**，替代 Google API 密钥。也可以使用 `Authorization: Bearer YOUR_API_KEY`。凭证相互冲突时返回 `400`。响应使用 `Cache-Control: no-store`，Search1API 会在其应用日志中遮蔽查询字符串里的密钥。

> **warn:** 这里的 `cx` 是可选的，只作为不透明的兼容值接受。它不会加载 Google 中保存的站点、排除项、细化条件、推广或排序规则。没有显式过滤条件时，请求会搜索整个网络。如果你的旧引擎只搜索一组站点，请在切换前重建这些限制。单个站点或 URL 前缀可使用 `siteSearch`；多个 `include_sites` 和 `exclude_sites` 请使用原生 [Search API](https://s1.dev/zh/docs/basic/search)。

迁移概览和配置变更请参阅[从 Google CSE 迁移](https://s1.dev/compatible-apis/google-cse)。

## 配置客户端

<Tabs items={['Google Python 客户端', 'curl']}>

<Tab value="Google Python 客户端">

官方 `google-api-python-client` 支持通过 `client_options` 覆盖 API 端点。下面的基础 URL 以 `/compat/google/` 结尾，**包括末尾的斜杠**；SDK 会自行追加 `customsearch/v1`。

```python
import os
from googleapiclient.discovery import build

with build(
    "customsearch",
    "v1",
    developerKey=os.environ["SEARCH1API_API_KEY"],
    client_options={
        "api_endpoint": "https://api.search1api.com/compat/google/",
    },
) as service:
    response = service.cse().list(
        q="hono cloudflare workers",
        cx="YOUR_OLD_ENGINE_ID",
        num=5,
    ).execute()

    for item in response.get("items", []):
        print(item["title"], item["link"], item["snippet"])
```

已使用 `google-api-python-client` **2.201.0** 针对本地网关（上游服务为桩）进行测试。验证了搜索调用、显式站点过滤和 `HttpError` 处理；测试不衡量真实环境下的排序或延迟。

</Tab>

<Tab value="curl">

```bash
curl --get 'https://api.search1api.com/compat/google/customsearch/v1' \
  --data-urlencode 'q=hono cloudflare workers' \
  --data-urlencode 'num=5' \
  -H "Authorization: Bearer $SEARCH1API_API_KEY"
```

已经在发送 `key` 的 HTTP 客户端可以继续这样做，只需换成 Search1API 密钥。请勿泄露包含凭证的 URL。

</Tab>

</Tabs>

硬编码 Google URL 的客户端需要覆盖端点或修改代码。如果客户端会丢弃 URL 路径，仅有名为 `base_url` 的设置是不够的。自行构建 Google 客户端的框架封装可能需要显式配置该客户端；这些封装尚未在此测试。不涵盖 Google 的嵌入式 Search Element JavaScript 和旧的 `/siterestrict` 端点。

## 支持的参数

| 参数 | 行为 |
| --- | --- |
| `q` | 必填，非空的搜索查询。 |
| `key` | Search1API 密钥，而非 Google 密钥。提供 Bearer 密钥时可省略。 |
| `cx` | 可选，不透明值，会在 `queries.request` 中原样返回；不会导入 Google 配置。会列在 `X-Search1API-Ignored` 中。 |
| `num` | `1` 到 `10` 的整数，默认 `10`。无效值返回 `400`。 |
| `start` | 省略或设为 `1`。其他任何值都会在搜索和扣除积分之前返回 `400`。 |
| `siteSearch` | 单个站点或 URL 前缀，会转换为 `site:` 过滤条件。默认为包含。 |
| `siteSearchFilter` | `i` 表示包含、`e` 表示排除 `siteSearch`；需要同时提供 `siteSearch`。 |
| `lr` | 单个 `lang_XX` 或三字母语言代码会映射为原生语言提示。布尔组合和地区形式会被忽略。Search1API 的语言语义可能与 CSE 的限制不同。 |
| `dateRestrict` | 只有 `d1`、`w1`、`m1`、`y1` 会映射为原生的日、周、月和年时间窗口。其他值会被忽略，不会近似到其他时间段。 |
| `searchType` | 省略或使用 `web`。图片搜索返回 `400`；原生搜索结果附带的图片并不是 CSE 图片搜索。 |
| `safe` | 省略或使用 `off`。`active` 返回 `400`，因为不提供 CSE 的 SafeSearch 过滤。 |
| `alt` | 省略或使用 `json`。JSONP `callback` 和其他输出格式返回 `400`。 |

其他参数会被忽略，包括 `hl`、`gl`、`cr`、`sort`、`exactTerms`、`excludeTerms`、`fileType`、`rights`、`filter`、`fields` 和图片过滤参数。`hl` 是界面语言，不会被视为结果语言限制。如果你的应用依赖其中某个选项，请在迁移前评估差异。

`X-Search1API-Ignored` 列出被忽略选项的**名称**，从不包含值，形式为有长度上限、以逗号分隔的百分号编码名称列表。它最多报告 50 个名称；编码后超过 120 个字符的名称会变为 `option_name_truncated`。评估请求时请检查这个响应头；只要请求中提供了 `cx`，就会将其列入。这个响应头并不表示旧引擎的配置已被应用。

## 响应与分页

成功的响应包含 `kind: "customsearch#search"`、`queries.request`、以秒为单位的 `searchInformation.searchTime`，以及 `items` 数组。每个条目包含 `kind: "customsearch#result"`、`title`、`link`、`displayLink`（主机名）和 `snippet`。

没有结果的成功搜索返回 `items: []`。搜索结果和排序可能与 CSE 不同。HTML 高亮、`pagemap`、推广、细化条件、拼写纠正和搜索总数估计会直接省略，不会伪造。

**只提供第一页。** 不返回 `queries.nextPage` 和 `queries.previousPage`。分页循环以及使用 `searchInformation.totalResults` 的应用需要调整；不要因为基础结果格式一致就认为与 CSE 完全兼容。

## 积分与错误

一次完成的搜索消耗 **1 积分**（包括确认无结果的搜索），并与原生 API 共用账户的速率限制。技术原因导致的搜索失败不计费。不会抓取页面内容。请参阅[积分与限制](https://s1.dev/zh/docs/essentials/credits-and-limits)。

错误使用 Google 的响应结构：`{ "error": { "code": 400, "message": "…", "errors": [{ "domain": "global", "reason": "invalid", "message": "…" }] } }`。HTTP 状态码仍沿用 Search1API 的定义：`400` 表示行为无效或不可用，`401` 表示密钥无效，`402` 表示积分不足，`403` 表示禁止访问，`429` 表示触发速率限制。`429` 会保留原生的 `Retry-After` 响应头。错误原因遵循 Google 格式；`402` 仍是 Search1API 的积分余额状态，而不是 Google 的每日配额。

## 迁移检查清单

1. 确认你使用的是 Custom Search JSON API，而不是嵌入式 Search Element。
2. 重建之前保存在 `cx` 背后的站点限制。
3. 替换端点和密钥；保留受支持的 `cse().list()` 调用和 `items` 解析。
4. 从该端点移除分页和图片搜索调用，并检查被忽略的过滤条件和不可用的元数据。
5. 在切换生产流量之前，运行有代表性的查询并验证错误处理。