---
title: "蒲公英笔记博主广场（找博主/选号）/PGY note blogger square"
method: POST
path: "/api/v1/xiaohongshu/pgy/get_blogger_list"
tags: ["Xiaohongshu-PGY-API"]
---

# 蒲公英笔记博主广场（找博主/选号）/PGY note blogger square

`POST /api/v1/xiaohongshu/pgy/get_blogger_list`

## Request

- Base URL: `https://api.tikhub.io` (the document declares no server; this is the origin it was published from)
- URL: `https://api.tikhub.io/api/v1/xiaohongshu/pgy/get_blogger_list`
- Auth: HTTP bearer

# [中文]
### 用途:
- 复刻蒲公英网页「找博主 → 笔记博主广场」的搜索与筛选，约 60 个筛选条件按 7 组传入
- 每条结果内嵌约 140 个字段（报价、阅读/互动中位数、直播场次等），通常不必再逐个调博主详情类接口

### 1. 搜索与排序
| 参数 | 说明 |
|---|---|
| `keyword` | 搜索词；空=只筛选不搜索 |
| `search_type` | `1`=按笔记关键词找博主（默认）`0`=按昵称搜 |
| `column` | 排序字段，共 **34 个**：`comprehensiverank`=智能排序（默认） `fansCount`=粉丝数 `price`=全部报价 `clickNum`=阅读中位数-日常 `mEngagementNum`=互动中位数-日常 `accumCommonImpMedinNum30d`=曝光中位数-日常 `accumPicCommonImpMedinNum30d`=图文曝光中位数-日常 `accumVideoCommonImpMedinNum30d`=视频曝光中位数-日常 `pictureClickMidNum`=图文阅读中位数-日常 `videoClickMidNum`=视频阅读中位数-日常 `pictureInterMidNum`=图文互动中位数-日常 `videoInterMidNum`=视频互动中位数-日常 `thousandLikePercent30`=千赞笔记比例 `pictureThousandLikePercent30`=图文千赞比例 `videoThousandLikePercent30`=视频千赞比例 `hundredLikePercent30`=百赞笔记比例 `pictureHundredLikePercent30`=图文百赞比例 `videoHundredLikePercent30`=视频百赞比例 `videoFinishRate`=视频完播率 `accumCoopImpMedinNum30d`=曝光中位数-合作 `readMidCoop30`=阅读中位数-合作 `interMidCoop30`=互动中位数-合作 `mCpuvNum30d`=外溢进店中位数-合作 `picturePrice`=图文报价 `videoPrice`=视频报价 `pictureReadCost`=图文预估阅读单价 `videoReadCost`=视频预估阅读单价 `inviteReply48hNumRatio`=邀约48h回复率 `fans30GrowthRate`=粉丝量变化幅度 `fansActiveIn28dLv`=活跃粉丝占比 `fansEngageNum30dLv`=互动粉丝占比 `kliveCnt30d`=近30天直播场次 `avgLiveViewerNum`=场均观播人数 `avgAgmv90d`=场均销售额 |
| `sort` | `desc` 降序（默认）/ `asc` 升序 |
| `brand_user_id` | 品牌方/广告主用户ID，可不填。带上后按该品牌做个性化推荐与已合作标记 |
| `similar_user_id` | **找相似博主**：填一个博主的 userId，返回与他相似的博主，**并且可以叠加下面所有筛选** |
| `similar_word` | 配合 `similar_user_id` 的博主昵称，仅用于回显、不影响结果 |

> `similar_user_id` 与 `/get_blogger_similar` 是两条不同链路：前者走本接口、**能叠加筛选**；
> 后者只按相似度返回、不支持筛选、每页最多 4 条。想「找相似 + 再按粉丝量/报价筛」用前者。

### 2. 筛选分组
筛选按维度分成 7 组，每组是一个对象：

| 分组 | 覆盖的筛选 |
|---|---|
| `goal` | 营销目标（曝光 / 种草 / 转化 × 指标 × 成本或规模） |
| `blogger` | 博主类目、人设、擅长内容、内容题材、二十大人群、行业特色画像、预估消费行为、笔记类目、性别、地域、签约情况 |
| `fans` | 粉丝量、粉丝年龄、粉丝性别、粉丝地域、婚恋状态、消费水平、母婴阶段、手机价格、手机品牌 |
| `note` | 日常笔记的曝光/阅读/互动中位数、千赞笔记比例、笔记类型 |
| `coop` | 合作报价、合作订单数、合作信用度、近期合作行业与品牌、传播规模、预估 CPM / 阅读单价 / 互动单价、外溢进店单价 |
| `live` | 近 30 天直播场次、场均观播人数、场均销售额 |
| `flags` | 明星、优质博主、新锐博主、笔记+直播均可合作、意向行业匹配、行业推荐博主、热门活动、剔除低活/掉粉/已合作/已邀约 |

**数值区间**统一写成 `{"min": 10000, "max": 50000}`。两端都可省略：省略 `min` 视为 0，
省略 `max` 视为不限；但两端全空会 422。

### 3. 各筛选填什么值

筛选值分三类：**照表填的固定枚举**、**要先查字典的**、**直接填名称的**。
填错不会报错，只会安静返回 0 条 —— 所以拿不到结果时先回来核对这一节。

#### 3.1 固定枚举（照表填，填表外的值直接 422）

左列是请求 JSON 里的路径，例如 `fans.age` 就是 `{"fans": {"age": 3}}`。

| 参数路径 | 对应筛选 | 全部取值 |
|---|---|---|
| `search_type` | 搜笔记 / 搜昵称 | `1`=搜笔记（默认）`0`=搜昵称 |
| `sort` | 排序方向 | `desc`=降序（默认）`asc`=升序 |
| `blogger.gender` | 性别 | `男` `女` |
| `blogger.signed` | 签约情况 | `-1`=不限 `0`=个人博主 `1`=机构博主 |
| `fans.age` | 粉丝年龄 | `0`=不限 `1`=<18 `2`=18~24 `3`=25~34 `4`=35~44 `5`=>44 占比高 |
| `fans.gender` | 粉丝性别 | `0`=不限 `1`=男性占比高 `2`=女性占比高 |
| `fans.marital_status` | 婚恋状态 | `-1`=不限 `0`=未婚 `1`=已婚 |
| `fans.consumption_level` | 消费水平 | `-1`=不限 `0`=低消费 `1`=中消费 `2`=高消费 |
| `fans.child_age`（数组） | 母婴阶段 | `0`=备孕 `2`=0-6月 `3`=7-12月 `4`=1-3岁 `5`=4-6岁 `6`=7-12岁 `7`=孕早期 `8`=孕晚期 |
| `fans.device_price`（数组） | 手机价格 | `0-999` `1000-1999` `2000-2999` `3000-3999` `4000-4999` `5000-5999` `6000-6999` `7000-7999` `8000+` |
| `fans.device_brand`（数组） | 手机品牌 | `苹果` `华为` `OPPO` `VIVO` `荣耀` `小米` `一加` `魅族` `中兴` `联想` |
| `note.note_type` | 笔记类型 | `0`=不限 `1`=图文笔记为主 `2`=视频笔记为主 |
| `coop.industry` | 近期合作行业 | `不限` `美妆个护` `食品饮料` `母婴` `3c及电器` `日用百货` `服装配饰` `互联网` `生活服务` `家居建材` `汽车` |
| `column` | 排序字段 | 34 个，见上面第 1 节的排序字段表 |
| `goal.market_target` | 营销目标 | 见该字段在 Swagger 中的说明（曝光/种草/转化 × 指标 × 成本或规模） |

#### 3.2 需要先查字典的筛选

下列筛选的可选项由平台维护、会随运营调整。先调 **`/get_blogger_filter_options`**
拿字典，再按对应层级填。字典基本不变，**建议缓存后复用**，不必每次筛选前都调。

**这些参数的写法不统一，下表是唯一可靠的对照。** 大多数是「裸名、任意层级、可混填」，
但有三个例外（`audience20` 要复合串、`note_scene` / `consumer_behavior` 要全路径、
`industry_crowd` 要数字 ID），照着别的参数的写法填会失败。

| 参数路径 | 对应筛选 | 字典键 | 填什么 | 能填哪一层 |
|---|---|---|---|---|
| `blogger.content_tag` | 博主类目 | `contentTags` | 裸名 | 一级 / 二级，可混填 |
| `blogger.personal_tags` | 博主人设 | `personalTagsV2` | 裸 `tagName` | 一级 / 二级 / 叶子，可混填 |
| `blogger.feature_tags` | 擅长内容 | `featureTags` | 裸名 | 一级 / 二级，可混填 |
| `blogger.location` | 地域（博主所在地） | `areas` | 裸地名 | 省 / 市 / 区县，可混填 |
| `fans.location` | 粉丝地域 | `fansAreas` | 裸地名 | 省 / 市（**没有区县**），可混填 |
| `blogger.content_theme` | 内容题材 | `contentTheme` | 裸 `label` | **只有一级**，子项无效 |
| `blogger.audience20` | 二十大人群 | `audience20` | 一级裸名，或 `"一级 一级-二级"` | **二级必须用复合串** |
| `blogger.note_scene` | 笔记类目 | `contentScenes` | **含根节点的空格全路径** | 任意深度 |
| `blogger.consumer_behavior` | 预估消费行为 | `consumerBehavior` | **含根节点的空格全路径** | 任意深度 |
| `blogger.industry_crowd` | 行业特色画像 | `automotiveIndustryTag` | **最深层叶子的 `value`（数字 ID）** | 只有叶子 |
| `flags.industry_first` / `industry_second` | 行业（**不是**博主类目） | `industryTags` | 裸名，**单值不是数组** | 一级 / 二级各一个 |
| `flags.activity_codes` | 热门活动 | `activities` | 每项的 `code` | — |

**数组型参数一律是「或」**，多个值取并集去重；上限 30 项、单项 64 字符。

##### 填错了会怎样：两种失败，都不报错

| 失败形态 | 表现 | 哪些参数 |
|---|---|---|
| **过度过滤** | `total=0`，看起来像「真的没有博主」 | 除 `consumer_behavior` 外的全部 |
| **静默不过滤** | 返回**未经筛选**的结果，你以为筛了其实没筛 | `consumer_behavior` |

实测（粉丝 100 万~120 万，无其它筛选时 `total=455`）：

* `content_tag=["护肤品"]`（多一个字）→ `total=0`
* `fans.location=["浙江省"]`（多一个「省」）→ `total=0`
* `audience20=["挑战极限者"]`（照字典 label 裸填）→ `total=0`
* `note_scene=["选车攻略"]`（只填末段，没填全路径）→ `total=0`
* `industry_crowd=["身心疗愈兴趣人群"]`（填了 label 不是数字 ID）→ `total=0`
* `consumer_behavior=["保时捷911"]`（裸叶子名）→ **`total=455`，等于没筛**
  ；`["zzz不存在zzz"]` 同样 `455`。**只有全路径写法才真的生效**
  （`["内容行为预估 汽车"]` → `54`）。

看到 `total=0` 时**先怀疑取值写法**，再怀疑「真的没有符合条件的博主」；
看到结果多得可疑时，检查是不是 `consumer_behavior` 被静默忽略了。
所有取值都应从 `/get_blogger_filter_options` 里**原样复制**，不要手写。

##### 博主类目（`blogger.content_tag`）怎么多选

网页上勾了好几个类目、有的勾「全部」有的只勾几个子项时，就按下面这样填。

`contentTags` 是两层树：`taxonomy1Tag` 是一级（美妆、护肤、个人护理…共 28 个），
`taxonomy2Tags[]` 是它的子项。**一级名和二级名混在同一个扁平数组里就行**，
不用分开传、也没有嵌套结构。二级名全局唯一、也不与一级名重名，所以不会有歧义。

举个网页上的实际场景：**美妆勾了「全部」，护肤只勾了 2 个子项，个人护理也勾了 2 个**：

```json
{"blogger": {"content_tag": ["美妆", "面部保养", "面部清洁", "头发产品", "身体护理"]}}
```

其中 `美妆` 是一级名（= 该类目全部），后面四个是二级名
（`面部保养`/`面部清洁` 属于护肤，`头发产品`/`身体护理` 属于个人护理）。

实测规则（固定同一组粉丝量条件、只改类目，对比返回的 `total`）：

| 传什么 | total | 说明 |
|---|---|---|
| `["面部保养"]` | 200 | — |
| `["面部清洁"]` | 2 | — |
| `["面部保养","面部清洁"]` | **201** | **多个之间是「或」**，并集去重（有 1 个博主两个都占） |
| `["护肤"]` | **379** | 一级名 = 该类目全部 |
| 护肤的全部 4 个二级 | **328** | **比一级名少 51 个** |
| `["护肤","面部保养"]` | 379 | 父子同填不报错，但子项本就被父项包含，等于只填父 |
| `["护肤品"]`（错别字） | **0** | **静默返回 0 条、不报错** |

两条要特别注意：

* **勾「全部」就填一级名，别自己展开成它的全部二级名。** 上表里护肤展开后少了 51 个博主
  —— 有些博主只挂了一级类目、没有二级标签，展开就把他们漏掉了。
* **名字填错不会报错，只会返回 0 条。** 务必从 `/get_blogger_filter_options` 的
  `contentTags` 里**原样复制**，别手写。看到 `total=0` 时先怀疑名字，再怀疑「真的没有博主」。

数组最多 30 项，单个标签名最长 64 字符。

##### 地域（`blogger.location` / `fans.location`）怎么多选

**和类目一样是扁平的**：直接填地名，省和市可以混在一起，多个之间是「或」。
网页上**浙江勾了「全部」，江苏只勾了 2 个市，广东也勾了 2 个**：

```json
{"fans": {"location": ["浙江", "南京", "苏州", "广州", "深圳"]}}
```

要选整个省就填省名，**不用展开成它下面所有的市**。博主地域多一层区县，写法一样：

```json
{"blogger": {"location": ["上海", "杭州", "上城区"]}}
```

上游对这两个字段的要求其实**相反**（博主地域吃裸地名、粉丝地域吃完整路径
`"中国 浙江 杭州"`），上游服务会各自转好，你填裸名就行。`fans.location` 侧
实际会转成：`浙江`→`中国 浙江`（**不会**展开成 11 个市）、`杭州`→`中国 浙江 杭州`、
`吉林`→`中国 吉林` **和** `中国 吉林 吉林`（省市同名，两条都发，「或」）。
字典里没有的名字（如 `浙江省`）原样透传 → **`total=0`**。

注意 `fans.location` 这棵树**只到市，没有区县**（`blogger.location` 才有）。

##### 二十大人群（`blogger.audience20`）怎么多选

**这个和上面两个不一样，不是扁平裸名。** 二级必须写成 `"一级 一级-二级"` ——
一级名要出现两次，中间一个空格，后半段用连字符。

网页上**自在户外勾了「全部」，自由畅行只勾了 2 个子项，运动焕活也勾了 2 个**
（实测 `total=169`）：

```json
{"blogger": {"audience20": [
    "自在户外",
    "自由畅行 自由畅行-都市漫游家", "自由畅行 自由畅行-静奢新贵",
    "运动焕活 运动焕活-轻松健体派", "运动焕活 运动焕活-线条雕塑家"
]}}
```

注意对比：**勾「全部」的那个只写一级裸名，勾了具体子项的每一个都要写成复合串。**

实测（基线 455）：`["自在户外"]`=135、`["自在户外 自在户外-挑战极限者"]`=9、
**`["挑战极限者"]`=0（照字典 label 裸填，静默返回 0）**、两个复合值=18（「或」）、
`["自由畅行","自在户外 自在户外-挑战极限者"]`=176（单独 `自由畅行`=175，可混填）。

##### 博主人设 / 擅长内容 —— 任意层级都能填

`personalTagsV2` 是**三层**树，`featureTags` 是两层，**任意一层都能填、可以混填**。
想要整组就填那一组的名字，不用把叶子一个个列出来。

```json
{"blogger": {"personal_tags": ["职业身份", "妈妈", "留学背景"],
             "feature_tags": ["风格", "vlog"]}}
```

实测：`personal_tags` 一级 `职业身份`=91、二级 `互联网`=8、叶子 `妈妈`=31，
`["职业身份","妈妈"]`=114（并集去重）；`feature_tags` 一级 `形式`=183、`风格`=37，
二级 `vlog`=136、`韩系`=19，`["风格","vlog"]`=150。

##### 内容题材（`blogger.content_theme`）—— 只能填一级

例外：**只有一级 `label` 有效，填子项一律 0 条**。实测 `["通用"]`=366，
其子项 `["口播"]`=**0**。想按子项筛只能退而求其次用 `feature_tags`。

##### 笔记类目 / 预估消费行为 —— 必须填「含根节点的空格全路径」

这两个**不能填裸名**，要把从根节点到目标节点每一层用空格连起来，可以停在任意深度：

```json
{"blogger": {
    "note_scene": ["汽车 理性决策", "汽车 理性决策 选车攻略 政策"],
    "consumer_behavior": ["内容行为预估 汽车"]
}}
```

实测 `note_scene`：`["汽车"]`=75、`["汽车 理性决策"]`=23、四级=1，
而只填末段 `["选车攻略"]`=**0**。

⚠️ **`consumer_behavior` 填错的后果和别的参数不一样**：不是返回 0 条，而是
**整个筛选被静默忽略**。实测 `["保时捷911"]`（裸叶子名）和 `["zzz不存在zzz"]`
都返回 455 —— 和完全不加这个筛选一模一样；只有全路径写法才生效
（`["内容行为预估 汽车"]`=54）。用它时**务必用加/不加两次请求的 `total` 对比确认**。
另外这个字典**按账号灰度**，有些账号取到 `null`，那就用不了。

##### 行业特色画像（`blogger.industry_crowd`）—— 只能填数字 ID

四层树，但**只有最深层叶子的 `value` 有效，而且是数字 ID**，不是 label。
实测 `["17121263"]`=110，填对应 label `["身心疗愈兴趣人群"]`=**0**。

##### 行业（`flags.industry_first` / `industry_second`）—— 单值，不能多选

这两个是**字符串不是数组**，各只能填一个，且要配套使用（二级必须属于一级）：

```json
{"flags": {"industry_first": "美妆个护", "industry_second": "护肤"}}
```

注意这是「**行业**」，和「博主类目」（`blogger.content_tag`）是两套完全不同的分类。

#### 3.3 直接填名称的筛选

| 参数路径 | 对应筛选 | 怎么填 |
|---|---|---|
| `coop.brand_ids` | 近期合作品牌 | 品牌方 userId 列表（**平台暂无品牌搜索接口，userId 需自行提供**） |

> 想按粉丝人群圈博主，用 `blogger.audience20`（二十大人群）而不是 `coop.brand_ids`。
#### 3.4 区间型字段（值写成 `{"min": x, "max": y}`）
| 参数路径 | 对应网页筛选 |
|---|---|
| `fans.count` | 粉丝量 |
| `note.imp_median` / `note.read_median` / `note.inter_median` | 日常笔记的曝光 / 阅读 / 互动中位数 |
| `note.thousand_like_percent` | 千赞笔记比例（单位 %） |
| `coop.pic_price` / `coop.video_price` | 合作报价（图文 / 视频一口价，元） |
| `coop.order_count` | 合作订单数 |
| `coop.reply_48h_ratio` | 合作信用度 → 48h 邀约回复率（%） |
| `coop.imp_median` / `coop.read_median` / `coop.inter_median` | 传播规模（合作笔记的中位数） |
| `coop.cpuv` / `coop.estimate_cpuv` | CPUV / 外溢进店单价 |
| `coop.pic_cpm` / `coop.video_cpm` | 预估 CPM（图文 / 视频） |
| `coop.pic_read_price` / `coop.video_read_price` | 预估阅读单价（图文 / 视频） |
| `coop.pic_engage_cost` / `coop.video_engage_cost` | 预估互动单价（图文 / 视频） |
| `live.live_count` / `live.avg_viewer` / `live.avg_gmv` | 近 30 天直播场次 / 场均观播 / 场均销售额 |

#### 3.5 开关型字段（值写 `true`）
| 参数路径 | 对应网页筛选 |
|---|---|
| `flags.is_star` / `flags.high_quality` / `flags.new_high_quality` | 明星 / 优质博主 / 新锐博主 |
| `flags.note_and_live` | 笔记 + 直播均可合作 |
| `flags.filter_intention` | 意向行业匹配 |
| `flags.exclude_low_active` / `flags.exclude_fans_down` | 剔除低活 / 掉粉博主 |
| `flags.exclude_cooperated` / `flags.exclude_invited` | 剔除已合作 / 已邀约博主 |
| `coop.exclude_brands` | 配合 `coop.brand_ids`：`true` = 排除与这些品牌合作过的博主 |

此外 `flags.industry_first` / `flags.industry_second` 是行业推荐博主的一级/二级行业名，
`flags.activity_codes` 是热门活动 code（取自 `/get_blogger_filter_options` 的 `activities`）。
枚举型字段（性别、签约情况、粉丝年龄等）的完整取值见各字段在 Swagger 中的说明。

### 4. 更多示例

本页最下方的 **[示例/Example]** 区里有 **15 条可直接照抄的请求体**，覆盖关键词搜索、画像选号、预算/性价比、直播带货、找相似博主、地域投放等场景，外加一节**常见错误写法**（那几种不会报错、只会安静返回 0 条）。

### 5. 翻页
| 参数 | 取值 | 说明 |
|---|---|---|
| `page_num` | **1-250** | 上游只放出前 5000 条 = 250 页，第 251 页起返回空数组 |
| `page_size` | **1-20** | 蒲公英网页固定 20。**建议保持 20**：上游会对当页结果做二次过滤（下架、不可合作等），取太小可能整页被过滤成 0 条 |

翻页只改 `page_num`，其余筛选**必须原样保持不变**，否则两页口径不一致会重复或遗漏。
**`total` 不是命中总数，是可翻上限**，恒为 `5000`。想缩小结果集请加筛选，而不是指望翻到底。

### 6. 返回
- `kols[]`: 命中的博主，每条约 140 个字段
    - 身份: `userId` / `name` / `redId` / `headPhoto` / `location` / `gender`
    - 标签: `contentTags` / `featureTags` / `personalTags` / `tradeType`
    - 数据: `fansNum` / `clickMidNum` 阅读中位数 / `interMidNum` 互动中位数 / `fans30GrowthRate` 粉丝增速 / `kliveCnt30d` 直播场次
    - 商业: `picturePrice` / `videoPrice` / `lowerPrice` / `estimatePictureCpm` / `cooperateState`
- `total`: 可翻上限（恒 5000）
- `trackId` / `highlightWords`

### 7. 注意:
- **字段名写错会直接 422**，不会被静默忽略
- 树形筛选的可选值请用 `/get_blogger_filter_options` 实时拉取，不要写死

### 响应结构（取数要剥两层）:
- `resp["data"]` 是蒲公英原样信封（含 `code`/`msg`/`success`），业务数据还在它里面一层
- 正确取法：`data = resp["data"]["data"]`；下面「返回」列的字段都在这一层下
### 计费:
- HTTP 200 → **计费**。包含「查无结果」（`data` 为 null，如 ID 不存在或筛选无命中）——
  请求已送达蒲公英，同样扣费，不要因为拿不到数据就重试
- HTTP 400 → 不计费（参数格式非法，或上游资源不足/异常）

# [English]
### Purpose:
- Full replica of PGY "Find Bloggers → Note Blogger Square"; ~60 filters grouped into 7 objects
- Each result embeds ~140 fields (quotes, read/engagement medians, live sessions, ...)

### 1. Search & Sort
| Param | Description |
|---|---|
| `keyword` | Search term; empty = filter only |
| `search_type` | `1`=find bloggers by note keyword (default), `0`=search by nickname |
| `column` | Sort field, **34 options** — see the Chinese table above for the full list with labels |
| `similar_user_id` | **Find similar bloggers**: pass a blogger's userId; results can be **combined with every filter below** |
| `similar_word` | Nickname echo for `similar_user_id`; does not affect results |
| `sort` | `desc` (default) / `asc` |
| `brand_user_id` | Brand/advertiser user ID, optional; enables brand-specific recommendation and "already cooperated" marks |

### 2. Filter Groups
| Group | Covers |
|---|---|
| `goal` | Marketing goal (exposure / seeding / conversion × metric × cost or scale) |
| `blogger` | Category, persona, expertise, content theme, top-20 crowds, industry-specific portrait, estimated consumption, note category, gender, location, agency status |
| `fans` | Fans count, age, gender, location, marital status, consumption level, parenting stage, phone price, phone brand |
| `note` | Organic note impression / read / engagement medians, 1000-like ratio, note type |
| `coop` | Quotes, order count, credit score, recent industries & brands, reach scale, estimated CPM / read cost / engagement cost, store-visit cost |
| `live` | Live sessions in last 30d, average viewers, average GMV |
| `flags` | Celebrity, high-quality, rising bloggers, note+live capable, intention match, industry recommendation, campaigns, exclude inactive / fans-declining / already-cooperated / already-invited |

**Numeric ranges** are written as `{"min": 10000, "max": 50000}`. Either bound may be omitted
(omitting `min` means 0, omitting `max` means unlimited), but omitting both returns 422.

### 3. What Values Go In Each Filter

Filter values come in three kinds: **fixed enums** (copy from the table),
**dictionary-backed** (fetch first), and **plain names**.
A wrong value does not raise — it just silently returns 0 results, so check
this section first when a query comes back empty.

#### 3.1 Fixed enums (values outside the table return 422)

| Path | Web filter | Allowed values |
|---|---|---|
| `search_type` | Search notes / nickname | `1`=notes (default) `0`=nickname |
| `sort` | Sort direction | `desc` (default) / `asc` |
| `blogger.gender` | Gender | `男` `女` |
| `blogger.signed` | Agency status | `-1`=any `0`=individual `1`=agency |
| `fans.age` | Fans age | `0`=any `1`=<18 `2`=18~24 `3`=25~34 `4`=35~44 `5`=>44 |
| `fans.gender` | Fans gender | `0`=any `1`=male-skewed `2`=female-skewed |
| `fans.marital_status` | Marital status | `-1`=any `0`=single `1`=married |
| `fans.consumption_level` | Consumption level | `-1`=any `0`=low `1`=mid `2`=high |
| `fans.child_age` (array) | Parenting stage | `0` `2` `3` `4` `5` `6` `7` `8` |
| `fans.device_price` (array) | Phone price | `0-999` … `8000+` |
| `fans.device_brand` (array) | Phone brand | `苹果` `华为` `OPPO` `VIVO` `荣耀` `小米` `一加` `魅族` `中兴` `联想` |
| `note.note_type` | Note type | `0`=any `1`=image-text `2`=video |
| `coop.industry` | Recent industry | `不限` `美妆个护` `食品饮料` `母婴` `3c及电器` `日用百货` `服装配饰` `互联网` `生活服务` `家居建材` `汽车` |
| `column` | Sort field | 34 options — see the sort table in section 1 |

#### 3.2 Dictionary-backed filters

Call **`/get_blogger_filter_options`** first, then fill values from the matching
level. The dictionary rarely changes — **cache it** rather than calling it before
every search.

**These parameters do NOT share one convention — this table is the only reliable
reference.** Most take plain names at any level and can be mixed, but three are
exceptions (`audience20` needs a compound string, `note_scene` /
`consumer_behavior` need a full path, `industry_crowd` needs a numeric ID).
Copying the convention from another parameter will fail.

| Path | Web filter | Dict key | What to pass | Which level |
|---|---|---|---|---|
| `blogger.content_tag` | Blogger category | `contentTags` | plain name | level 1 / 2, mixable |
| `blogger.personal_tags` | Persona | `personalTagsV2` | plain `tagName` | level 1 / 2 / leaf, mixable |
| `blogger.feature_tags` | Expertise | `featureTags` | plain name | level 1 / 2, mixable |
| `blogger.location` | Blogger location | `areas` | plain place name | province / city / district, mixable |
| `fans.location` | Fans location | `fansAreas` | plain place name | province / city (**no district**), mixable |
| `blogger.content_theme` | Content theme | `contentTheme` | plain `label` | **level 1 only**, children invalid |
| `blogger.audience20` | Top-20 crowds | `audience20` | level-1 name, or `"L1 L1-L2"` | **level 2 needs the compound form** |
| `blogger.note_scene` | Note category | `contentScenes` | **space-joined full path incl. root** | any depth |
| `blogger.consumer_behavior` | Estimated consumption | `consumerBehavior` | **space-joined full path incl. root** | any depth |
| `blogger.industry_crowd` | Industry portrait | `automotiveIndustryTag` | **deepest leaf `value` (numeric ID)** | leaves only |
| `flags.industry_first` / `industry_second` | Industry (**not** blogger category) | `industryTags` | plain name, **single value, not an array** | one each |
| `flags.activity_codes` | Campaigns | `activities` | each item's `code` | — |

**Array parameters are always OR-ed** (union, de-duplicated); max 30 items,
64 characters per item.

##### Two ways a wrong value fails — neither raises an error

| Failure | What you see | Which parameters |
|---|---|---|
| **Over-filtering** | `total=0`, looks like "no such bloggers" | everything except `consumer_behavior` |
| **Silently not filtering** | unfiltered results — you think it applied, it didn't | `consumer_behavior` |

Measured (fans 1.0M–1.2M, `total=455` with no other filter):

* `content_tag=["护肤品"]` (one extra character) → `total=0`
* `fans.location=["浙江省"]` (extra 省) → `total=0`
* `audience20=["挑战极限者"]` (bare level-2 label) → `total=0`
* `note_scene=["选车攻略"]` (last segment only, not the full path) → `total=0`
* `industry_crowd=["身心疗愈兴趣人群"]` (label instead of numeric ID) → `total=0`
* `consumer_behavior=["保时捷911"]` (bare leaf) → **`total=455` — no filtering at
  all**; `["zzz不存在zzz"]` also returns `455`. **Only the full-path form works**
  (`["内容行为预估 汽车"]` → `54`).

When you get `total=0`, suspect the value format before concluding "no such
bloggers". When you get suspiciously many results, check whether
`consumer_behavior` was silently ignored. Always copy values **verbatim** from
`/get_blogger_filter_options`.

> **How to fill location** (the most common mistake): just pass the plain place
> name — `["上海"]`, `["杭州"]`, `["上城区"]` all work, OR-ed together.
> A full path (`"中国 浙江 杭州"`) also works; upstream normalizes it.
> The only difference: `fans.location` has **no district level** — city is the finest.

##### Selecting multiple blogger categories (`blogger.content_tag`)

`contentTags` is a two-level tree: `taxonomy1Tag` is level 1 (28 of them — 美妆,
护肤, 个人护理 …) and `taxonomy2Tags[]` are its children. **Mix level-1 and level-2
names in the same flat array** — there is no nesting and nothing to send separately.
Level-2 names are globally unique and never collide with level-1 names, so a flat
array is unambiguous.

A real case from the web UI: **美妆 = "all", plus 2 children of 护肤 and 2 of 个人护理**:

```json
{"blogger": {"content_tag": ["美妆", "面部保养", "面部清洁", "头发产品", "身体护理"]}}
```

`美妆` is a level-1 name (= the whole category); the other four are level-2 names.

Measured behaviour (same fan-count filter throughout, only the category changed,
comparing the returned `total`):

| Value | total | Note |
|---|---|---|
| `["面部保养"]` | 200 | — |
| `["面部清洁"]` | 2 | — |
| `["面部保养","面部清洁"]` | **201** | **Multiple tags are OR-ed**, de-duplicated (1 blogger has both) |
| `["护肤"]` | **379** | A level-1 name means the whole category |
| all 4 children of 护肤 | **328** | **51 fewer than the level-1 name** |
| `["护肤","面部保养"]` | 379 | Parent + child is accepted but pointless — the child is already covered |
| `["护肤品"]` (typo) | **0** | **Silently returns 0 — no error** |

Two things to watch out for:

* **For "all", pass the level-1 name — do NOT expand it into all its children.**
  Expanding 护肤 above loses 51 bloggers: some carry only the level-1 category and
  have no level-2 tag, so expanding drops them.
* **A misspelled name is not rejected — it just returns 0 results.** Copy names
  verbatim from `contentTags` in `/get_blogger_filter_options`. When you see
  `total=0`, suspect the spelling before concluding "no such bloggers".

Max 30 items per array; each tag name at most 64 characters.

##### Locations (`blogger.location` / `fans.location`)

**Flat, like categories**: pass plain place names; provinces and cities can be
mixed; multiple values are OR-ed. The web-UI case — **浙江 set to "all", 2 cities
picked under 江苏, 2 more under 广东**:

```json
{"fans": {"location": ["浙江", "南京", "苏州", "广州", "深圳"]}}
```

For a whole province just pass the province name — **do not expand it into all its
cities**. Blogger location has one extra level (district), same syntax:

```json
{"blogger": {"location": ["上海", "杭州", "上城区"]}}
```

Upstream actually wants **opposite** formats for these two fields (blogger location
takes bare names, fans location takes full paths like `"中国 浙江 杭州"`); the
upstream service normalizes each, so plain names are all you need. On the
`fans.location` side: `浙江`→`中国 浙江` (**not** expanded into its 11 cities),
`杭州`→`中国 浙江 杭州`, `吉林`→ both `中国 吉林` and `中国 吉林 吉林` (a province
and a city share that name; both are sent, OR-ed). A name absent from the
dictionary (e.g. `浙江省`) is passed through as-is → **`total=0`**.

Note `fans.location` only goes down to **city — no districts** (`blogger.location`
has them).

##### Top-20 crowds (`blogger.audience20`)

**This one is NOT a flat plain name.** Level 2 must be written as
`"L1 L1-L2"` — the level-1 name appears twice, separated by a space, joined by a
hyphen in the second half.

The web-UI case — **自在户外 set to "all", 2 children under 自由畅行, 2 under
运动焕活** (measured `total=169`):

```json
{"blogger": {"audience20": [
    "自在户外",
    "自由畅行 自由畅行-都市漫游家", "自由畅行 自由畅行-静奢新贵",
    "运动焕活 运动焕活-轻松健体派", "运动焕活 运动焕活-线条雕塑家"
]}}
```

Note the contrast: **the "all" one is a bare level-1 name; every individually
picked child must use the compound form.**

Measured (baseline 455): `["自在户外"]`=135, `["自在户外 自在户外-挑战极限者"]`=9,
**`["挑战极限者"]`=0 (bare level-2 label — silently returns 0)**, two compound
values=18 (OR), `["自由畅行","自在户外 自在户外-挑战极限者"]`=176 (vs 175 for
`自由畅行` alone — mixing works).

##### Persona / Expertise — any level works

`personalTagsV2` is a **three**-level tree, `featureTags` two. **Any level works and
they can be mixed.** For a whole group just pass that group's name — no need to
enumerate its leaves.

```json
{"blogger": {"personal_tags": ["职业身份", "妈妈", "留学背景"],
             "feature_tags": ["风格", "vlog"]}}
```

Measured: `personal_tags` level-1 `职业身份`=91, level-2 `互联网`=8, leaf `妈妈`=31,
`["职业身份","妈妈"]`=114 (union). `feature_tags` level-1 `形式`=183, `风格`=37;
level-2 `vlog`=136, `韩系`=19; `["风格","vlog"]`=150.

##### Content theme (`blogger.content_theme`) — level 1 only

An exception: **only the level-1 `label` works; children always return 0.**
Measured `["通用"]`=366 but its child `["口播"]`=**0**. To filter by a child, use
`feature_tags` instead.

##### Note category / Estimated consumption — full path required

These two **do not take plain names**. Join every level from the root with spaces;
you may stop at any depth:

```json
{"blogger": {
    "note_scene": ["汽车 理性决策", "汽车 理性决策 选车攻略 政策"],
    "consumer_behavior": ["内容行为预估 汽车"]
}}
```

Measured `note_scene`: `["汽车"]`=75, `["汽车 理性决策"]`=23, 4-level=1, while the
last segment alone `["选车攻略"]`=**0**.

⚠️ **`consumer_behavior` fails differently from every other parameter**: instead of
returning 0, **the whole filter is silently ignored**. Measured `["保时捷911"]`
(bare leaf) and `["zzz不存在zzz"]` both return 455 — identical to not passing the
filter at all. Only the full-path form works (`["内容行为预估 汽车"]`=54). When
using it, **compare `total` with and without the filter** to confirm it applied.
This dictionary is also **rolled out per account** — some accounts get `null`, in
which case the filter is unavailable.

##### Industry portrait (`blogger.industry_crowd`) — numeric IDs only

A four-level tree, but **only the deepest leaf's `value` works, and it is a numeric
ID**, not a label. Measured `["17121263"]`=110; the matching label
`["身心疗愈兴趣人群"]`=**0**.

##### Industry (`flags.industry_first` / `industry_second`) — single value

These are **strings, not arrays** — one value each, used together (the level-2 value
must belong to the level-1 one):

```json
{"flags": {"industry_first": "美妆个护", "industry_second": "护肤"}}
```

This is **industry**, a completely different taxonomy from blogger category
(`blogger.content_tag`).

#### 3.3 Plain-name filters

| Path | Web filter | How to fill |
|---|---|---|
| `coop.brand_ids` | Recent brands | Brand userId list (**no brand search endpoint — supply userIds yourself**) |
#### 3.4 Range Fields (value = `{"min": x, "max": y}`)
| Path | Web filter |
|---|---|
| `fans.count` | Fans count |
| `note.imp_median` / `note.read_median` / `note.inter_median` | Organic note impression / read / engagement median |
| `note.thousand_like_percent` | 1000-like note ratio (%) |
| `coop.pic_price` / `coop.video_price` | Sponsored quote (image-text / video, CNY) |
| `coop.order_count` | Sponsored order count |
| `coop.reply_48h_ratio` | 48h invitation reply rate (%) |
| `coop.imp_median` / `coop.read_median` / `coop.inter_median` | Sponsored note medians |
| `coop.cpuv` / `coop.estimate_cpuv` | CPUV / store-visit cost |
| `coop.pic_cpm` / `coop.video_cpm` | Estimated CPM (image-text / video) |
| `coop.pic_read_price` / `coop.video_read_price` | Estimated cost per read |
| `coop.pic_engage_cost` / `coop.video_engage_cost` | Estimated cost per engagement |
| `live.live_count` / `live.avg_viewer` / `live.avg_gmv` | Live sessions / avg viewers / avg GMV |

#### 3.5 Boolean Flags (value = `true`)
| Path | Web filter |
|---|---|
| `flags.is_star` / `flags.high_quality` / `flags.new_high_quality` | Celebrity / high-quality / rising blogger |
| `flags.note_and_live` | Accepts both note and live cooperation |
| `flags.filter_intention` | Industry intention match |
| `flags.exclude_low_active` / `flags.exclude_fans_down` | Exclude inactive / fans-declining bloggers |
| `flags.exclude_cooperated` / `flags.exclude_invited` | Exclude already-cooperated / already-invited |
| `coop.exclude_brands` | With `coop.brand_ids`: `true` = exclude bloggers who worked with those brands |

`flags.industry_first` / `flags.industry_second` are industry names for industry-recommended
bloggers; `flags.activity_codes` are campaign codes from `/get_blogger_filter_options`.

### 4. More Examples

The **[示例/Example]** block at the bottom of this page has **15 ready-to-copy request bodies** covering keyword search, audience targeting, budget/ROI, live commerce, similar-blogger expansion and geo targeting — plus a **common mistakes** section (those silently return 0 results instead of erroring).

### 5. Paging
| Param | Range | Note |
|---|---|---|
| `page_num` | **1-250** | Upstream exposes only the first 5000 items = 250 pages; page 251+ returns an empty array |
| `page_size` | **1-20** | Fixed at 20 on the web page. **Keep it at 20**: upstream post-filters each page, so a small size may leave 0 rows |

Change `page_num` only and keep every other filter identical, otherwise pages overlap or skip.
**`total` is not the match count, it is the paging cap** and is always `5000`.

### 6. Return
- `kols[]`: matched bloggers (~140 fields each)
    - Identity: `userId` / `name` / `redId` / `headPhoto` / `location` / `gender`
    - Tags: `contentTags` / `featureTags` / `personalTags` / `tradeType`
    - Metrics: `fansNum` / `clickMidNum` (read median) / `interMidNum` (engagement median) /
      `fans30GrowthRate` (fan growth) / `kliveCnt30d` (live sessions)
    - Commercial: `picturePrice` / `videoPrice` / `lowerPrice` / `estimatePictureCpm` /
      `cooperateState`
- `total`: paging cap (**always 5000** — not the real match count, see Paging above)
- `trackId` / `highlightWords`

### 7. Notes:
- **Misspelled field names return 422** instead of being silently ignored
- Fetch tree-style option values from `/get_blogger_filter_options` instead of hardcoding them

### Response shape (two layers):
- `resp["data"]` is PGY's raw envelope (with `code`/`msg`/`success`); the payload is one level deeper
- Correct access: `data = resp["data"]["data"]` — every field listed under "Return" lives here
### Billing:
- HTTP 200 → **charged**, including "no result" (`data` is null, e.g. ID not found or filters matched
  nothing): the request did reach PGY, so retrying on empty data just burns quota
- HTTP 400 → not charged (malformed params, or upstream unavailable/error)

# [示例/Example]
```json
// ① 关键词找博主：搜「咖啡」相关笔记的博主，按粉丝数降序；带品牌方 ID 做个性化推荐并剔除已合作过的
{"page_num": 1, "page_size": 20, "keyword": "咖啡", "search_type": 1,
 "column": "fansCount", "sort": "desc",
 "brand_user_id": "5dcfa5370000000001006030",
 "flags": {"exclude_cooperated": true}}

// ② 按昵称找号：search_type=0 时 keyword 匹配的是博主昵称而不是笔记内容
{"page_num": 1, "page_size": 20, "keyword": "小鹿", "search_type": 0}

// ③ 最基础的类目 + 粉丝量筛选：粉丝 1 万-5 万的美妆博主，按互动中位数降序
{"page_num": 1, "page_size": 20, "column": "mEngagementNum",
 "fans": {"count": {"min": 10000, "max": 50000}},
 "blogger": {"content_tag": ["美妆"]}}

// ④ 按画像选号：1 万-5 万粉、粉丝以 25-34 岁女性为主的美妆「妈妈」博主
{"page_num": 1, "page_size": 20,
 "blogger": {"content_tag": ["美妆"], "personal_tags": ["妈妈"]},
 "fans": {"count": {"min": 10000, "max": 50000}, "gender": 2, "age": 3}}

// ⑤ 按预算与响应速度选号：图文报价 1 万-5 万、48h 邀约回复率 ≥80%
{"page_num": 1, "page_size": 20,
 "coop": {"pic_price": {"min": 10000, "max": 50000},
          "reply_48h_ratio": {"min": 80}}}

// ⑥ 按性价比选号：图文报价 ≤5000、阅读中位数 ≥3000 的视频博主，只看优质号并剔除低活
{"page_num": 1, "page_size": 20,
 "note": {"read_median": {"min": 3000}, "note_type": 2},
 "coop": {"pic_price": {"max": 5000}},
 "flags": {"high_quality": true, "exclude_low_active": true}}

// ⑦ 找会直播带货的博主：近 30 天开播 1-5 场、场均销售额 ≥1 万，且笔记+直播均可合作
{"page_num": 1, "page_size": 20,
 "live": {"live_count": {"min": 1, "max": 5}, "avg_gmv": {"min": 10000}},
 "flags": {"note_and_live": true}}

// ⑧ 多维组合：7 个筛选组同时生效（字段名写错会直接 422，不会被静默忽略）
{"page_num": 1, "page_size": 20, "column": "comprehensiverank", "sort": "desc",
 "goal":    {"market_target": "mAccumImpNum"},
 "blogger": {"content_tag": ["美妆"], "gender": "女", "signed": 0},
 "fans":    {"count": {"min": 10000}, "age": 3, "device_brand": ["苹果"]},
 "note":    {"inter_median": {"min": 500}, "thousand_like_percent": {"min": 20}},
 "coop":    {"industry": "美妆个护", "video_price": {"max": 8000}},
 "live":    {"avg_viewer": {"min": 5000}},
 "flags":   {"exclude_cooperated": true}}


// ⑨ 找相似博主：给一个满意的标杆博主，扩一批相似的，**并叠加自己的筛选**
//    （/get_blogger_similar 只按相似度返回、不支持筛选，需要叠加筛选就用这里）
{"page_num": 1, "page_size": 20,
 "similar_user_id": "5a8cf39111be10466d285d6b",
 "fans": {"count": {"min": 50000}},
 "coop": {"pic_price": {"max": 20000}}}

// ⑩ 预算优先：按「图文预估阅读单价」从低到高排，先看最便宜的
{"page_num": 1, "page_size": 20,
 "column": "pictureReadCost", "sort": "asc",
 "coop": {"pic_price": {"min": 1000, "max": 10000}}}

// ⑪ 涨粉快的新秀：按近 30 天粉丝增长率降序，并剔除掉粉的
{"page_num": 1, "page_size": 20,
 "column": "fans30GrowthRate", "sort": "desc",
 "fans": {"count": {"min": 5000, "max": 100000}},
 "flags": {"exclude_fans_down": true}}

// ⑫ 爆款率优先：按百赞笔记占比降序（看重内容稳定性而非粉丝量时用）
{"page_num": 1, "page_size": 20,
 "column": "hundredLikePercent30", "sort": "desc",
 "note": {"thousand_like_percent": {"min": 10}}}

// ⑬ 地域投放：找上海本地的探店博主
//    blogger.location 直接填地名即可，不要填「上海市-上海市-黄浦区」这种全路径
{"page_num": 1, "page_size": 20,
 "blogger": {"location": ["上海"], "content_tag": ["美食"]}}

// ⑭ 按粉丝所在地投放（和 ⑬ 是两个不同维度：⑬ 是博主在哪，⑭ 是粉丝在哪）
{"page_num": 1, "page_size": 20,
 "fans": {"location": ["广东"], "count": {"min": 20000}}}

// ⑮ 直播带货 + 90 天口径：场均观播 ≥5000、场均销售额 ≥1 万
//    注意 avg_viewer / avg_gmv 是近 90 天，而 live_count 是近 30 天
{"page_num": 1, "page_size": 20,
 "live": {"live_count": {"min": 1}, "avg_viewer": {"min": 5000}, "avg_gmv": {"min": 10000}}}
```

### 常见错误写法 / Common mistakes

分两类。**第二类才是真正的坑** —— 它们不会报错，只会安静返回 0 条。

#### A. 会直接 422 的写法（好在会立刻报错）

```json
// ✗ 区间写成了数字 —— 区间型字段必须是对象
{"fans": {"count": 10000}}
// ✓
{"fans": {"count": {"min": 10000}}}

// ✗ 字段名拼错（驼峰）—— 本接口 extra="forbid"，写错直接 422，不会被静默忽略
{"blogger": {"contentTag": ["美妆"]}}
// ✓
{"blogger": {"content_tag": ["美妆"]}}

// ✗ 排序列填了未放行的值
{"column": "forecastReadUv"}
// ✓ 见第 1 节的 34 个排序字段
{"column": "avgAgmv90d"}

// ✗ page_size 超上限（1-20）
{"page_size": 100}
// ✓
{"page_size": 20}

// ✗ 百分比量纲错了 —— 千赞比例填 200 或 9500（按万分比/原始条数填）
{"note": {"thousand_like_percent": {"min": 200}}}
// ✓ 想表达「20%」就填 20
{"note": {"thousand_like_percent": {"min": 20}}}
```

#### B. 不会报错、但查不到数据的写法（最难排查）

下面这些**全都能通过校验**，返回 200，结果却是 0 条 —— 因为值本身合法、只是没有匹配项。

```json
// ✗ 地域填了带分隔符的全路径
{"blogger": {"location": ["上海市-上海市-黄浦区"]}}
// ✓ 直接填地名
{"blogger": {"location": ["上海"]}}

// ✗ 百分比填了小数（0.2 是合法的 0.2%，不是 20%）
{"note": {"thousand_like_percent": {"min": 0.2}}}
// ✓ 想表达「20%」
{"note": {"thousand_like_percent": {"min": 20}}}

// ✗ 类目值是自己编的
{"blogger": {"content_tag": ["美妆护肤"]}}
// ✓ 用 /get_blogger_filter_options 的 contentTags 里真实存在的值
{"blogger": {"content_tag": ["美妆"]}}

// ✗ 开关型填 false —— 等于没筛，不是「反向筛」
{"flags": {"exclude_cooperated": false}}
// ✓ 想生效就填 true
{"flags": {"exclude_cooperated": true}}

// ✗ 把「行业」当成「博主类目」用（两个是不同的筛选）
{"flags": {"industry_first": "美妆"}}
// ✓ 博主类目用 blogger.content_tag
{"blogger": {"content_tag": ["美妆"]}}
```

**搜不到结果时的排查顺序**：先把筛选一个个去掉，定位是哪个条件卡住的；
需要查字典的筛选（类目 / 场景 / 消费行为 / 地域）优先怀疑值写错了。

## Request body

- BloggerListReq
  - `page_num` integer: 页码，1-250（上游只放出前 5000 条）/Page, 1-250
  - `page_size` integer: 每页数量，1-20（网页固定 20，建议保持 20）/Page size, 1-20
  - `keyword` string: 搜索词；空=只筛选不搜索/Keyword
  - `search_type` 0 | 1: 1=按笔记关键词找博主(默认) 0=按昵称搜/Search mode
  - `column` 'comprehensiverank' | 'fansCount' | 'price' | 'clickNum' | 'mEngagementNum' | 'accumCommonImpMedinNum30d' | 'accumPicCommonImpMedinNum30d' | 'accumVideoCommonImpMedinNum30d' | 'pictureClickMidNum' | 'videoClickMidNum' | 'pictureInterMidNum' | 'videoInterMidNum' | 'thousandLikePercent30' | 'pictureThousandLikePercent30' | 'videoThousandLikePercent30' | 'hundredLikePercent30' | 'pictureHundredLikePercent30' | 'videoHundredLikePercent30' | 'videoFinishRate' | 'accumCoopImpMedinNum30d' | 'readMidCoop30' | 'interMidCoop30' | 'mCpuvNum30d' | 'picturePrice' | 'videoPrice' | 'pictureReadCost' | 'videoReadCost' | 'inviteReply48hNumRatio' | 'fans30GrowthRate' | 'fansActiveIn28dLv' | 'fansEngageNum30dLv' | 'kliveCnt30d' | 'avgLiveViewerNum' | 'avgAgmv90d': 排序字段，共 34 个，见接口说明的排序字段表/Sort column, 34 options, see endpoint docs
  - `sort` 'desc' | 'asc': 排序方向 desc/asc
  - `brand_user_id` string, nullable: 品牌方/广告主用户ID，可不填/Brand user ID, optional
  - `similar_user_id` string, nullable: 找相似博主：填一个博主的 userId，返回与他相似的博主，**并且可以叠加下面所有筛选**（这是它与 /get_blogger_similar 的关键区别）/Find similar bloggers; unlike /get_blogger_similar it can be combined with all filters below
  - `similar_word` string, nullable: 配合 similar_user_id 的博主昵称，仅用于回显、不影响结果/Nickname echo for similar_user_id; does not affect results
  - `goal` GoalFilter: 合作目标：按营销目标（曝光 / 种草 / 转化）匹配博主。 想按粉丝人群圈博主，用 `BloggerAttrFilter.audience20`（二十大人群）。
    - `market_target` 'estimateAllCpm' | 'mAccumImpNum' | 'readCost' | 'mValidRawReadFeedNum' | 'estimateEngageCost' | 'mEngagementNum90d' | 'estimateCpuv' | 'mCpuvNum', nullable: 「营销目标」：网页上是三级选择（目标 → 指标 → 统计方式），填最终叶子码 —— `estimateAllCpm` 曝光/曝光表现/成本、`mAccumImpNum` 曝光/曝光表现/规模、`readCost` 曝光/阅读表现/成本、`mValidRawReadFeedNum` 曝光/阅读表现/规模、`estimateEngageCost` 种草/互动表现/成本、`mEngagementNum90d` 种草/互动表现/规模、`estimateCpuv` 转化/外溢进店表现/成本、`mCpuvNum` 转化/外溢进店表现/规模
  - `blogger` BloggerAttrFilter: 博主属性：类目、人设、擅长内容、人群画像、性别、地域、签约情况等。 带层级的选项（类目/人设/擅长内容/内容题材/二十大人群/行业特色画像/预估消费行为） 先调 `/api/v2/pgy/blogger/filter-options` 取值，各字段说明里写了该取哪一层。
    - `content_tag` string[], nullable: 「博主类目」：填类目名。一级共 28 个 —— 美妆/护肤/个人护理/母婴/时尚/美食/家居家装/影视综资讯/运动健身/宠物/文化艺术/兴趣爱好/生活记录/教育/职场/情感/摄影/游戏/科技数码/出行旅游/音乐/搞笑/健康养生/汽车/婚嫁/商业财经/素材/其他；也可填某一级下的二级名，**两级可以混在同一个数组里**，多个之间是「或」。某个类目要「全部」就填它的一级名，**不要展开成它的全部二级名**（有些博主只挂一级、没有二级标签，展开会漏掉他们）。**不限类目就不要传这个字段** —— 没有名为「全部」的类目，填了会静默返回 0 条。填错的名字同样不报错、只返回 0 条，务必从 contentTags 字典里原样复制。注意与「行业」(coop.industry) 不是同一套分类
    - `personal_tags` string[], nullable: 「博主人设」：填 `/get_blogger_filter_options` 里 personalTagsV2 的 `tagName`（裸名）。这是三层树（家庭身份/职业身份/特色背景 → 分组 → 叶子），**三层任意一层都能填、可以混填**，多个之间是「或」；想要整组就填那一组的名字，不用把叶子一个个列出来（实测：一级 `职业身份`=91 条，二级 `互联网`=8 条，叶子 `妈妈`=31 条）
    - `feature_tags` string[], nullable: 「擅长内容」：填 `/get_blogger_filter_options` 里 featureTags 的名字（裸名）。**一级（形式/风格/生活方式/肤质肤色/皮肤养护）和二级（如 vlog / 韩系）都能填、可混填**，多个之间是「或」；填一级就是那一组全要（实测：一级 `形式`=183，二级 `vlog`=136）
    - `content_theme` string[], nullable: 「内容题材」：填 `/blogger/filter-options` 里 contentTheme 的**一级 label**（如 汽车特色 / 通用）。注意只支持一级，填子项 label 或 id 查不到结果
    - `audience20` string[], nullable: 「二十大人群」：**不是扁平裸名**。一级填名字即可（如 `自在户外`，= 整个人群）；**二级必须写成 `"一级 一级-二级"`**（如 `"自在户外 自在户外-挑战极限者"`）—— 一级名要出现两次。**照字典里的二级 label 直接填（如 `挑战极限者`）会静默返回 0 条**。一级与复合二级可以混在同一个数组里，多个之间是「或」。全量见 `/get_blogger_filter_options` 的 audience20（19 个一级 / 121 个二级）
    - `industry_crowd` string[], nullable: 「行业特色画像」：填 `/blogger/filter-options` 里 automotiveIndustryTag **最深层叶子的 value**（数字字符串 ID，如 15795914，共 1600+ 个）。填 label 或中间层 id 均无结果
    - `consumer_behavior` string[], nullable: 「预估消费行为」：**必须填含根节点的空格全路径**（如 `"内容行为预估 汽车"`），路径可停在任意深度。取值查 `/get_blogger_filter_options` 的 **consumerBehavior**（5 层树）。⚠️ **填裸的叶子名不会报错、也不会返回 0 条，而是整个筛选被静默忽略** —— 实测 `["保时捷911"]` 与 `["zzz不存在zzz"]` 都返回未经筛选的全量结果，只有全路径写法才真的生效（`["内容行为预估 汽车"]` 才筛出 54 条）。用它时请对比加/不加该筛选两次请求的 total 确认真的筛上了。另：该项按账号灰度开放，字典里为 null 时表示当前账号不可用
    - `note_scene` string[], nullable: 「笔记类目」：填**空格分隔的完整路径**（类目 一级 二级 三级），如 "汽车 理性决策 选车攻略 政策"，路径可停在任意深度（越深越窄）。**只填末段（如 "选车攻略"）会返回 0 条。** 取值查 `/get_blogger_filter_options` 的 **contentScenes**（实测：`["汽车"]`=75、`["汽车 理性决策"]`=23、四级路径=1、`["选车攻略"]`=0）
    - `gender` '男' | '女', nullable: 「性别」：男 / 女（填中文，不是数字）
    - `location` string[], nullable: 「地域」：博主所在地。**直接填地名即可**，省/市/区县任意层级都行 —— ["上海"]、["杭州"]、["上城区"] 都可以，多个之间是「或」。也可以填完整路径（如 "中国 浙江 杭州"），上游会自动转成地名。可选地名见 /get_blogger_filter_options 的 **areas**（到区县）/Blogger location: plain place names work (province/city/district), OR-ed together
    - `signed` -1 | 0 | 1, nullable: 「签约情况」：-1=不限 0=个人博主 1=机构博主
  - `fans` CrawlersXiaohongshuPgyPgyFiltersFansFilter: 粉丝画像 —— 网页「粉丝画像」区（粉丝量 / 年龄 / 性别 / 地域 / 婚恋 / 消费 / 母婴 / 手机）。
    - `count` CrawlersXiaohongshuPgyPgyFiltersRangeFilter: 数值区间，对应网页筛选里的「自定义」输入框。 - 两端都可省略：省略 `min` 视为 0，省略 `max` 视为不限 - 至少要给一个端点，否则 422
      - `min` number, nullable: 最小值（含）
      - `max` number, nullable: 最大值（含）；省略=不限
    - `age` 0 | 1 | 2 | 3 | 4 | 5, nullable: 「粉丝年龄」：0=不限 1=<18 2=18~24 3=25~34 4=35~44 5=>44 占比高
    - `gender` 0 | 1 | 2, nullable: 「粉丝性别」：0=不限 1=男性占比高 2=女性占比高
    - `marital_status` -1 | 0 | 1, nullable: 「婚恋状态」：-1=不限 0=未婚 1=已婚
    - `consumption_level` -1 | 0 | 1 | 2, nullable: 「消费水平」：-1=不限 0=低消费 1=中消费 2=高消费
    - `child_age` integer[], nullable: 「母婴阶段」：0=备孕 2=0-6月 3=7-12月 4=1-3岁 5=4-6岁 6=7-12岁 7=孕早期 8=孕晚期
    - `device_price` string[], nullable: 「手机价格」档位，如 3000-3999 / 8000+
    - `device_brand` string[], nullable: 「手机品牌」
    - `location` string[], nullable: 「粉丝地域」：粉丝所在地。**直接填地名即可**（["上海"]、["杭州"]），上游会自动补成完整路径。也可以自己填完整路径 "中国 浙江 杭州"。注意这棵树**只到市**（没有区县），可选地名见 /get_blogger_filter_options 的 **fansAreas**/Fans location: plain place names work; this tree only goes down to city level
  - `note` NoteFilter: 日常笔记数据 —— 网页「日常笔记」区（近 30 天自然流量表现）。
    - `imp_median` CrawlersXiaohongshuPgyPgyFiltersRangeFilter: 数值区间，对应网页筛选里的「自定义」输入框。 - 两端都可省略：省略 `min` 视为 0，省略 `max` 视为不限 - 至少要给一个端点，否则 422
      - `min` number, nullable: 最小值（含）
      - `max` number, nullable: 最大值（含）；省略=不限
    - `read_median` CrawlersXiaohongshuPgyPgyFiltersRangeFilter: 数值区间，对应网页筛选里的「自定义」输入框。 - 两端都可省略：省略 `min` 视为 0，省略 `max` 视为不限 - 至少要给一个端点，否则 422
      - `min` number, nullable: 最小值（含）
      - `max` number, nullable: 最大值（含）；省略=不限
    - `inter_median` CrawlersXiaohongshuPgyPgyFiltersRangeFilter: 数值区间，对应网页筛选里的「自定义」输入框。 - 两端都可省略：省略 `min` 视为 0，省略 `max` 视为不限 - 至少要给一个端点，否则 422
      - `min` number, nullable: 最小值（含）
      - `max` number, nullable: 最大值（含）；省略=不限
    - `thousand_like_percent` PercentRange: 百分数区间：按 0~100 的百分点填，`40` 表示 40%（**不是 0.4**）。 口径来自蒲公英网页自己：千赞笔记比例的档位写作 `{"40%以上": [40, null]}`、 `{"10%以下": [null, 10]}` —— 发给上游的就是 40 / 10。 **拦得住什么、拦不住什么，说清楚免得误会**： * 拦得住「量纲大了」—— 填 200、9500（按万分比或原始条数填）会 422， 而通用 RangeFilter 上限是 1e12，这类值原来是直接放行的。 * **拦不住「量纲小了」**—— 填 0.4 仍然合法，因为 0.4% 本身是正当取值， 类型上无法区分「他想写 40% 却写成 0.4」和「他真的要筛 0.4%」。 这个方向只能靠字段说明讲清楚。
      - `min` number, nullable: 最小值（含），百分点 0~100/Min percent 0-100
      - `max` number, nullable: 最大值（含），百分点 0~100/Max percent 0-100
    - `note_type` 0 | 1 | 2, nullable: 「笔记类型」：0=不限 1=图文笔记为主 2=视频笔记为主
  - `coop` CoopFilter: 合作笔记 —— 网页「合作笔记」区（合作表现 + 数据表现）。
    - `pic_price` CrawlersXiaohongshuPgyPgyFiltersRangeFilter: 数值区间，对应网页筛选里的「自定义」输入框。 - 两端都可省略：省略 `min` 视为 0，省略 `max` 视为不限 - 至少要给一个端点，否则 422
      - `min` number, nullable: 最小值（含）
      - `max` number, nullable: 最大值（含）；省略=不限
    - `video_price` CrawlersXiaohongshuPgyPgyFiltersRangeFilter: 数值区间，对应网页筛选里的「自定义」输入框。 - 两端都可省略：省略 `min` 视为 0，省略 `max` 视为不限 - 至少要给一个端点，否则 422
      - `min` number, nullable: 最小值（含）
      - `max` number, nullable: 最大值（含）；省略=不限
    - `order_count` CrawlersXiaohongshuPgyPgyFiltersRangeFilter: 数值区间，对应网页筛选里的「自定义」输入框。 - 两端都可省略：省略 `min` 视为 0，省略 `max` 视为不限 - 至少要给一个端点，否则 422
      - `min` number, nullable: 最小值（含）
      - `max` number, nullable: 最大值（含）；省略=不限
    - `reply_48h_ratio` PercentRange: 百分数区间：按 0~100 的百分点填，`40` 表示 40%（**不是 0.4**）。 口径来自蒲公英网页自己：千赞笔记比例的档位写作 `{"40%以上": [40, null]}`、 `{"10%以下": [null, 10]}` —— 发给上游的就是 40 / 10。 **拦得住什么、拦不住什么，说清楚免得误会**： * 拦得住「量纲大了」—— 填 200、9500（按万分比或原始条数填）会 422， 而通用 RangeFilter 上限是 1e12，这类值原来是直接放行的。 * **拦不住「量纲小了」**—— 填 0.4 仍然合法，因为 0.4% 本身是正当取值， 类型上无法区分「他想写 40% 却写成 0.4」和「他真的要筛 0.4%」。 这个方向只能靠字段说明讲清楚。
      - `min` number, nullable: 最小值（含），百分点 0~100/Min percent 0-100
      - `max` number, nullable: 最大值（含），百分点 0~100/Max percent 0-100
    - `industry` '不限' | '美妆个护' | '食品饮料' | '母婴' | '3c及电器' | '日用百货' | '服装配饰' | '互联网' | '生活服务' | '家居建材' | '汽车', nullable: 「近期合作行业」
    - `brand_ids` string[], nullable: 「近期合作品牌」：品牌 userId 列表
    - `exclude_brands` boolean, nullable: 配合 brand_ids：true=排除与这些品牌合作过的博主
    - `imp_median` CrawlersXiaohongshuPgyPgyFiltersRangeFilter: 数值区间，对应网页筛选里的「自定义」输入框。 - 两端都可省略：省略 `min` 视为 0，省略 `max` 视为不限 - 至少要给一个端点，否则 422
      - `min` number, nullable: 最小值（含）
      - `max` number, nullable: 最大值（含）；省略=不限
    - `read_median` CrawlersXiaohongshuPgyPgyFiltersRangeFilter: 数值区间，对应网页筛选里的「自定义」输入框。 - 两端都可省略：省略 `min` 视为 0，省略 `max` 视为不限 - 至少要给一个端点，否则 422
      - `min` number, nullable: 最小值（含）
      - `max` number, nullable: 最大值（含）；省略=不限
    - `inter_median` CrawlersXiaohongshuPgyPgyFiltersRangeFilter: 数值区间，对应网页筛选里的「自定义」输入框。 - 两端都可省略：省略 `min` 视为 0，省略 `max` 视为不限 - 至少要给一个端点，否则 422
      - `min` number, nullable: 最小值（含）
      - `max` number, nullable: 最大值（含）；省略=不限
    - `cpuv` CrawlersXiaohongshuPgyPgyFiltersRangeFilter: 数值区间，对应网页筛选里的「自定义」输入框。 - 两端都可省略：省略 `min` 视为 0，省略 `max` 视为不限 - 至少要给一个端点，否则 422
      - `min` number, nullable: 最小值（含）
      - `max` number, nullable: 最大值（含）；省略=不限
    - `pic_cpm` CrawlersXiaohongshuPgyPgyFiltersRangeFilter: 数值区间，对应网页筛选里的「自定义」输入框。 - 两端都可省略：省略 `min` 视为 0，省略 `max` 视为不限 - 至少要给一个端点，否则 422
      - `min` number, nullable: 最小值（含）
      - `max` number, nullable: 最大值（含）；省略=不限
    - `video_cpm` CrawlersXiaohongshuPgyPgyFiltersRangeFilter: 数值区间，对应网页筛选里的「自定义」输入框。 - 两端都可省略：省略 `min` 视为 0，省略 `max` 视为不限 - 至少要给一个端点，否则 422
      - `min` number, nullable: 最小值（含）
      - `max` number, nullable: 最大值（含）；省略=不限
    - `pic_read_price` CrawlersXiaohongshuPgyPgyFiltersRangeFilter: 数值区间，对应网页筛选里的「自定义」输入框。 - 两端都可省略：省略 `min` 视为 0，省略 `max` 视为不限 - 至少要给一个端点，否则 422
      - `min` number, nullable: 最小值（含）
      - `max` number, nullable: 最大值（含）；省略=不限
    - `video_read_price` CrawlersXiaohongshuPgyPgyFiltersRangeFilter: 数值区间，对应网页筛选里的「自定义」输入框。 - 两端都可省略：省略 `min` 视为 0，省略 `max` 视为不限 - 至少要给一个端点，否则 422
      - `min` number, nullable: 最小值（含）
      - `max` number, nullable: 最大值（含）；省略=不限
    - `pic_engage_cost` CrawlersXiaohongshuPgyPgyFiltersRangeFilter: 数值区间，对应网页筛选里的「自定义」输入框。 - 两端都可省略：省略 `min` 视为 0，省略 `max` 视为不限 - 至少要给一个端点，否则 422
      - `min` number, nullable: 最小值（含）
      - `max` number, nullable: 最大值（含）；省略=不限
    - `video_engage_cost` CrawlersXiaohongshuPgyPgyFiltersRangeFilter: 数值区间，对应网页筛选里的「自定义」输入框。 - 两端都可省略：省略 `min` 视为 0，省略 `max` 视为不限 - 至少要给一个端点，否则 422
      - `min` number, nullable: 最小值（含）
      - `max` number, nullable: 最大值（含）；省略=不限
    - `estimate_cpuv` CrawlersXiaohongshuPgyPgyFiltersRangeFilter: 数值区间，对应网页筛选里的「自定义」输入框。 - 两端都可省略：省略 `min` 视为 0，省略 `max` 视为不限 - 至少要给一个端点，否则 422
      - `min` number, nullable: 最小值（含）
      - `max` number, nullable: 最大值（含）；省略=不限
  - `live` CrawlersXiaohongshuPgyPgyFiltersLiveFilter: 直播数据 —— 网页「直播数据」区。
    - `live_count` CrawlersXiaohongshuPgyPgyFiltersRangeFilter: 数值区间，对应网页筛选里的「自定义」输入框。 - 两端都可省略：省略 `min` 视为 0，省略 `max` 视为不限 - 至少要给一个端点，否则 422
      - `min` number, nullable: 最小值（含）
      - `max` number, nullable: 最大值（含）；省略=不限
    - `avg_viewer` CrawlersXiaohongshuPgyPgyFiltersRangeFilter: 数值区间，对应网页筛选里的「自定义」输入框。 - 两端都可省略：省略 `min` 视为 0，省略 `max` 视为不限 - 至少要给一个端点，否则 422
      - `min` number, nullable: 最小值（含）
      - `max` number, nullable: 最大值（含）；省略=不限
    - `avg_gmv` CrawlersXiaohongshuPgyPgyFiltersRangeFilter: 数值区间，对应网页筛选里的「自定义」输入框。 - 两端都可省略：省略 `min` 视为 0，省略 `max` 视为不限 - 至少要给一个端点，否则 422
      - `min` number, nullable: 最小值（含）
      - `max` number, nullable: 最大值（含）；省略=不限
  - `flags` CrawlersXiaohongshuPgyPgyFiltersFlagFilter: 开关类 —— 网页「平台推荐」（精选博主/热门活动）+「常规剔除」（一键剔除）区。
    - `is_star` boolean, nullable: 「明星」
    - `new_high_quality` boolean, nullable: 「新锐博主」
    - `high_quality` boolean, nullable: 「优质博主」
    - `note_and_live` boolean, nullable: 「笔记+直播均可合作」
    - `filter_intention` boolean, nullable: 「意向行业匹配」
    - `activity_codes` string[], nullable: 「热门活动」：填 `/blogger/filter-options` 里 activities 每项的 code
    - `exclude_low_active` boolean, nullable: 「剔除低活博主」
    - `exclude_fans_down` boolean, nullable: 「剔除掉粉博主」
    - `exclude_cooperated` boolean, nullable: 「剔除已合作博主」
    - `exclude_invited` boolean, nullable: 「剔除已邀约博主」
    - `industry_first` string, nullable: 「行业推荐博主」一级行业名（如 美妆个护）；平台会按该行业推荐专属博主
    - `industry_second` string, nullable: 「行业推荐博主」二级行业名（配合 industry_first，取 `/blogger/filter-options` 里 industryTags 的 taxonomy2Tags）

## Response `200`

Successful Response

- ResponseModel
  - `code` integer: HTTP status code | HTTP状态码
  - `request_id` string, nullable: Unique request identifier | 唯一请求标识符
  - `message` string: Response message (EN-US) | 响应消息 (English)
  - `message_zh` string: Response message (ZH-CN) | 响应消息 (中文)
  - `support` string: Support message | 支持消息
  - `time` string: The time the response was generated | 生成响应的时间
  - `time_stamp` integer: The timestamp the response was generated | 生成响应的时间戳
  - `time_zone` string: The timezone of the response time | 响应时间的时区
  - `docs` string, nullable: Link to the API Swagger documentation for this endpoint | 此端点的 API Swagger 文档链接
  - `cache_message` string, nullable: Cache message (EN-US) | 缓存消息 (English)
  - `cache_message_zh` string, nullable: Cache message (ZH-CN) | 缓存消息 (中文)
  - `cache_url` string, nullable: The URL to access the cached result | 访问缓存结果的 URL
  - `router` string: The endpoint that generated this response | 生成此响应的端点
  - `params` unknown
  - `data` unknown

## Other responses

- `422`: Validation Error

## Changes

- **2026-10-08** `96a122a5f220`: 1 breaking, 3 info
  - removed `#/components/schemas/LiveFilter` from the `live` request property `anyOf` list
  - added `#/components/schemas/crawlers__xiaohongshu__pgy__pgy_filters__LiveFilter` to the `live` request property `anyOf` list
  - added the optional property `detail/items/ctx` to the response with the `422` status
  - added the optional property `detail/items/input` to the response with the `422` status
- **2026-10-06** `927ffb5aef5d`: 2 breaking, 2 info
  - removed `#/components/schemas/FlagFilter` from the `flags` request property `anyOf` list
  - removed `#/components/schemas/crawlers__xiaohongshu__pgy__pgy_filters__LiveFilter` from the `live` request property `anyOf` list
  - added `#/components/schemas/LiveFilter` to the `live` request property `anyOf` list
  - added `#/components/schemas/crawlers__xiaohongshu__pgy__pgy_filters__FlagFilter` to the `flags` request property `anyOf` list
- **2026-10-06** `908b21213eeb`: 1 breaking, 1 info
  - removed `#/components/schemas/crawlers__xiaohongshu__pgy__pgy_filters__FlagFilter` from the `flags` request property `anyOf` list
  - added `#/components/schemas/FlagFilter` to the `flags` request property `anyOf` list
- …earlier changes not shown

[Operation history](https://skmtc.dev/tikhub/apis/tikhub-douyin-tiktok-xiaohongshu-lemon8-bilibili-kuaishou-pi/changes/api/v1/xiaohongshu/pgy/get_blogger_list/post.md)

---

[API](https://skmtc.dev/tikhub/apis/tikhub-douyin-tiktok-xiaohongshu-lemon8-bilibili-kuaishou-pi.md) - [All operations](https://skmtc.dev/tikhub/apis/tikhub-douyin-tiktok-xiaohongshu-lemon8-bilibili-kuaishou-pi/llms.txt) - [OpenAPI document](https://skmtc.dev/tikhub/apis/tikhub-douyin-tiktok-xiaohongshu-lemon8-bilibili-kuaishou-pi/revisions/86a3483c705a?raw)
