WeChat-Search-V2-API
微信综合搜索(搜一搜)/WeChat Universal Search
Changed on[中文]
用途:
- 微信「搜一搜」综合搜索,垂类由 business_type 字符串键切换,传关键词即可。
- 实测可搜:综合 / 公众号(含服务号、视频号)/ 文章 / 视频号视频 / 表情。
- ⚠️ 其余垂类(直播 / 朋友圈 / 新闻 / 读书 / 听书 / 图片 / 百科 / 微信指数 / 划线 / 评论 / 贴图 / 小游戏 / AI 搜索)由微信服务端按账号与地区下发,当前实测恒返回空结果(no_more 文案非空即为正常空结果,raw=true / raw=false 都在顶层;raw=false 时另有 count=0 + items=[]。仍按一次正常调用计费)。
- 价格:0.01$/次
- ⏱️ 由于微信服务器原因,本接口响应较慢,请将客户端请求超时(timeout)设置为 30 秒;timeout 设置过小会造成已扣费但收不到响应的情况。
- ⚠️ 大整数 ID 精度:响应中的 docID / feedNonceId 等为 64 位大整数,超出 JavaScript 安全整数范围(2^53-1)。请始终以字符串方式接收 / 传递这类 ID(解析 JSON 用 json-bigint 或按文本取值),勿让其经过 JS Number。Swagger UI 文档页对超大整数会末位舍入显示,属正常现象,不影响接口实际返回的数据。
参数:
- keyword: 搜索关键词(去空白后 1-100 字)。示例 人民日报
- business_type: 可选,默认 all。垂类字符串键(共 18 个)。实测有结果:综合 all / 公众号·服务号·视频号 account / 文章 article / 视频 video / 表情 sticker。其余键 underline / encyclopedia / live_stream / comment / listen / news / photos / book / moments / image / mini_game / weixin_index / ai_search 由服务端按账号与地区下发,当前实测恒空。传列表外的值将直接报错(422)。
- sort: 可选,默认 default。排序(结果页「排序」下拉,综合与各垂类通用)—— 不限(相关性) default/0 / 最新(发布时间降序) latest/1 / 最热(点赞降序) hot/2,字符串键或整数均可,非法值报错(400)。
- publish_time: 可选,默认 all。发布时间(结果页「时间」下拉,通用)—— 不限 all/0 / 最近一天 day/1 / 最近七天 week/2 / 最近半年 half_year/3,字符串键或整数均可,非法值报错(400)。
- offset: 可选,默认 0(>=0)。首页传 0;翻页请用 cursor,只传 offset 无效(每页都回到第一页)。
- cursor: 可选。翻页游标。首页留空;翻页时把上一页响应里返回的 cursor 原样传回(须同时重复上一页的 sort / publish_time),配合 continue_flag 判断是否还有下一页。
- raw: 可选,默认 True。True=原始搜索响应;False=精简解析。
💡 视频垂类专属的「时长」筛选见 /fetch_search_videos(business_type=video + duration)。
返回:
- 搜索结果列表(结构随垂类略有差异)
典型链路:
- 💡 拿视频号详情(媒体下载地址 / decode_key):搜索(video / all)的视频结果项不直接含媒体,只给 exportId(+ jumpInfo.extInfo.feedNonceId)。要下载 / 解密:取 exportId → 走视频号 V2 接口 /api/v1/wechat_channels/v2/fetch_video_detail(传 export_id)拿 media(url / url_token / decode_key)和明文 username。完整链路与解密说明见该接口文档。
- 公众号结果项的 jumpInfo.userName(gh_…)可喂给公众号 V2 接口 /api/v1/wechat_mp/v2/fetch_account_profile / fetch_account_articles。
响应结构与 JSON Path:
raw=false(精简解析,snake_case):
- 关键词回填: $.data.keyword
- 实际生效的垂类代码(如公众号=33554499): $.data.business_type
- 结果总数: $.data.total —— 响应里所有 box 与 subBox 的 totalCount 全扫取最大值(垂类的 totalCount 挂在 subBoxes 上;综合搜索没有单一总数,给的是最大的那个 box)。零结果时为 null(没有任何正数 totalCount),恒空垂类恒为 null
- 是否还有下一页: $.data.continue_flag
- 翻页游标(原样回传接口的 cursor 参数取下一页): $.data.cursor
- 服务端 offset(仅供参考,单独传回无法翻页): $.data.offset
- 本页结果数: $.data.count
- 拍平的结果项列表(已把各 box / subBox 的 items 合并): $.data.items[]
- 服务端下发的垂类 tab 清单(仅综合 business_type=all 时有值,其余为 null): $.data.categories[] —— 每项 .type / .word / .extra_kvs,这是当前账号真正可用垂类的权威来源。注意 .type 是垂类的整数码(如 33554499),而本接口的 business_type 只收上面那 18 个字符串键,整数会被拒(422)—— 需自行按键表映射(如 33554499 → account)
- 零结果时服务端给的文案: $.data.no_more(有值 = 微信返回的正常空结果,不是调用失败)
- 单项(N 为下标,字段随垂类不同;以下为 account 公众号示例):
- 标题(含 <em> 高亮): $.data.items[N].title
- 描述: $.data.items[N].desc
- 文档 id: $.data.items[N].docID
- 类型名(如「公众号」): $.data.items[N].accTypeName
- 跳转信息: $.data.items[N].jumpInfo(.userName = gh_…、.nickName、.signature)
- 视频垂类项另含 exportId(+ jumpInfo.extInfo.feedNonceId)→ 见上「典型链路」
raw=true(原始,本接口默认):
- data 顶层: keyword / business_type / results(不做 items 拍平)/ total / continue_flag / offset / cursor / categories / no_more(total / categories / no_more 两种模式都在顶层,字段集已对齐)
- 完整结果集: $.data.results
- 结果盒子数组: $.data.results.data[],每个 box 的结果在 $.data.results.data[M].items[] 与 $.data.results.data[M].subBoxes[K].items[] —— 即 raw=false 的 items[] 是把这些拍平后的结果。
- 是否还有下一页: $.data.continue_flag(raw=true / raw=false 均在顶层)
- 翻页游标: $.data.cursor —— 原样回传接口的 cursor 参数取下一页;$.data.offset 仅为服务端 offset,单独传回无法翻页
- 总数直接读顶层 $.data.total(raw 两种模式都有)—— 它是 $.data.results.data[M].totalCount 与 $.data.results.data[M].subBoxes[K].totalCount 全扫取的最大值;垂类的数字挂在 subBoxes 上,只读顶层 box 的 totalCount 会拿到 null
- 做结果遍历建议直接用 raw=false(items 已拍平),路径更干净。
[English]
Purpose:
- WeChat "Search" (搜一搜) universal search; the vertical is switched by the business_type string key — just pass a keyword.
- Verified to return data: all / official accounts (incl. service accounts and Channels) / articles / Channels videos / stickers.
- ⚠️ The other verticals (live streams, Moments, news, books, listen, images, encyclopedia, WeChat Index, underline, comments, photos, mini games, AI search) are gated server-side per account & region and currently always come back empty. A non-empty no_more is the universal signal (top level in both raw=true and raw=false); raw=false additionally gives count=0 + items=[]. Still billed as a normal call.
- Price: $0.01 per request
- ⏱️ Due to WeChat server latency, this endpoint responds slowly; please set your client request timeout to 30 seconds — a timeout that is too small may result in being billed without receiving the response.
- ⚠️ Large-integer ID precision: IDs such as docID / feedNonceId in the response are 64-bit big integers beyond JavaScript's safe-integer range (2^53-1). Always receive / pass such IDs as strings (parse JSON with json-bigint or read them as text), never through JS Number. Swagger UI rounds the trailing digits of huge integers in its docs view — this is expected and does not affect the actual data returned by the API.
Parameters:
- keyword: Search keyword (1-100 chars after trimming). E.g. 人民日报
- business_type: Optional, default all. Vertical string key (18 total). Verified to return data: all / account / article / video / sticker. The rest (underline / encyclopedia / live_stream / comment / listen / news / photos / book / moments / image / mini_game / weixin_index / ai_search) are gated server-side per account & region and currently always come back empty. Any value outside this list is rejected (422).
- sort: Optional, default default. Sort (result page "sort" dropdown, common to all verticals) — default/0 (relevance) / latest/1 (newest) / hot/2 (most liked); string key or integer, invalid values rejected (400).
- publish_time: Optional, default all. Publish time (result page "time" dropdown, common) — all/0 / day/1 / week/2 / half_year/3; string key or integer, invalid values rejected (400).
- offset: Optional, default 0 (>=0). Pass 0 for the first page; use cursor to paginate — offset alone does not work (it returns the first page every time).
- cursor: Optional. Pagination cursor. Leave empty for the first page; for the next page pass back the cursor returned in the previous response (repeat the same sort / publish_time), and use continue_flag to check whether there are more pages.
- raw: Optional, default True. True=raw search response; False=simplified parsing.
💡 The "duration" filter specific to the video vertical is available at /fetch_search_videos (business_type=video + duration).
Return:
- Search result list (structure varies slightly by vertical)
Typical chains:
- 💡 To get Channels video detail (media download address / decode_key): video result items from search (video / all) do not contain media directly — only exportId (+ jumpInfo.extInfo.feedNonceId). To download / decrypt: take exportId → call the Channels V2 endpoint /api/v1/wechat_channels/v2/fetch_video_detail (pass export_id) to get media (url / url_token / decode_key) and the plain username. See that endpoint's docs for the full chain and decryption notes.
- The jumpInfo.userName (gh_…) of official account result items can be fed into the MP V2 endpoints /api/v1/wechat_mp/v2/fetch_account_profile / fetch_account_articles.
Response structure & JSON Path:
raw=false (simplified, snake_case):
- Keyword echo: $.data.keyword
- Effective vertical code (e.g. account=33554499): $.data.business_type
- Total results: $.data.total — the maximum totalCount scanned across every box and subBox (a vertical's totalCount hangs off subBoxes; the All tab has no single total, so this is the largest contributing box). null on a zero-result page (no positive totalCount anywhere), so it is always null for the always-empty verticals
- Has next page: $.data.continue_flag
- Pagination cursor (pass back as the cursor parameter for the next page): $.data.cursor
- Server-side offset (informational only; passing it back alone does not paginate): $.data.offset
- Results on this page: $.data.count
- Flattened result item list (items of all boxes / subBoxes merged): $.data.items[]
- Server-issued vertical tab list (only present for business_type=all, null otherwise): $.data.categories[] — each entry has .type / .word / .extra_kvs. This is the authoritative list of verticals actually available to the serving account. Note .type is the vertical's integer code (e.g. 33554499) while this endpoint's business_type accepts only the 18 string keys above — an integer is rejected (422), so map it yourself (e.g. 33554499 → account).
- Server wording for a zero-result page: $.data.no_more (present = a legitimate empty result from WeChat, not a failed call)
- Single item (N is the index; fields vary by vertical; below is an account example):
- Title (with <em> highlight): $.data.items[N].title
- Description: $.data.items[N].desc
- Document id: $.data.items[N].docID
- Type name (e.g. "公众号"): $.data.items[N].accTypeName
- Jump info: $.data.items[N].jumpInfo (.userName = gh_…, .nickName, .signature)
- Video vertical items also carry exportId (+ jumpInfo.extInfo.feedNonceId) → see "Typical chains" above
raw=true (raw, default for this endpoint):
- data top level: keyword / business_type / results (items are not flattened) / total / continue_flag / offset / cursor / categories / no_more (total / categories / no_more are top level in both modes — the key sets are aligned)
- Full result set: $.data.results
- Result box array: $.data.results.data[]; each box's results are in $.data.results.data[M].items[] and $.data.results.data[M].subBoxes[K].items[] — the items[] of raw=false is the flattened merge of these.
- Has next page: $.data.continue_flag (top level for both raw=true / raw=false)
- Pagination cursor: $.data.cursor — pass it back as the cursor parameter for the next page; $.data.offset is only the server-side offset and passing it back alone does not paginate
- Read the total from the top level $.data.total (present in both raw modes) — it is the maximum of $.data.results.data[M].totalCount and $.data.results.data[M].subBoxes[K].totalCount; a vertical's number hangs off subBoxes, so reading only the top-level box's totalCount yields null
- For result iteration, prefer raw=false (items already flattened) for cleaner paths.
post/api/v1/wechat_search/v2/fetch_search
Request
- Base URLhttps://api.tikhub.io (the document declares no server; this is the origin it was published from)
- AuthHTTP bearer
Request body
Example request
{
"keyword": "人民日报",
"business_type": "account"
}Response
Successful Response
Changes
- info
added the optional property
//to the response with the status - info
added the optional property
//to the response with the status
- info
- info
added the new
ai_searchenum value to the request property - info
added the new
commentenum value to the request property - info
added the new
mini_gameenum value to the request property - info
added the new
photosenum value to the request property - info
added the new
stickerenum value to the request property - info
added the new
underlineenum value to the request property
- info