DeepSeek 推出 Search API:接口替代多套联网检索服务
上周给一期视频脚本核实「DeepSeek 搜索接口怎么计费」时,模型凭记忆只能给出一个含糊区间——这类问题,正是联网检索要解决的。
DeepSeek 在 2026 年 10 月 17 日上线的 Search / Fetch 两个接口,把联网 RAG 里的「检索」和「正文抽取」两段收进了同一套 Bearer 鉴权:Search 按 query 返回带 rerank_score 与 authority 的结构化结果,Fetch 把任意 http/https 页面转成 Markdown。 也就是说,你不必再分别维护搜索服务、正文解析器、重排接口三套 SDK 与三套错误码。
下面覆盖:Search 与 Fetch 的请求字段、搜索语法、返回结构、错误码与计费口径,四步 Python 实现,以及它覆盖不到的场景——全部基于 DeepSeek 官方 Search & Fetch 指南 与 2026-10-17 的 API 变更日志,价格与限速以官方文档为准。这是我们 AI-Skills.video 做选题调研时最先接进来的外部能力。
先划边界:它只管「公开网页」这一路,私有文档、登录态页面、付费墙内容都不在其中,这部分仍要自建索引或走知识库方案。
一、这套接口省了什么:联网 RAG 从「五段自建」变成「两段调用」
结论先说:传统 RAG 与联网检索的差别不在模型,而在索引由谁维护。
离线 RAG 里,你要自己把文档灌进向量库、调 embedding、跑 TopK、再挂一个 rerank;联网检索里,索引和重排都在服务端。对照如下:
| 环节 | 自建离线 RAG | DeepSeek Search / Fetch |
|---|---|---|
| 索引来源 | 你自己灌入的文档与切块 | 自研搜索引擎的网页索引 |
| 召回方式 | 向量相似度 + TopK | query 语法 + 服务端重排评分 |
| 重排 rerank | 自建 cross-encoder 或调第三方 | 直接返回 rerank_score(0~3) |
| 正文清洗 | 自写解析器、去样板、去导航 | Fetch 直接返回 Markdown |
| 数据新鲜度 | 看你何时重灌 | 实时网页 |
据官方变更日志,这套检索由 DeepSeek 自研搜索引擎承担,日志称其每天处理全球数亿次搜索请求、索引数千亿网页,并针对 SEO 垃圾与低质 AIGC 做了过滤。对使用者的实际意义是:你拿到的每条结果都自带重排分和权威度,不必再从零搭一套清洗 + 打分流水线。
省掉的「多套 API」具体是三样东西:
- 鉴权与配额:同一把 Key、同一个 Base URL(
api.deepseek.com),与模型调用共用。 - 计费口径:搜索与抓取都是每 1000 次成功请求 5 元,不按 token、不按条数。
- 错误格式:OpenAI 兼容错误体,
param恒为null,重试逻辑可以复用。
据作者观察,多供应商方案真正贵的不是接入代码,而是四套口径漂移后的排障——同一个 429 在不同厂商代表「超频」「余额不足」「被目标站拦截」三种事。
这套接口和 Agent 侧的工具调用是同一条线上的事,配套的编排方式可以对照 AI Agent 工具调用与工作流 一起看。
二、动手前的三个前提:Key、计费口径、限流
两个接口不单独开通,和模型 API 共用一把 Key 与一个 Base URL。 前提只有三条。
Key 与鉴权:在 platform.deepseek.com 申请后,请求头带 Authorization: Bearer <Key> 与 Content-Type: application/json 即可。
计费:按请求次数计费,每 1,000 次成功请求 5 元(英文文档标注 $0.75)。关键口径是——只有 HTTP 200 且产生结果才计费:Search 的 results 必须非空,Fetch 必须返回正文;失败、空结果、超时都不计费;count 只影响返回条数,不影响单价。
| 项目 | 口径(以官方文档为准) |
|---|---|
| Search | 每 1,000 次成功请求 5 元 |
| Fetch | 每 1,000 次成功请求 5 元 |
| 计费触发 | 200 且返回结果/正文 |
| 结果条数 | 不影响价格 |
限流与免费期:QPS 按账号粒度限制,Search 与 Fetch 独立计数,互不占用额度;Fetch 另有针对目标域名的 QPS 限制,超限一律返回 429。另外,通过 DeepSeek Harness / Claude Code / Codex 使用 DeepSeek API 时,服务端工具调用已经会打到这两个接口,这部分目前不计费,自北京时间 2026 年 11 月 1 日 00:00 起按次收费——免费期结束前对一下自己的调用量比较稳妥。
三种接入形态,按需要选一种
| 形态 | 适合谁 | 你要做的事 |
|---|---|---|
| 服务端工具(Harness / Claude Code / Codex) | 在 IDE、CLI 里让 Agent 自己搜 | 无需配置,走 DeepSeek API 即生效 |
| 原始 HTTP(本文主线) | 自建服务、自动化脚本 | 两个 POST + Bearer |
| 社区 MCP 封装(非官方) | 任意支持 MCP 的客户端 | 配 mcpServers 与环境变量 Key |
三、Search:5 个请求字段、8 个操作符、10 个返回字段
Search 是 POST https://api.deepseek.com/v1/tools/search,全部信息都在这 5 个请求字段里。
| 字段 | 类型 | 必填 | 口径(以官方文档为准) |
|---|---|---|---|
query |
string | 是 | 1~128 字符,不能全空白,支持操作符 |
count |
integer | 否 | 1~50,默认 10 |
ori_question |
string | 否 | 用户原始问题,1~128 字符,助意图理解 |
localization |
string | 否 | IP 地址,优先返回本地化结果 |
need_content |
boolean | 否 | 默认 false,false 时 content 为空串 |
搜索语法原样透传给搜索服务,8 个操作符可在一个 query 里组合:
| 操作符 | 示例 | 作用 |
|---|---|---|
site: |
site:github.com |
限定站点,多个用 OR 连接 |
-site: |
-site:example.com |
排除指定站点 |
- |
-广告 |
排除含该词的页面 |
-"" |
-"release notes" |
排除含该短语的页面 |
"" |
"精确短语" |
精确匹配短语 |
lang: |
lang:zh,en |
限定语言,逗号分隔 |
after: |
after:2025-01-01 |
限定发布日期之后 |
before: |
before:2026-06-01 |
限定发布日期之前 |
返回是一个 results 数组(无匹配时为空数组),每条结果 10 个字段:
| 字段 | 说明 |
|---|---|
rerank_score |
0~3,≥1.5 为优质结果 |
title / url |
标题与链接 |
snippet |
100~200 字符,适合前端展示 |
summary |
600~800 字符,适合给模型读 |
content |
正文,need_content=false 时为空串 |
site_name / site_icon |
站点名与图标,缺失为 null |
date |
UTC RFC 3339,未知为 null |
authority |
0~4,≥3 为极权威,-1 表未知 |
一条结果的形态大致是这样(简化示意,字段取值来自官方文档示例):
{
"results": [
{
"rerank_score": 2.4,
"title": "DeepSeek V4 model released",
"url": "https://example.com/deepseek-v4",
"snippet": "DeepSeek V4 is now available...",
"summary": "DeepSeek has released the V4 model...",
"content": "",
"site_name": "Example News",
"site_icon": "https://example.com/favicon.ico",
"date": "2026-06-01T00:00:00+00:00",
"authority": 3
}
]
}
一个能省一半调用的取舍:summary 已是 600~800 字符的长摘要,概览型问答只靠它就够;只有当你要引用原文细节、核对具体数字与版本号时,才值得再对精选链接调一次 Fetch。字段全貌可查 Search API Reference。
四、Fetch:1 个字段、2 个返回项,和三类抓不到的情况
Fetch 是 POST https://api.deepseek.com/v1/tools/fetch,请求体只有一个 url。 要求是 HTTP/HTTPS、含 host、不带用户名密码、长度不超过 8192 字符;返回两项——title 与 content(Markdown 正文)。
它的价值在于把「网页 → 干净文本」这一段黑箱化:不用自己写正文抽取、不用处理导航与广告样板、不用关心页面里是 <article> 还是 <div>。
但它有三类情况会抓不到,都属于正常返回而非接口故障:
- 站点侧拒绝或页面不存在:URL 不可用、被站点策略拦截、目标站点返回错误,归入 403/404 一类。
- 内容类型不受支持或无正文可提取:返回 404,
content自然也没有。 - 按域名 QPS 超限:返回 429,等一会儿重试。
因此正确用法是「少量精选 + 兜底」:先搜再抓,每轮只抓 2~3 篇最相关的结果,抓不到就退回该条的 summary,不要让抓取成为链路的单点。抓下来的 Markdown 想直接塞进客户端工具,可以照着 把网页抓取封装成 MCP 工具 的思路包一层。
五、实操:四步搭出 search → 筛选 → fetch → 回答
环境是 Python 3.9+,依赖只有 requests。下面四段代码按第一步到第四步的顺序拼进同一个 rag.py,在文件末尾加一行 print(answer("你的问题")[0]) 即可运行,不要单独执行某一段。
第一步:申请 Key 并跑通最小 Search 请求
这一段只做一件事:确认 Key 有效、能拿到 results 数组。
# rag.py 顶部 · 依赖 requests(pip install requests),Python 3.9+
import os
import requests
from urllib.parse import urlsplit
API_KEY = os.environ["DEEPSEEK_API_KEY"] # 在 platform.deepseek.com 申请
BASE = "https://api.deepseek.com/v1/tools"
HEADERS = {"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"}
def search(query, count=5):
"""返回 results 列表;无匹配时是空数组,不是异常。"""
resp = requests.post(f"{BASE}/search", headers=HEADERS,
json={"query": query, "count": count}, timeout=20)
resp.raise_for_status() # 400/401/402/429 在这里抛出
return resp.json()["results"]
# 试跑:print([(r["rerank_score"], r["title"]) for r in search("DeepSeek 搜索 API 计费", 3)])
第二步:用 rerank_score 与 authority 筛掉噪音
筛选口径直接对齐官方阈值:rerank_score ≥ 1.5 为优质,authority ≥ 3 为极权威。 顺手按站点去重,避免前三条全来自同一家转载站。
def pick(results, min_score=1.5, min_authority=2, limit=3):
kept, seen = [], set()
for r in sorted(results,
key=lambda x: (x["rerank_score"], x["authority"]),
reverse=True):
if r["rerank_score"] < min_score or r["authority"] < min_authority:
continue # authority 为 -1(未知)时自然被排除
host = urlsplit(r["url"]).netloc # 同一站点只留一条
if host in seen:
continue
seen.add(host)
kept.append(r)
if len(kept) == limit:
break
return kept
第三步:对入选链接调 Fetch 拿 Markdown 正文
这一步要处理的就是 429 与超时:408/429/5xx 退避重试,其它 4xx 不改请求不重试。 同时按字符截断,控制后续上下文长度。
import time
def fetch_md(url, max_chars=12000, retries=3):
"""返回 dict;失败时 error 非空,由调用方决定是否用摘要兜底。"""
for i in range(retries):
resp = requests.post(f"{BASE}/fetch", headers=HEADERS,
json={"url": url}, timeout=30)
if resp.status_code == 200:
page = resp.json()
return {"title": page["title"],
"content": page["content"][:max_chars],
"error": None}
if resp.status_code in (408, 429) or resp.status_code >= 500:
time.sleep(2 ** i) # 1s → 2s → 4s
continue
return {"title": "", "content": "", "error": resp.status_code}
return {"title": "", "content": "", "error": "retry_exhausted"}
第四步:把片段拼进 Prompt 并调模型回答
拼装时给每条资料编号,并用 system 指令要求「无法回答就说不知道」,否则模型很容易把相邻页面的信息混成一条。
def answer(question, top_k=3):
hits = pick(search(question, count=10), limit=top_k)
if not hits: # 幂等兜底:空结果不进模型
return "没有检索到可用结果", []
ctx, srcs = [], []
for h in hits:
page = fetch_md(h["url"])
idx = len(srcs) + 1
if page["error"]: # 抓不到就退回长摘要
ctx.append(f"[{idx}] {h['title']}\n{h['summary']}")
else:
ctx.append(f"[{idx}] {page['title']}\n{page['content']}")
srcs.append(h["url"])
prompt = ("只依据下面的资料回答,无法回答就说不知道,"
"并在句末用 [n] 标注来源。\n\n" + "\n\n".join(ctx))
resp = requests.post("https://api.deepseek.com/chat/completions",
headers=HEADERS,
json={"model": "deepseek-flash", # 模型名以官方文档为准
"messages": [{"role": "system", "content": prompt},
{"role": "user", "content": question}]},
timeout=120)
resp.raise_for_status()
return resp.json()["choices"][0]["message"]["content"], srcs
这条链路的完整流程,速查如下:
| 步骤 | 关键动作 | 关键字段与参数 |
|---|---|---|
| 1 | POST /v1/tools/search |
query、count |
| 2 | 按分数筛选去重 | rerank_score≥1.5、authority≥2 |
| 3 | POST /v1/tools/fetch |
url、正文截断 12000 字符 |
| 4 | 拼 Prompt 调模型 | 来源编号 + 拒答指令 |
脚本的完整版(含并发队列、429 退避与日志)我们整理成了 联网 RAG Agent 模板与脚本骨架,改 query 和截断长度就能接到自己的项目里。
六、坑与局限:429、404 和它真没覆盖的部分
这段是在 AI-Skills.video 的自动化选题流程里踩出来的,按坑的严重程度排。
同一个 QPS 数字,文档出现过两个版本
我们检索时,官方文档的中文页与英文页对 QPS 的记载并不一致:一处是 Search 与 Fetch 各 5 QPS,另一处是 Search 100 / Fetch 25。这说明限速在调整中,接入时不要把它写死进业务假设——按最保守的一档设计队列,遇到 429 就指数退避,并回读一次当前文档。账号级限速与模型并发限制相互独立,不要用模型侧的并发经验反推它。
404 不等于接口坏了
Fetch 的 404 涵盖「URL 不可用、被站点策略拦截、目标站点报错、内容类型不受支持、页面没有可提取正文」五种成因。所以别把 404 当异常监控的 P1 报警,它更接近「这条链接没价值」。真正要报警的是 401(Key 失效)、402(余额不足)与大面积 429。
计费只在「成功」时发生,但成功不等于「有用」
成功口径是「200 且检索出结果」,空结果不计费——这对手气不好时挺友好,但也意味着**「计费次数」不等于「有效资料数」**。做预算时按「成功请求数」算,做效果评估时按 rerank_score ≥1.5 的条数算,两个指标别混。
它不管的那部分:私有文档、登录态与非 HTML 内容
这套接口不做你私有文档的索引,也不处理需要登录态的页面;url 还明确禁止带用户名密码。换句话说,它替代的是「公开网页检索 + 正文抽取」,不是「企业知识库 RAG」。 需要私有资料时,仍要回到自建切块与召回的那条路。
抓取合规:两句话
Fetch 的结果受目标站点可用性与访问策略影响,抓取前先确认目标站的 robots 与授权条款;对受版权保护的正文,缓存范围与二次分发要自己收窄。
七、成本与选型:什么时候换、什么时候别换
先算一笔账(示例口径,非实测账单):一个内容团队每天 200 次 Search 做选题调研、100 次 Fetch 做正文核实,30 天共 9,000 次成功请求,按每 1,000 次 5 元估算约 45 元/月;把 count 从 5 调到 20,费用不变。实际扣费以账单为准。
| 场景 | 建议 | 理由 |
|---|---|---|
| 公开网页的实时事实核查 | Search + Fetch | 无需自建索引 |
| 纯概览型问答 | 只调 Search,need_content=false |
summary 已有 600~800 字符 |
| 私有文档问答 | 自建知识库 RAG | 它检索不到你的文件 |
| 高频批量抓取 | 队列 + 退避,或降级多供应商 | 账号级 QPS 是硬约束 |
| 需要精细正文清洗 | Fetch + 自建后处理 | Markdown 仍需二次加工 |
判断标准可以压成一句:如果「资料在公开网页上、且你要的是实时性」,就值得换;如果「资料在你自己的库里、且你要的是稳定语料」,就别换。 需要私有知识库那条路时,切块与召回参数可以照 Dify 知识库 RAG 的切块与召回配置 调。
放到具体工作流里,我们用它的位置很固定:视频选题阶段用 Search 扫一轮行业动态与官方公告,写作前用 Fetch 抓 2~3 篇一手页面核对版本号与价格——这两件事以前要分别接一套搜索 API 和一套正文解析服务,现在是一条 Key 加两个 POST。
总结
- 要实时公开网页 → 用 Search 的
summary就够,先别急着抓正文。 - 要引用原文细节 → 只对
rerank_score≥1.5 的 2~3 篇调 Fetch。 - 要私有文档问答 → 这套接口帮不上,仍走自建知识库切块与召回。
- 要控成本 → 每 1,000 次成功请求 5 元,空结果与失败不计费。
- 要稳 → 按最保守的账号级 QPS 设计队列,429 指数退避,别写死限速。
它替代的不是 RAG,而是「检索 + 清洗」那两段最脏的维护工作。