开放平台 API 文档

把「AI 推荐查询」能力接入你的应用:提交问题 → 轮询结果 → 拿到正文与引用来源。提交即采集,结果实时回传。

除「AI 推荐查询」/v1/ask/* 外,v1 还提供 监测(/v1/monitor/*,看品牌是否被 AI 推荐)与 联网检索(/v1/search/*,找引用源)。下方「新增章节」逐一带你接。

Base URL:https://www.geocha.cn(https://www.v2zy.com 为同站点别名,二者等价) · 版本 v2 · 更新 2026-10-14(新增 P1 三字段 / 4 码构造法 / 字段名口径) · 管理后台:开发者中心

鉴权 平台编码 接口 状态码 计费 返回字段 端点总表 监测·提交 监测·查询 检索·提交 检索·查询 额度/并发 mode 4 值 平台编码 V2 错误码 browser 专属错误 结果字段 P1 三字段 字段名口径 内部台账 可用性 轮询协议

鉴权

所有接口在请求头带 X-API-Key。Key 在 开发者中心 生成,完整 Key 仅显示一次,请妥善保存。

X-API-Key: sk-714ff81f3b63b329bbd23651b36d511e857c4034
🔒 服务端只保存 Key 的 sha256 哈希与展示码(前 8 位),明文不落库。Key 被误泄可在开发者中心禁用即时失效。

平台编码

提交任务时用 name 指定平台。共 11 个编码(7 个网页端 + 4 个 APP 端):

编码(新)模型端旧编码(兼容)
doubao-web豆包webDB
doubao-app豆包appDOUBA
deepseek-webDeepSeekwebDP
deepseek-appDeepSeekappDPA
qwen-web千问webTYQW
qwen-app千问appTYQWA
yuanbao-web腾讯元宝webTXYB
yuanbao-app腾讯元宝appTXYBA
kimi-webKimiwebKIMI
wenxin-web文心一言webWXYY
zhipu-web智谱清言webZP
♻️ 编码已升级(V2):请优先使用新编码(形如 doubao-web)。旧编码(DB / DOUBA 等)仍完全兼容,老调用方无需改动。

接口

POST/v1/ask/check

提交一个问答任务,返回 reqId 用于轮询结果。

参数类型必填说明
promptstring是提问内容
namestring是平台编码(见上表)
thinking_enabledint否思考模式(一期忽略,预留)
curl -X POST https://www.geocha.cn/v1/ask/check \
  -H "X-API-Key: sk-xxxx" \
  -H "Content-Type: application/json" \
  -d '{"prompt":"重庆小面培训哪家好","name":"doubao-web"}'
{
  "code": 200,
  "msg": "成功",
  "data": { "reqId": "ask_20261005_123456" }
}

GET/v1/ask/result

轮询任务结果。status 为 ING 时表示仍在采集,建议间隔 3~5 秒重试,超时 5 分钟视为失败。

参数位置必填说明
reqIdquery是提交任务时返回的任务 ID
curl "https://www.geocha.cn/v1/ask/result?reqId=ask_20261005_123456" \
  -H "X-API-Key: sk-xxxx"
{
  "code": 200,
  "msg": "success",
  "data": {
    "status": "SUCCESS",
    "prompt": "重庆小面培训哪家好",
    "result": [
      {
        "context": "重庆小面培训推荐……(正文)",
        "quote": [
          { "url": "https://example.com/a", "title": "推荐A", "snippet": "" }
        ],
        "think": "",
        "search_word": ["重庆小面", "培训"],
        "suggestions": [],
        "rich_media_block": []
      }
    ]
  }
}

GET/v1/account/quota

查询当前 Key 的配额使用情况。

curl "https://www.geocha.cn/v1/account/quota" -H "X-API-Key: sk-xxxx"
{ "code": 200, "msg": "success", "data": { "used": 12, "total": 100, "window": "total" } }

状态码

code含义触发场景
200成功—
400reqId 未提交未带 reqId / 任务不存在或已过期
401未授权Key 无效、已禁用、或访问他人任务
400参数错误prompt/name 缺失、或平台编码未知(会提示「编码格式已升级」)
406配额不足已用 ≥ 总额度
429请求频繁同 Key 并发任务 ≥ 5
HTTP 状态码与响应体 code 字段一致,便于网关直接判断。

计费

按 「项」 计费:1 模型 × 1 端 = 1 项。例如同时查「豆包(web) + DeepSeek(app)」= 2 项。任务提交即扣减,失败可在控制台申请退还。

💰 监测 / 联网检索独立计费(与上方「AI 推荐查询」口径分开):1 项 = 1 关键词 × 1 平台 × 1 采样(accepted_items = keywords × platforms × n)。cloud_phone(APP)¥0.15/项,browser(Web)与 /v1/search ¥0.05/项;n ≥ 2 按倍数计。失败不计费(cost_cny = 0),对账恒等式:total_cost_cny ÷ succeeded_items = 单价。
💎 judge: true → 每条成功结果额外一次 LLM 判定成本(按项另计 / 或已含在单价内,实际以「内部台账」为准);未传 → 无额外成本(见「P1 三字段」)。

返回字段说明

字段类型说明
contextstringAI 回答正文(已做跨屏/跨页拼接)
quotearray引用来源列表:{url, title, snippet}
thinkstring思考过程(当前多为空,预留)
search_wordarray模型检索使用的关键词
suggestionsarray推荐追问(可选)
rich_media_blockarray视频/富媒体块(暂不返回)

端点总表 & 五分钟上手

v1 对外端点统一 X-API-Key 鉴权,统一风格:提交 → 拿 req_id → 轮询查询。除 /v1/ask/*(AI 推荐查询)外,还有以下端点:

#端点方法用途计费
1POST /v1/monitor/submitPOST提交监测任务(品牌是否被 AI 推荐)按项
2GET /v1/monitor/{req_id}GET查询监测结果(轮询)—
3POST /v1/search/submitPOST提交联网检索任务(供写文章找引用源)按次
4GET /v1/search/{req_id}GET查询联网结果—
5GET /v1/quotaGET查额度与并发上限—
6GET /v1/internal/billGET内部账号月度台账(仅内部账号)—
⛔ 除上表 6 组外,没有别的对外端点。 /api/*(含 /api/collector/*、/api/status、/api/sys/*)、/v1/keys*、/v1/_test/*、/api/admin/* 均为我方内部 / 管理 / 网站前端接口,访问会得到 401 / 404 —— 属预期行为,不是「缺权限」。

五分钟上手

# ① 提交监测(1 关键词 × 1 平台 × 1 采样 = 1 项)
curl -X POST https://www.geocha.cn/v1/monitor/submit \
  -H "X-API-Key: sk-xxxxxxxx" -H "Content-Type: application/json" \
  -d '{"brand_name":"米途GEO","keywords":["重庆GEO哪家好"],"platforms":["doubao"],"mode":"cloud_phone","n":1,"judge":true}'
#    ↑ 末尾 "judge":true 即生成 P1 三字段(mention_rank / sentiment / competitors)
#      省略则不生成(P0 兼容,三字段为 null / [])—— 见「P1 三字段」章节

# ② 轮询(拿到 req_id 后;建议 3-5s 起指数退避)
curl https://www.geocha.cn/v1/monitor/geo_req_20261011_123456 -H "X-API-Key: sk-xxxxxxxx"

监测:提交 · POST /v1/monitor/submit

提交一个「品牌是否被 AI 推荐」监测任务,返回 req_id 用于轮询结果。统一风格:提交 → 拿 req_id → 轮询查询。

字段类型必填说明
brand_namestring是品牌名(用于判定是否被提及)
keywordsstring[]是关键词/问题,非空;≤ 50 条(超出截断)
platformsstring[]是平台,非空;≤ 11 条;裸名或 V2 码(见平台编码 / mode)
modestring是通道,4 值(见 mode 章节)。只有 cloud_phone / browser 可执行
nint否每个 (关键词,平台) 采样次数,默认 1,上限 10
metaobject否透传对象(如 {"report_id":"..."}),原样回显;同 meta.report_id 可做业务侧关联
callback_urlstring否⚠️ 预留字段,当前版本不发起回调;请用轮询
judgebool否是否生成 P1 三字段(mention_rank / sentiment / competitors)。默认 false;传 true → 对每条有答案结果额外跑一次 LLM 判定(见「P1 三字段」)
curl -X POST https://www.geocha.cn/v1/monitor/submit \
  -H "X-API-Key: sk-xxxxxxxx" -H "Content-Type: application/json" \
  -d '{"brand_name":"米途GEO","keywords":["重庆GEO哪家好"],"platforms":["doubao"],"mode":"cloud_phone","n":1}'
{
  "req_id": "geo_req_20261011_123456",
  "status": "queued",
  "accepted_items": 1,
  "estimated_points": 1,
  "estimated_duration_sec": 90,
  "created_at": "2026-10-11T20:30:00+08:00"
}
⏱️ estimated_duration_sec = accepted_items × 单条实测耗时(cloud_phone 按 90s、browser 按 50s 估)。用它决定下一批何时发(估算偏保守,宁等勿撞 429)。
// 400 参数错误(例:缺 keywords)
{ "code": "INVALID_PARAMS", "message": "keywords 非空数组必填" }

// 501 不支持的 mode(例:real_device)
{ "code": "NOT_IMPLEMENTED",
  "message": "mode=real_device(物理真机)我方无真机资源、P0 不交付;请改用 cloud_phone(MuMu 本地模拟器,同属 App 端,结果等价)" }

// 402 额度不足
{ "code": "QUOTA_EXCEEDED", "message": "额度不足:本次需 4 项,剩余 2 项" }

// 429 并发超限(该 Key 活跃 ≤5)
{ "code": "RATE_LIMITED", "message": "请求频繁:该 Key 并发任务过多(≥5),请退避后重试" }

监测:查询 · GET /v1/monitor/{req_id}

轮询监测结果。建议 3–5s 起指数退避。status 为 queued / running 时 finished_at 为 null 属正常。

curl https://www.geocha.cn/v1/monitor/geo_req_20261011_123456 -H "X-API-Key: sk-xxxxxxxx"
// 未完成
{ "req_id": "geo_req_20261011_123456", "status": "running",
  "created_at": "2026-10-11T20:30:00+08:00", "finished_at": null,
  "progress": { "done": 0, "total": 1 }, "results": [], "meta": {} }

// 完成
{
  "req_id": "geo_req_20261011_123456",
  "status": "done",
  "created_at": "2026-10-11T20:30:00+08:00",
  "finished_at": "2026-10-11T20:31:35+08:00",
  "progress": { "done": 1, "total": 1 },
  "results": [ /* 见「返回字段」章节的结果字段全表 */ ],
  "summary": { "total_items": 1, "succeeded": 1, "failed": 0, "total_cost_cny": 0.15 },
  "meta": {}
}
🔵 judge:true 的批次,完成时顶层多返回 sentiment_axis(见「P1 三字段」);未开 judge 的批次不出现该字段。
// 404 从未存在(或 kind 不符)
{ "code": "NOT_FOUND", "message": "req_id 不存在:geo_req_20261011_999999" }

// 410 曾存在但已超保留期(≥7 天,实际 8 天)
{ "code": "EXPIRED", "message": "req_id 已过期(结果保留 ≥7 天):geo_req_20261001_000001" }

// 批级回收 / 全失败时 status=failed,带机器可读 error
{ "req_id": "geo_req_20261011_123456", "status": "failed",
  "created_at": "…", "finished_at": "…",
  "progress": { "done": 0, "total": 2 }, "results": [],
  "summary": { "total_items": 2, "succeeded": 0, "failed": 0, "total_cost_cny": 0 },
  "error": "BATCH_TIMEOUT",
  "error_detail": "批级超时(30 分钟无进展):已完成 0/2 …" }

联网检索:提交 · POST /v1/search/submit

提交一个联网检索任务(供写文章找引用源),返回 req_id 用于轮询。

字段类型必填说明
querystring是检索问题
platformsstring[]否默认 ["doubao-web","kimi-web","qwen-web"](定序 fallback:任一成功即返回)
top_kint否引用条数上限,默认 10
metaobject否透传
🔴 /v1/search 只走 Web 端:platforms 含 -app 码(如 doubao-app)→ 400(fail-fast,不会静默返回空引用)。
curl -X POST https://www.geocha.cn/v1/search/submit \
  -H "X-API-Key: sk-xxxxxxxx" -H "Content-Type: application/json" \
  -d '{"query":"重庆GEO优化公司怎么选","top_k":5}'
{ "req_id": "geo_search_20261011_654321", "status": "queued",
  "accepted_items": 1, "estimated_points": 1,
  "estimated_duration_sec": 50, "created_at": "2026-10-11T20:32:00+08:00" }

// 400:带 -app 码
{ "code": "INVALID_PARAMS", "message": "/v1/search 仅支持 Web 端(references 依赖网页引用);请改用 doubao-web 等" }

联网检索:查询 · GET /v1/search/{req_id}

轮询联网结果。注意:results 是对象不是数组(与 /v1/monitor 不同!)。

// 未完成
{ "req_id": "geo_search_…", "status": "running", "created_at": "…",
  "progress": { "done": 0, "total": 3 }, "results": null }

// 完成
{
  "req_id": "geo_search_20261011_654321",
  "status": "done",
  "created_at": "2026-10-11T20:32:00+08:00",
  "finished_at": "2026-10-11T20:33:10+08:00",
  "results": {
    "answer_text": "……联网检索摘要……",
    "references": [
      { "url": "https://…", "title": "…", "site_name": "…", "publish_time": "2026-01-15" }
    ],
    "platform": "doubao",
    "cost_cny": 0.05,
    "latency_ms": 6400
  }
}

// 失败
{ "req_id": "geo_search_…", "status": "failed", "created_at": "…", "finished_at": "…",
  "error": "CAPTCHA_BLOCKED", "error_detail": "…", "results": null }

结果字段全表(results[])

GET /v1/monitor/{req_id} 完成时,results 为数组,每项是下表一条;GET /v1/search/{req_id} 的 results 是对象(见上「检索·查询」)。

字段类型说明
keywordstring关键词(本条对应的问题)
platformstring平台裸名(doubao / wenxin / …)
modestring通道(=cloud_phone / browser)
n_indexint第几次采样(1-based)
checked_atISO8601该条采集完成时刻(≠ 提交时刻、≠ 整批 finished_at)
answer_textstringAI 回答正文;失败条目恒为 ""
cited_urlsstring[]引用链接(去重后)
structuredobject{ is_mentioned, mention_rank, mention_quality, context_type, sentiment, competitors, cited_sources[], citations_status };P0:仅 is_mentioned / cited_sources 有值;P1(judge:true):mention_rank / sentiment / competitors 有值(详见「P1 三字段」),其余恒为 null/[]
sourcestringui_cloud(cloud_phone)/ ui_web(browser)/ api(未交付)
endstringapp / web / none
statusstringok / failed
errorstring失败原因(ok 时不存在该字段,或为空)
error_detailstring原始失败说明(可选,排查用)
citations_statusstringok / empty / not_supported
citations_notestring引用状态说明(可选)
cost_cnyfloat真实成本;失败 = 0
metaobject提交时透传的 meta(原样)
modelstring内部模型 id(一般不必消费)
🔵 structured.citations_status = "not_supported" = 该通道本就不提供引用(APP 端)→ 不要重试,如需引用请用 mode=browser 或 /v1/search。整批 summary:{ total_items, succeeded, failed, total_cost_cny }(total_cost_cny 只累计成功项)。

P1 三字段(judge:true 时生成)

🆕 2026-10-13 上线。未开 judge 时三字段恒为 null / [](向后兼容)。

12.1 触发

提交参数三字段LLM
judge: true✅ 生成跑一次判定
未传 / judge: false(默认)❌ 全 null / []不跑、不加价

judge 是 POST /v1/monitor/submit 的可选布尔参数(默认 false),见「监测:提交」参数表。

12.2 字段(results[].structured)

字段类型值域含义
mention_rankint | null1 / 2 / … / null品牌首次被提及的位次(1-based);未提及 → null
sentimentstring | nullrecommended / neutral / negative / null轴②情感极性
competitorsarray[{name, is_recommended, rank}]竞品清单(形状见 12.4)

12.3 批次级(顶层)

"sentiment_axis": { "total": 1, "recommended": 1, "neutral": 0, "negative": 0 }
total = 本批已判定条目数;recommended + neutral + negative = total。仅 judge:true 批次返回;未开 judge 的批次不出现该字段。

12.4 competitors 形状(P0 → P1)

原(P0):无此字段,恒为 null / []。新(P1):

"competitors": [ { "name": "海星小象", "is_recommended": true, "rank": 2 } ]
键类型说明
namestring竞品名(识别不出的返原文字符串)
is_recommendedbool | null是否被 AI 推荐;判不出 → null
rankint | null竞品位次;判不出 → null

12.5 空值语义

12.6 边界

12.7 成本

12.8 示例(judge:true 批次 · 服务端真实返回)

{
  "req_id": "geo_req_20261013_362984",
  "status": "done",
  "results": [
    {
      "platform": "doubao",
      "mode": "cloud_phone",
      "status": "ok",
      "answer_text": "…米途GEO(探源者GEO)…",
      "structured": {
        "is_mentioned": true,
        "mention_rank": 1,
        "mention_quality": null,
        "context_type": null,
        "sentiment": "recommended",
        "competitors": [
          { "name": "海星小象", "is_recommended": true, "rank": 2 },
          { "name": "珍岛", "is_recommended": true, "rank": 3 },
          { "name": "派活鹿信息科技", "is_recommended": true, "rank": 4 }
        ],
        "cited_sources": [],
        "citations_status": "not_supported"
      },
      "cost_cny": 0.15
    }
  ],
  "sentiment_axis": { "total": 1, "recommended": 1, "neutral": 0, "negative": 0 }
}
📌 示例节选:competitors 条数随 AI 回答而定 —— 本示例为线上真实返回(共 3 家),实际可能更多或更少;三字段均以本条 structured 为准。

字段名与失败条目口径(易错点澄清)

13.1 三个易写错的字段名

✅ 正确❌ 易错写法位置
cost_cnycostresults[].cost_cny、summary.total_cost_cny
platformchannelresults[].platform(恒为裸名)
answer_textanswerresults[].answer_text
🔴 读侧请按「正确」列取字段;写成「易错」列会拿到 undefined。

13.2 失败条目口径

项口径
条目级 status"failed"(不是 "error")
cost_cny恒为 0(失败不计费)
answer_text恒为 ""
structured 三键恒为 null / []
error失败码(见「错误码全表」/「browser 专属 error」)
外层批 status随整体成败为 done / failed —— 不要与条目级混用
🔴 常见误读:① 把条目级 status="failed" 当成 "error";② 以为失败条目也计费。都不对。

额度与并发 · GET /v1/quota

查询当前 Key 的额度与并发上限。limits.max_inflight(默认 5)即同 Key 活跃子任务上限,运行时读取它来决定退避。

curl "https://www.geocha.cn/v1/quota" -H "X-API-Key: sk-xxxxxxxx"
// 外部账号
{ "used": 3, "total": 500, "window": "total", "limits": { "max_inflight": 5 } }

// 内部账号(无余额概念,用量走「内部台账」)
{ "account_type": "internal", "account": "tanyuanzhe",
  "used": 0, "total": null, "window": "none", "limits": { "max_inflight": 5 } }

mode 4 值(关键)

mode通道执行位置支持
cloud_phoneMuMu 本地 Android 模拟器(APP 端)我方采集机✅
browserPlaywright 无头浏览器(Web 端)服务器 / 采集机(见可用性)✅
real_device物理真机—❌ 501
api各平台官方 API—❌ 501
🔴 real_device / api 传了会直接返回 501,即使用其它字段都合法。为什么 501 而不是「静默降级」:把 real_device 当 app 跑却标 source=ui_real、把 web 结果标 source=api,都是数据污染(比不交付更糟)。cloud_phone 与 real_device 结果等价(我方「真机形态」实为本地模拟器)→ 请直接用 cloud_phone。传一个完全不存在的 mode(如 telepathy)→ 400 INVALID_PARAMS(列出 4 个合法值),与「501 已明确不支持」区分开。

平台 × 通道 支持矩阵(先看这个)

平台(裸名)mode=browser(Web 端)mode=cloud_phone(App 端)
doubao / deepseek / qwen / yuanbao✅✅
kimi / wenxin / zhipu✅❌ 无 App 脚本 → 400
合计7 / 74 / 7
🔴 mode 与 platforms 必须同端,否则提交即 400(不派发、不占槽位、不计费):
{
  "code": "INVALID_PARAMS",
  "message": "mode=cloud_phone 与 platforms 不匹配 —— 无 App 通道:wenxin。App 端仅支持 doubao/deepseek/qwen/yuanbao;其余平台请用 mode=browser(Web 端 7 家全)"
}
同理 mode=browser + doubao-app(显式 -app 码)→ 400(否则会实际跑 App 端却标 end=web、并按 browser 单价少计费)。

平台编码 V2(11 项 · 已冻结)

V2 码对外 platform(裸名)端
doubao-webdoubaoWeb
deepseek-webdeepseekWeb
qwen-webqwenWeb
yuanbao-webyuanbaoWeb
kimi-webkimiWeb
wenxin-webwenxinWeb
zhipu-webzhipuWeb
doubao-appdoubaoAPP
deepseek-appdeepseekAPP
qwen-appqwenAPP
yuanbao-appyuanbaoAPP
♻️ 编码不区分大小写(DOUBAO-WEB 等价 doubao-web)。⚠️ APP 端只有 4 家(doubao/deepseek/qwen/yuanbao)→ kimi/wenxin/zhipu 没有 APP 通道,只能用 browser(Web)。

写法:推荐 裸名 + mode({"platforms":["doubao","qwen"],"mode":"cloud_phone"});也兼容 V2 码(自带端,与 mode 需一致,如 ["doubao-app","qwen-web"])。无论传裸名还是 V2 码,响应里 results[].platform 恒为裸名。旧缩写码(DB/DOUBA/TYQW… 11 个)仍受理(向后兼容),但新接入请只用上表两种。

错误码全表

🔴 分两层看:HTTP 状态码(整个请求的成败)与 条目级 error(results[] 里单条的成败,HTTP 仍是 200)。两者不要混用:例如某平台风控失败时,HTTP 是 200,而 results[0].error = "CAPTCHA_BLOCKED"。

HTTP 层

HTTPcode场景建议动作
400INVALID_PARAMS参数错误(缺字段 / 类型错 / 超上限 / 未知平台码)不重试(先修请求)
401UNAUTHORIZEDX-API-Key 无效或缺失不重试 + 告警(Key 可能被禁用)
402QUOTA_EXCEEDED额度用完(外部账号)停止 + 告警(重试无意义)
404NOT_FOUNDreq_id 从未存在(或 kind 不符)不重试(检查 ID / 是否提交成功)
410EXPIREDreq_id 曾存在但超保留期重新提交
429RATE_LIMITED该 Key 活跃子任务 ≥ max_inflight(5)退避重试(指数退避)
500INTERNAL_ERROR服务端内部错误(兜底码,正常不出现)退避重试 1 次;持续出现请报我方
501NOT_IMPLEMENTEDmode=api / mode=real_device / /v1/monitor/batch不重试(换 mode 或换端点)

条目级 error(写在 results[].error,HTTP 仍 200)

error含义建议动作
CAPTCHA_BLOCKED命中风控 / 人机验证不重试(换平台 / 降频)
LOGIN_REQUIRED站点登录态失效不重试(需我方重新登录,请联系)
PAGE_ERROR页面结构异常(找不到输入框等)可重试 1 次;持续请报我方
EMPTY_RESPONSE页面正常但正文为空可重试 1 次;持续则降频
TIMEOUT采集超时(页面卡住/加载慢)可重试(退避)
UPSTREAM_FAILED上游平台 / 采集机失败按轮询协议退避重试
⚠️ 上表的 TIMEOUT / UPSTREAM_FAILED 不是 HTTP 504/502 —— 它们是条目级取值,HTTP 仍是 200。
🔴 失败条目的条目级 status 是 "failed"(不是 "error");只有整批的外层 status 才取 done / failed —— 两者不要混用(详见「字段名口径」)。
失败条目 cost_cny 恒为 0(失败不计费)。每条失败还带 error_detail(原始说明,含采集侧备注)—— 排查用,非必须消费。

browser 专属 error(5 条)

mode=browser 的失败条目,error 取值与建议动作:

error建议动作
CAPTCHA_BLOCKED🔴 勿重试(同 IP 重试仍被拦;换平台或降频)
LOGIN_REQUIRED🔴 勿重试(需我方重登后恢复)
PAGE_ERROR可重试 1 次
EMPTY_RESPONSE可重试 1 次
TIMEOUT可重试(退避)

6.1 4 码构造法(含复现步骤 · 🆕 2026-10-13)

上表中 4 个可定向构造的码,触发条件 / 重试策略 / 独立复现如下:

error触发条件重试策略复现步骤
CAPTCHA_BLOCKED命中风控 / 人机验证(滑块)❌ 不重试同 IP 高频请求(连提 3+ 次)/ 机房 IP 首发
LOGIN_REQUIRED站点登录态失效(cookie/session 过期)❌ 不重试删除 states / 退出登录后查询
PAGE_ERROR页面结构异常(选择器缺失)✅ 重试 1 次平台改版 / mock 错选择器
TIMEOUT采集超时(> WEB_TASK_TIMEOUT_MS)✅ 退避重试设 WEB_TASK_TIMEOUT_MS=2000 → 任意查询必超时
🔧 一键构造(读侧可本地复现):verify-isolated-kit.js —— 用真实生产代码 composeMonitor 生成 4 段与线上 GET /v1/monitor/{req_id} 逐字节一致的失败响应;配套 4 段 sha256(失败基准比 sha256,不比字符串,避免行尾/空行扯皮)。交付文档:4码构造法-探源者.md。
🔴 关于哨兵串 NO_ANSWER_FOUND(重要):采集脚本在「没抓到答案」时会写占位串 NO_ANSWER_FOUND(恰好 15 字符)。我方已保证它不会出现在 status:"ok" 的 answer_text 里(三层防线:采集侧判失败 → 组装层置空 → 输出层双判)。但请读侧仍做一次廉价兜底:判定成功 = status=="ok" ∧ 非空 ∧ 非 NO_ANSWER_FOUND ∧ 长度 ≥ 40。完整判据 + 可直接照抄的 Python/JS 代码:_nav_fix/SENTINEL-NO-ANSWER-FOUND.md。

内部台账 · GET /v1/internal/bill

仅内部账号(account_type=internal)或 admin(X-Admin-Token + ?account=<账号>)。month 省略默认当月,格式须 YYYY-MM,否则 400。凡内部账号调用不扣余额,全部计入台账。

curl "https://www.geocha.cn/v1/internal/bill?month=2026-10" \
  -H "X-API-Key: sk-xxxxxxxx"
{
  "account": "tanyuanzhe",
  "month": "2026-10",
  "submitted_items": 16,
  "calls": 10,
  "succeeded_items": 11,
  "cost_cny": 1.05,
  "markup_cny": 0.11,
  "total_cny": 1.16,
  "failures": [ { "req_id": "geo_req_…", "platform": "wenxin", "error": "…" } ],
  "details": [ { "req_id": "geo_req_…", "kind": "monitor", "submitted_items": 2, "succeeded_items": 2, "cost_cny": 0.3, "status": "settled" } ]
}
📌 字段语义(务必区分两个口径)

轮询协议

项规则
幂等同一 (brand_name, keywords, platforms, mode, n, meta.report_id) 在 5 分钟内重复提交 → 返回同一 req_id,不重复计费、不被限流
退避建议 3–5s 起,指数退避,上限 30s(queued 久停 ≠ 失败,见可用性)
保留期结果保留 ≥7 天(实际 8 天;未命中 404)
404 vs 410404 = 从未存在(含 kind 不符);410 = 曾存在但超期被清理
并发上限该 Key 活跃子任务(queued+running)≥5 → 新的 */submit 直接 429(可用 GET /v1/quota 的 limits.max_inflight 运行时读取)
别重复提交看到 queued 就重复提交 → 占并发名额;超出 5 分钟幂等窗口还会重复计费

可用性(关键)

11.1 两条通道的执行方与在线性

通道执行位置可用性
browser服务器(7×24)✅ 不依赖我方采集机开关机 —— 夜里也能跑
browser 的 doubao / zhipu / yuanbao采集机(住宅 IP,自动路由)⚠️ 依赖采集机在线
cloud_phone(APP,7 家)采集机(MuMu,个人 PC)⚠️ 依赖采集机在线
🔵 路由是自动的,你们无需改任何参数:同一个 mode=browser 请求,doubao/zhipu/yuanbao 我方自动确保由住宅 IP 执行。返回的 source 仍是 ui_web、end 仍是 web。为什么这三家要绕本机:服务器是机房 IP,实测 doubao 0/3(人机验证)、zhipu 0/3;换住宅 IP 后 3/3。其余 4 家(deepseek/kimi/qwen/wenxin)在服务器上稳定 → 走 7×24。

11.2 notice 字段:「在等采集机」会被显式告知

当一批里含 doubao/zhipu/yuanbao(本机专属)且采集机当前离线,GET /v1/monitor/{req_id} 会多返回两个字段(正常批次不出现):

{
  "status": "queued",
  "notice": "LOCAL_COLLECTOR_OFFLINE",
  "notice_text": "本批含 1 项「仅本机可执行」的平台任务(doubao):…本机当前已离线约 2 分钟 → 任务保持排队(不失败、不计费、不代跑);本机恢复后自动继续,无需重新提交。"
}
你们照常轮询即可;有 notice 时在 UI/日志上标注「等待采集机」,避免误判卡死。不要因为它是 queued 就重复提交。

11.3 「排队」与「失败」的边界

queued 久停 = 在等采集机(我方已给 notice)。「等待」本身不算失败。失败只在两种情况发生:① 批级 BATCH_TIMEOUT(默认 30 分钟无任何进展);② 采集侧返回了终态错误。

11.4 该用哪些端点(白名单)

✅ 该用(X-API-Key)⛔ 不该用(内部,401/404 属预期)
POST /v1/monitor/submit/api/collector/*(采集机 ↔ 服务器内部链路)
GET /v1/monitor/{req_id}/api/status、/api/sys/*(我方运维观测)
POST /v1/search/submit/v1/keys*(Key 管理,admin)
GET /v1/search/{req_id}/v1/_test/*、/api/admin/*
GET /v1/quota/api/auth/*、/api/user/*、/api/brand/*(我站网页前端用)
GET /v1/internal/bill(内部账号)—
需要「是不是在等采集机」→ 看 notice(11.2);需要「谁执行的」→ 看 source/end(结果字段)。/v1/quota 与 /v1/monitor/* 已覆盖你们需要的全部信号,无需访问任何内部端点。
需要帮助?在 开发者中心 管理 Key 与用量。文档随版本更新,以线上为准。