# SERPdive API 参考

> 一个接口，一件事：`POST api.serpdive.com/v1/search` 接收一个问题，返回可直接用于回答的网页内容——已提取、已清洗，并按 LLM 的用量裁剪。本页就是 API 的全部：这里没有的，你也不需要。

- [API 调试台](https://serpdive.com/dashboard/playground)
- [控制台](https://serpdive.com/dashboard)
- [Python SDK](https://github.com/serpdive/serpdive-python)
- [TypeScript SDK](https://github.com/serpdive/serpdive-js)
- [MCP 服务器](https://serpdive.com/zh/docs#mcp)
- [OpenAPI 规范](https://serpdive.com/openapi.json)
- [llms.txt](https://serpdive.com/llms.txt)
- [联系我们](mailto:contact@serpdive.com)

## 快速上手

到[控制台](https://serpdive.com/dashboard/keys)获取一个 API 密钥（免费额度：每月 1,000 点，无需绑卡），安装对应语言的[官方 SDK](#sdks)，然后开始搜索：

```python
# pip install serpdive
from serpdive import SerpDive

client = SerpDive()  # reads SERPDIVE_API_KEY
response = client.search("what happened between Trump and FIFA", answer=True)

print(response.answer)
for result in response.results:
    print(result.url, result.date, result.content)
```

```typescript
// npm install serpdive
import { SerpDive } from "serpdive";

const client = new SerpDive(); // reads SERPDIVE_API_KEY
const response = await client.search("what happened between Trump and FIFA", {
  answer: true,
});

console.log(response.answer);
for (const result of response.results) {
  console.log(result.url, result.date, result.content);
}
```

想直接用 HTTPS？整个集成就是一次 POST，任何语言都可以：

```bash
curl -X POST https://api.serpdive.com/v1/search \
  -H "Authorization: Bearer $SERPDIVE_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "query": "what happened between Trump and FIFA",
        "model": "mako" }'
```

[调试台](https://serpdive.com/dashboard/playground)会用你自己的密钥生成这些代码片段，并在旁边展示真实的响应结果。

## 官方 SDK

两个官方客户端，刻意做得很薄：它们严格遵循 `/v1/search` 的契约，只增加类型和重试，别无其他。

| 语言 | 安装 | 源码 |
|---|---|---|
| Python 3.9+ | `pip install serpdive` | [serpdive/serpdive-python](https://github.com/serpdive/serpdive-python) |
| TypeScript / JavaScript | `npm install serpdive` | [serpdive/serpdive-js](https://github.com/serpdive/serpdive-js) |

两者都会从环境变量 `SERPDIVE_API_KEY` 读取密钥（也可以显式传入），返回**带类型的响应**，抛出**带类型的错误**（携带[错误表](#errors)中的稳定错误码），并对瞬时故障（`502`/`503`）**自动重试**——这始终是安全的，因为失败的搜索从不计费。

Python 客户端提供同步（`SerpDive`）与异步（`AsyncSerpDive`）两种形式。TypeScript 客户端零依赖，只要有 `fetch` 就能运行：Node 18+、Bun、Deno 以及各类边缘运行时。

## MCP 服务器

SERPdive 同时提供托管的 **MCP 服务器**，地址为 `https://mcp.serpdive.com`：只有一个工具 `serpdive_search`，让 Claude Code、Claude Desktop、Cursor 以及任何其他 MCP 客户端获得与 API 完全相同的、已提取且可直接回答的网页结果。无需安装，无需自行运行任何东西。

```bash
claude mcp add --transport http serpdive https://mcp.serpdive.com \
  --header "Authorization: Bearer sd_live_YOUR_KEY"
```

```json
{
  "mcpServers": {
    "serpdive": {
      "url": "https://mcp.serpdive.com/?key=sd_live_YOUR_KEY"
    }
  }
}
```

认证用的就是你平时的 API 密钥：既可以放在 `Authorization: Bearer` 请求头里，也可以对只接受 URL 的客户端，作为服务器地址上的 `?key=` 参数传入。通过 MCP 发起的搜索与 API 调用完全同价，消耗相同额度，受相同的[速率限制](#credits)约束。

## 认证

每个请求都在 `Authorization` 请求头中携带密钥：`Bearer sd_live_…`。密钥在 [API 密钥](https://serpdive.com/dashboard/keys)页面创建、查看和吊销；吊销立即生效。创建时可以为密钥设置**过期日期**（过期后不再通过认证，返回与已吊销密钥相同的 `401`），也可以为它单独设置**每月额度上限**——当你要把密钥部署到自己无法完全掌控的地方时，这是一道护栏。缺失、过期或未知的密钥会得到 `401`，错误码为 `missing_api_key` 或 `invalid_api_key`：搜索根本不会执行，也不会产生任何费用。

## LangChain

官方包：[`langchain-serpdive`](https://github.com/serpdive/langchain-serpdive)。一个工具类，同步异步皆可，基于 Python SDK 构建。

```python
# pip install langchain-serpdive
from langchain_serpdive import SerpdiveSearch

tool = SerpdiveSearch()  # reads SERPDIVE_API_KEY
tool.invoke({"query": "latest developments in solid state batteries"})

# or hand [SerpdiveSearch()] to any LangChain / LangGraph agent as its tools list
```

## LlamaIndex

官方包：[`llama-index-tools-serpdive`](https://github.com/serpdive/llama-index-tools-serpdive)。结果以 `Document` 形式返回（正文为提取出的网页内容，元数据为 url/title/date），可直接用于智能体或 RAG 流水线。

```python
# pip install llama-index-tools-serpdive
from llama_index_tools_serpdive import SerpdiveToolSpec

spec = SerpdiveToolSpec()  # reads SERPDIVE_API_KEY
documents = spec.serpdive_search("latest developments in solid state batteries")

# in an agent: tools=SerpdiveToolSpec().to_tool_list()
```

## Vercel AI SDK

TypeScript SDK 可以直接接入 [AI SDK](https://ai-sdk.dev) 的工具：由模型决定何时搜索，返回的内容已经是适合 LLM 的体量。

```typescript
// npm install ai serpdive zod
import { generateText, tool, stepCountIs } from "ai";
import { z } from "zod";
import { SerpDive } from "serpdive";

const serpdive = new SerpDive(); // reads SERPDIVE_API_KEY

const { text } = await generateText({
  model: "anthropic/claude-sonnet-5",
  prompt: "What changed in the EU AI Act this month?",
  stopWhen: stepCountIs(5),
  tools: {
    serpdive_search: tool({
      description:
        "Search the live web for current information. Returns extracted, " +
        "answer-ready page content (url, title, date, text), not links.",
      inputSchema: z.object({
        query: z.string().describe("The search, in any language"),
      }),
      execute: async ({ query }) => serpdive.search(query),
    }),
  },
});
```

## OpenAI 函数调用

不需要额外的包：声明函数，在模型调用时执行搜索，再把 JSON 结果回传即可。

```python
# pip install openai serpdive
import json
from openai import OpenAI
from serpdive import SerpDive

client, serpdive = OpenAI(), SerpDive()

tools = [{
    "type": "function",
    "name": "serpdive_search",
    "description": "Search the live web for current information. Returns "
                   "extracted, answer-ready page content, not links.",
    "parameters": {
        "type": "object",
        "properties": {"query": {"type": "string"}},
        "required": ["query"],
    },
}]

response = client.responses.create(
    model="gpt-5", input="What changed in the EU AI Act this month?", tools=tools
)
for item in response.output:
    if item.type == "function_call":
        result = serpdive.search(json.loads(item.arguments)["query"])
        # send result.raw back as the function_call_output, then re-run
```

## Anthropic 工具调用

在 Claude API 上是同样的思路——或者干脆跳过这些管道：Claude 客户端可以直接使用我们托管的 [MCP 服务器](#mcp)。

```python
# pip install anthropic serpdive
import anthropic
from serpdive import SerpDive

client, serpdive = anthropic.Anthropic(), SerpDive()

tools = [{
    "name": "serpdive_search",
    "description": "Search the live web for current information. Returns "
                   "extracted, answer-ready page content, not links.",
    "input_schema": {
        "type": "object",
        "properties": {"query": {"type": "string"}},
        "required": ["query"],
    },
}]

message = client.messages.create(
    model="claude-sonnet-5",
    max_tokens=1024,
    tools=tools,
    messages=[{"role": "user", "content": "What changed in the EU AI Act this month?"}],
)
for block in message.content:
    if block.type == "tool_use":
        result = serpdive.search(block.input["query"])
        # send result.raw back as a tool_result block, then re-run
```

## 请求

`POST /v1/search`，请求体为 JSON。四个参数，其余的我们替你决定：

| 参数 | 类型 | 说明 |
|---|---|---|
| `query` | string，必填 | 你想问的问题，怎么问就怎么写。最长 300 个字符；超出部分会被截断，而不是报错。 |
| `model` | `"krill"` \| `"mako"` \| `"moby"` | 检索深度。`krill`（免费，合理使用下不限量）返回最短的可用句子集合（约 700 tokens，来源更少），以低优先级处理，且不含 `answer` 字段；`mako`（默认）返回每个来源中承载事实的句子；`moby` 返回每个页面完整的可读正文。无法识别的取值会回退到 `mako`。 |
| `answer` | boolean | 按需生成的直接回答。设为 `true` 会附带一段基于来源写成的答案：Mako 上简明扼要，Moby 上更详尽并带 `[n]` 引用标注。价格已包含在内，绝不额外扣额度；预计会多花几百毫秒。Krill 从不生成答案——在它上面这个字段会被忽略。 |
| `max_results` | integer，1–10 | 对返回结果数量的硬上限，保留排名最靠前的那些。它会裁剪响应体——以及你下游的 token 账单——但**不会**让搜索更快：引擎始终完整读取一遍。不传时，引擎会返回它认为相关的全部结果。 |

**本地化是自动的。** 由查询语句的语言决定我们去哪里搜索：德语查询搜德语网页，日语查询搜日语网页，与调用方是谁、身在何处无关。没有国家参数可设。

**请把客户端超时设得宽裕些。** Mako 几秒内返回；Moby 要通读整页，遇到内容繁重的来源会明显更久。我们建议客户端超时设为 80 秒——我们自己的调试台就是这么设的。

## 响应

搜索成功时返回 `200`，结构永远如下，不会有别的：

```json
{
  "query": "what happened between Trump and FIFA",
  "model": "mako",
  "response_time_ms": 2641,
  "results": [
    {
      "url": "https://www.reuters.com/sports/soccer/…",
      "title": "Trump, FIFA's Infantino unveil …",
      "date": "2026-07-11",
      "content": "The announcement came during a joint press conference … [the fact-carrying sentences of the page]"
    },
    {
      "url": "https://www.bbc.com/sport/football/…",
      "title": "What the deal means for the 2026 World Cup",
      "content": "…"
    }
  ]
}
```

| 字段 | 类型 | 说明 |
|---|---|---|
| `query` | string | 原样回显你的查询。 |
| `model` | string | 实际作答的模型：`krill`、`mako` 或 `moby`。 |
| `response_time_ms` | number | 我们实际耗费的时间，单位毫秒。 |
| `answer` | string \| null | 写成文字的答案。仅当你传了 `answer: true` 时出现；若无法从来源中构建出答案，则为 `null`。 |
| `extra_info` | object | 结构化的直接答案块，仅当查询本身带有这类答案时出现：天气、汇率、词条释义、实时比分等。其 `type` 字段说明属于哪一类。 |
| `results` | array | 提取出的来源，最相关的在前。每一项包含 `url`、`title`、`content`（提取结果：Mako 上是句子，Moby 上是完整可读页面），已知发布日期时还有 `date`——始终是 ISO 格式 `YYYY-MM-DD`，未知时该字段直接不存在。 |

结果**只包含真实的页面提取内容**：读取失败的来源直接不出现，不会用摘要凑数，也没有占位符。URL 中的追踪参数（`utm_*`、点击 ID 等）会被清除，所以你引用的链接是干净的。

请求带上答案时是这样的：

```json
{ "query": "who won the 2026 Champions League final",
  "model": "moby",
  "answer": true }
```

## 错误

每次失败都会返回一个含两个字段的 JSON：`error` 是稳定的、可供程序识别的错误码，`message` 是一句给人看的说明，告诉你该怎么办：

```json
{
  "error": "invalid_api_key",
  "message": "This API key is invalid or was revoked. Manage your keys at https://serpdive.com/dashboard/keys"
}
```

| 状态码 | 错误码 | 含义 |
|---|---|---|
| 400 | `invalid_json` | 请求体不是合法的 JSON。 |
| 400 | `missing_query` | 请求体里没有 `query`。 |
| 401 | `missing_api_key` / `invalid_api_key` | `Authorization` 请求头中没有可用的密钥。 |
| 429 | `rate_limit_exceeded` | 超过每秒 5 次或每分钟 200 次。请遵守 `retry-after` 响应头并放慢速度。 |
| 429 | `monthly_quota_exceeded` | 本月额度已用尽（若使用按量付费，则是达到了你在账单页设置的消费上限）；额度会在你的月度周期续期时恢复（以注册日或账单日为准）。被拦下的调用不计费。 |
| 429 | `key_limit_exceeded` | 这个密钥达到了它创建时设定的每月额度上限；月度周期续期后恢复。其他密钥不受影响。被拦下的调用不计费。 |
| 429 | `too_many_concurrent_requests` | 该账户同时在途的搜索过多：Krill 为 1 个，Mako 与 Moby 为 5 个。这限制的是同时进行的工作量，而不是请求到达的频率——等其中一个返回后再重试即可。不计费。 |
| 502 | `search_failed` | 搜索未能完成。可以安全地重试。 |
| 503 | `server_busy` | 瞬时满载。`503` 会带上 `retry-after` 响应头：请遵守它，然后重试。 |

**失败的搜索从不计费。** 任何非 `200` 的响应都消耗 0 额度，无论原因为何。

## Krill、Mako 与 Moby

| 模型 | 费用 | 你会得到什么 |
|---|---|---|
| `krill` | 免费 | 免费档，合理使用下不限量：最短的可用集合（约 700 tokens，来源更少），不含写成文字的答案，同一时间只能跑一个搜索，以低优先级处理。当 token 预算比深度更重要时，用它。 |
| `mako` | 1 点额度 | 为智能体准备的精简默认档：只保留每个来源中承载事实的句子（总计约 1k tokens），几秒内作答。若请求答案，会是一段简明的文字。 |
| `moby` | 1.5 点额度 | 深度通读：每个来源完整的可读正文（最多约 15k tokens），适合下游模型需要完整来龙去脉的场景。若请求答案，会更详尽并带 `[n]` 引用标注。 |

经验法则：先用 Mako。当任务是综合归纳时再上 Moby——简报、对比，以及任何漏掉一段就会改变结论的场景。Krill 适合有量但没有预算的情况：它能回答大多数问题，只是来源更少，也不写成文字的答案。

## 额度与限制

一次 Krill 搜索是免费的，且完全不占用你的额度——正是这一点让它得以“不限量”。一次 Mako 搜索消耗 1 点额度，一次 Moby 搜索消耗 1.5 点。`answer` 始终包含在内，失败不收费。每个账户每月获得 1,000 点额度；额度按你的注册日（付费方案则按账单日）每月续期，且不累积到下月。实时用量见[用量](https://serpdive.com/dashboard/usage)页面，方案与价格见[账单](https://serpdive.com/dashboard/billing)页面。

当月额度用尽后，后续请求会返回 `429 monthly_quota_exceeded`，直到额度重置为止；被拦下的调用从不计费。在**按量付费**下默认没有月度上限——用量会被计量，并在每个账单周期结束时开票；你可以在[账单](https://serpdive.com/dashboard/billing)页面设置一个可选的每月**消费上限**，超出后请求同样会返回 429，行为与额度用尽一致。若某个密钥创建时带有自己的月度上限，用满后会返回 `429 key_limit_exceeded`——你其余的密钥照常工作。速率限制为**每秒 5 次、每分钟 200 次**，所有方案一致；超出后请求返回 `429 rate_limit_exceeded` 并带 `retry-after` 响应头，且不计费。此外，同一账户同时在途的搜索最多 **Mako 与 Moby 各 5 个、Krill 1 个**——这限制的是同时进行的工作量而非请求频率，超出会返回 `429 too_many_concurrent_requests`。需要大规模搜索？[联系我们](mailto:contact@serpdive.com)。
