## 查询取数接入路线

以下是推荐阅读顺序，不代表已完成的进度；可选步骤可以跳过。

1. [获取与使用 API Key](/docs/authentication)
2. [完成第一次查询](/docs/quickstart)
3. [关键词与语义检索](/docs/search)
4. [用户、话题、标签与分类](/docs/browse)（可选） ← 当前页
5. [读取内容与历史](/docs/documents)
6. [下载图片与附件](/docs/attachments)（可选）
7. [限流、重试与排错](/docs/production)

**开始前**：需要按用户、话题、标签或分类取数时，准备实际平台 ID、筛选值和对应字段授权；仅搜索可跳过。

# 用户、话题、标签与分类

这些都是对外只读接口，使用应用的 **Consumer API Key**，不需要 Collector Key 或 Admin Token。先按[获取与使用 API Key](/docs/authentication)配置环境；需要关键词或语义相似查询时使用[关键词与语义检索](/docs/search)。

## 选择接口

| 需求 | 接口 |
| --- | --- |
| 读取已采集的平台用户资料 | `GET /v1/users/{platform}/{external_author_id}` |
| 读取该用户发布的内容 | `GET /v1/users/{platform}/{external_author_id}/documents` |
| 按话题、原生标签或分类获取内容 | `GET /v1/documents/browse` |
| 获取某数据集可用的分类 code | `GET /v1/classifications?dataset_id=...` |
| 读完整正文、指标、版本或附件 | 取得 `document.id` 后[读取内容与历史](/docs/documents)或[下载图片与附件](/docs/attachments) |

`platform` 是平台 code；`external_author_id` 是该平台的作者 ID，不是 MarsYoo 组织用户 ID，也不是昵称。按作者查询必须指定一个平台，不能把采集频道 ID 当作作者 ID。

## 按条件筛选内容

下面使用已配置的 `WHALE_BASE_URL` 和 `WHALE_API_KEY`，查找指定时间窗口内已入库、话题为 AI 的内容。数据集和话题值请替换为你实际获得授权的数据：


```bash
curl --fail-with-body --get "$WHALE_BASE_URL/v1/documents/browse" \
  -H "Authorization: Bearer $WHALE_API_KEY" \
  --data-urlencode 'dataset_id=social_media_raw' \
  --data-urlencode 'source_platforms=reddit' \
  --data-urlencode 'topics=AI' \
  --data-urlencode 'score_min=10' \
  --data-urlencode 'created_from=2026-10-01T00:00:00Z' \
  --data-urlencode 'created_to=2026-10-02T00:00:00Z' \
  --data-urlencode 'limit=50'
```


| 参数 | 说明 |
| --- | --- |
| `dataset_id` | 单个数据集；省略时仍仅查询授权范围 |
| `source_platforms` | 平台 code，多个用逗号分隔 |
| `external_author_id` | 作者 ID；必须同时指定恰好一个 `source_platforms` |
| `topics` | 内容话题或主题，多个用逗号分隔 |
| `tags` | 内容原生标签，多个用逗号分隔；不是外部分类别名 |
| `classification_codes` | 外部分类 code，多个用逗号分隔；先查询分类列表 |
| `score_min` / `score_max` | 当前平台整数评分的闭区间；Reddit 可用于筛选当前 `score`，允许负数 |
| `content_types` | 内容类型，多个用逗号分隔 |
| `published_from` / `published_to` | 平台发布时间，RFC3339，左闭右开 |
| `created_from` / `created_to` | 首次入库时间，RFC3339，左闭右开；默认最近 24 小时，单次窗口不超过 31 天 |
| `limit` | 1–200，默认 50 |
| `cursor` | 上一页的 `next_cursor`，原样传回 |
| `include_snapshot` | `true` 时附带可授权的最新指标快照，默认 `false` |
| `query` | 精确查找文档 ID 或原文 URL；指定平台时也支持平台内容 ID。不是全文搜索，精确查找可省略入库时间窗口 |
| `status` | 可选文档状态筛选 |

每个多值参数内部为“任一匹配”，不同参数之间为“同时满足”。话题、标签、作者 ID、分类 code 按实际存储值精确匹配，区分大小写，不做语义扩展、别名推断或补 `#`。逗号是多值分隔符，当前不能查询值本身含逗号的标签。

分类条件只匹配当前版本或文档级的 `category` fact 中的 `metadata.label_code`；没有该结构的旧数据不会被推断为分类命中。评分条件读取当前文档投影里的平台 `score`，没有合法整数评分的数据不会命中。没有作者/话题/标签的内容也不会匹配相应条件。

### 响应与分页


```json
{
  "documents": [
    {
      "document": {
        "id": "doc_example",
        "dataset_id": "social_media_raw",
        "source_platform": "reddit",
        "title": "示例内容"
      }
    }
  ],
  "next_cursor": "...",
  "created_from": "2026-10-01T00:00:00Z",
  "created_to": "2026-10-02T00:00:00Z"
}
```


结果按首次入库时间降序、文档 ID 降序排列。这是当前文档目录，不包含完整正文，也不是全部历史版本列表。文档可能先已落库、后才可被全文或向量索引搜到。

下一次请求保持相同参数、应用与 `limit`，增加 `cursor`。默认时间窗口由游标固定，无需重新计算；显式时间条件也不能改变。没有 `next_cursor` 即结束。范围外内容不会返回，不提供全库总数。翻页期间更新或删除的数据可能变化，这不是冻结快照。

批量回填更长历史时，把入库时间划分为不超过 31 天的窗口，逐窗分页；持续同步改用[增量同步数据](/docs/change-feed)，不要反复扫描目录。

## 读取用户资料与用户内容


```bash
curl --fail-with-body --get "$WHALE_BASE_URL/v1/users/reddit/author_example" \
  -H "Authorization: Bearer $WHALE_API_KEY" \
  --data-urlencode 'dataset_id=social_media_raw'

curl --fail-with-body --get "$WHALE_BASE_URL/v1/users/reddit/author_example/documents" \
  -H "Authorization: Bearer $WHALE_API_KEY" \
  --data-urlencode 'dataset_id=social_media_raw' \
  --data-urlencode 'limit=50'
```


用户资料接口返回 `user.source_platform`、`external_author_id`、`author`、作为依据的 `document_id` 和该文档的 `observed_at`。`author` 包含采集到的昵称、头像、主页、简介、关注数等，但不会返回采集器的 `extra_payload`。

这是请求窗口内最新入库的授权文档所附带的作者资料，不是平台实时完整用户档案；字段可能为空，文档最近观察时间也不代表每个资料字段单独更新的时间。默认只看最近 24 小时，没有样本时返回 `404 user_not_found`，可以显式扩大到不超过 31 天。需要新采集用户资料时使用[按需采集新数据](/docs/live-query)，需要定期跟踪用户时使用[定期采集用户与频道](/docs/collection-subscriptions)。

用户内容接口支持上面的目录参数与分页，路径已经指定作者和平台，不可用查询参数改成另一用户。用户资料接口仅支持 `dataset_id`、`created_from/to`、`published_from/to`，不分页，不接受 `cursor` 或 `query`。

## 查询分类字典


```bash
curl --fail-with-body --get "$WHALE_BASE_URL/v1/classifications" \
  -H "Authorization: Bearer $WHALE_API_KEY" \
  --data-urlencode 'dataset_id=social_media_raw' \
  --data-urlencode 'limit=100'
```


响应的 `classifications[]` 仅包含 `code`、`dataset_id`、`name`、`description`、`label_schema`。只返回 active 分类，按 code 升序排列；分页时将 `next_after_code` 传为 `after_code`，保持同一数据集和 `limit`，直到游标为空。默认/最大 `limit` 为 100。

再把返回的实际 `code` 传入目录接口的 `classification_codes`。分类是数据集级字典，不保证每个 code 都有当前可读取内容；没有内容时返回空列表。`ai`、`sports`、`tourism` 等只是可能的分类名称，不可假设每个数据集都已创建。

## 权限与错误

数据集、平台、语言、地区、发布时间均按当前有效订阅过滤。作者筛选和资料读取需要 `author` 或 `metadata` 字段权限；话题/标签筛选需要 `metadata`；分类筛选与字典读取需要 `facts`；评分筛选需要 `metrics`。组合条件必须在同一授权条目中允许，不能拼接互不相容的订阅扩大范围。返回字段也会按匹配的授权裁剪。

| HTTP / code | 处理 |
| --- | --- |
| `200` 且 `documents=[]` | 检查时间窗口、精确标签值及授权范围；不代表全库没有数据 |
| `400 invalid_document_query` | 检查时间、分页参数、未知参数及作者所需的平台 |
| `400 invalid_classification_query` | 检查必填 `dataset_id`、`limit`、`after_code` |
| `401 unauthorized` | 检查 Consumer Key |
| `403 entitlement_denied` | 申请对应数据和字段权限；不要使用写入接口探权限 |
| `404 user_not_found` | 当前授权范围和窗口内没有该用户样本 |
| `429` | 按 `Retry-After` 退避 |
| `503 catalog_unavailable` | 服务暂时不可用；缩小窗口并指数退避，不要高频重试 |

取到 ID 后，下一步是[读取正文、版本与事实](/docs/documents)，不是把目录元数据当作完整内容。

## 检查与继续

**完成标准**：能够读取用户观察资料、查询分类 code 并分页筛选内容，取得用于读取正文的 document.id。

上一步：[关键词与语义检索](/docs/search)

**推荐下一步**：[读取内容与历史](/docs/documents)