# go-threads-api Python Full AI Reference

本文件由 `scripts/llms_docs.py` 根据 Proto、`python/threads_client` 公共异步方法和
`docs/llms-metadata.json` 生成。禁止手工修改。

AI 在生成调用代码前必须先选择工作流，再按每一步的参数来源取值。不得只根据方法签名猜测
`uid`、`uuid`、`upload_id`、分页游标或设备字段。

## Install

```bash
python -m pip install -e "python/"
```

运行时要求 Python 3.10+、`grpcio` 和 `protobuf`。

## Client

```python
import asyncio
import os

import grpc
from threads_client import ThreadsClient


async def main() -> None:
    async with ThreadsClient(
        target=os.getenv("THREADS_GRPC_TARGET", "127.0.0.1:50051"),
        token=os.environ["THREADS_IG_TOKEN"],
        proxy_url=os.getenv("THREADS_ACCOUNT_PROXY"),
    ) as client:
        current_user = await client.auth.get_current_user(edit=False)
        print(current_user.pk, current_user.username)


asyncio.run(main())
```

`ThreadsClient` 自动注入 `x-ig-token` 和可选的账号级 `x-threads-proxy-url`。
Token、代理认证信息、密码和 2FA seed 必须来自受保护配置，禁止写入源码、日志、
异常、测试快照或提交记录。

## Async And Error Model

- 所有业务网络方法都是 `async def`，必须使用 `await`。
- 捕获 `grpc.aio.AioRpcError`，检查 `code()`、`details()` 和 metadata。
- 只读且幂等的 RPC 仅可对明确的临时错误做有限退避重试。
- 创建、更新、删除接口不得盲目重试；超时后先查询真实副作用。
- `OK`、HTTP 2xx、对象非空或单个字段存在都不能单独证明业务成功。

```python
try:
    user = await client.profile.get_user_info("17841400000000000")
except grpc.aio.AioRpcError as exc:
    print(exc.code().name, exc.details())
```

## Task Workflows

<a id="workflow-verify_session"></a>

### 校验现有 IGT:2 会话 `verify_session`

- 状态：`available`
- 目标：确认 token 对应的真实账号，并取得后续调用使用的 uid 和资料字段。
- 前置条件：ThreadsClient 已注入 IGT:2 token。；每个账号使用独立 proxy_url；不要让多账号共享出口。

| 步骤 | 调用接口 | 目的 | 输入与参数来源 | 响应与下一步 |
| ---: | --- | --- | --- | --- |
| 1 | `AuthClient.get_current_user`<br>`threads.auth.v1.AuthService/GetCurrentUser` | 调用 get_current_user(edit=False) 校验会话。 | edit=False；token 和代理由 ThreadsClient metadata 自动注入。 | 核对 pk、username；将 pk 作为发帖 uid 或目标 user_id。 |

<a id="workflow-login"></a>

### 账密登录并取得 IGT:2 `login`

- 状态：`experimental`
- 目标：通过 CAA/Bloks 登录生成会话并即时核对账号身份。
- 前置条件：准备持久化设备参数 LoginDevice。；生产使用必须在 Go server 通过 THREADS_KEYBOX_FILE 加载硬件 attestation signer；Docker 镜像从仓库根目录版本化 keybox.xml 构建。；无硬件证明登录属于高风险操作，必须得到明确授权。

| 步骤 | 调用接口 | 目的 | 输入与参数来源 | 响应与下一步 |
| ---: | --- | --- | --- | --- |
| 1 | `AuthClient.login`<br>`threads.auth.v1.AuthService/Login` | 提交账号、密码、设备和可选 2FA seed。 | 默认 allow_unattested_login=False；不要把密码或 2FA seed 写入日志。 | 核对 success、session.token、verified_user.username 和 assurance。 |
| 2 | `AuthClient.get_current_user`<br>`threads.auth.v1.AuthService/GetCurrentUser` | 用新 token 创建 ThreadsClient 后再次读取当前账号。 | 将 LoginResponse.session.token 注入 ThreadsClient.token。 | 确认 pk、username 与登录账号一致；即时成功不代表长期稳定。 |

#### 步骤间参数传递

- `LoginResponse.session.token` → `ThreadsClient.token`：登录返回的 IGT:2 用于后续全部业务 RPC。

#### 当前缺失能力

- 软件环境尚不能证明 session 可长期稳定；stable_for_automation 必须保持 false，直到硬件证明和存活观测通过。

<a id="workflow-edit_profile"></a>

### 安全编辑当前账号资料 `edit_profile`

- 状态：`available`
- 目标：修改指定资料字段，同时避免整表回传接口清空未携带的原值。
- 前置条件：已有有效 IGT:2 token。；已持久化当前账号 uuid。

| 步骤 | 调用接口 | 目的 | 输入与参数来源 | 响应与下一步 |
| ---: | --- | --- | --- | --- |
| 1 | `AuthClient.get_current_user`<br>`threads.auth.v1.AuthService/GetCurrentUser` | 先调用 get_current_user(edit=True) 读取完整当前资料。 | edit=True。 | 保存 username、full_name、biography、is_private、external_url 和 bio_links。 |
| 2 | `ProfileClient.edit_profile`<br>`threads.profile.v1.ProfileService/EditProfile` | 只替换目标字段，其余字段使用上一步原值并整表提交。 | username/first_name/biography/is_private 必须完整回传；uuid 来自持久设备身份。 | 核对返回资料，再调用 get_current_user(edit=True) 验证保存结果。 |

#### 步骤间参数传递

- `CurrentUser.username/full_name/biography/is_private/external_url` → `EditProfileRequest 对应字段`：未修改字段必须回填原值，不能省略。

#### 当前缺失能力

- 头像和封面需要独立上传端点，当前契约未实现。
- location 不是 EditProfile 端点字段。

<a id="workflow-create_text_post"></a>

### 发布文本帖 `create_text_post`

- 状态：`available`
- 目标：使用当前账号和持久设备身份创建纯文本帖子。
- 前置条件：已有有效 IGT:2 token。；已通过 get_current_user 取得 uid。；已持久化 device_id 和 uuid。；写操作已获得明确授权。

| 步骤 | 调用接口 | 目的 | 输入与参数来源 | 响应与下一步 |
| ---: | --- | --- | --- | --- |
| 1 | `PostsClient.create_text_post`<br>`threads.posts.v1.PostsService/CreateTextPost` | 提交 caption、uid、device_id、uuid 和可选发帖功能字段。 | 纯文本帖不需要先上传媒体；upload_id 可省略或由调用方生成。 | 核对返回 Media 的作者、caption、code 和 permalink。 |
| 2 | `ProfileClient.list_profile_threads`<br>`threads.profile.v1.ProfileService/ListProfileThreads` | 重新读取账号主页确认帖子真实出现。 | user_id 使用发帖 uid。 | 在 threads[].items[].post 中找到新帖子。 |

#### 步骤间参数传递

- `CurrentUser.pk` → `CreateTextPostRequest.uid`：发帖 uid 来自当前账号身份校验。

<a id="workflow-create_image_post"></a>

### 发布单图帖 `create_image_post`

- 状态：`available`
- 目标：先上传 WebP 图片，再用上传结果创建单图帖子。
- 前置条件：已有有效 IGT:2 token。；已通过 get_current_user 取得 uid。；已持久化 device_id 和 uuid。；准备 WebP 图片字节及真实宽高。；写操作已获得明确授权。

| 步骤 | 调用接口 | 目的 | 输入与参数来源 | 响应与下一步 |
| ---: | --- | --- | --- | --- |
| 1 | `PostsClient.upload_image`<br>`threads.posts.v1.PostsService/UploadImage` | 上传图片二进制及真实宽高。 | image_data 为 WebP bytes；original_width/original_height 来自图片元数据。 | 要求 status=ok，并保存响应 upload_id。 |
| 2 | `PostsClient.create_image_post`<br>`threads.posts.v1.PostsService/CreateImagePost` | 使用上传响应创建单图帖。 | upload_id 必须使用 UploadImageResult.upload_id；宽高与上传步骤保持一致。 | 核对 media_type=1、caption、image_versions2 和帖子作者。 |
| 3 | `ProfileClient.list_profile_threads`<br>`threads.profile.v1.ProfileService/ListProfileThreads` | 重新读取账号主页确认图片帖真实出现。 | user_id 使用发帖 uid。 | 在 threads[].items[].post 中找到新帖子和图片字段。 |

#### 步骤间参数传递

- `UploadImageResult.upload_id` → `CreateImagePostRequest.upload_id`：图片上传返回值必须原样传给创建接口。
- `UploadImageRequest.original_width/original_height` → `CreateImagePostRequest.original_width/original_height`：创建接口使用与上传图片一致的真实尺寸。

<a id="workflow-create_video_post"></a>

### 发布视频帖 `create_video_post`

- 状态：`unavailable`
- 目标：上传视频并创建视频帖子。
- 前置条件：无

当前没有可执行调用步骤。

#### 当前缺失能力

- 当前 Proto、Go SDK 和 Python 门面均没有视频上传 RPC。
- 当前没有视频 configure RPC、视频转码状态查询或封面上传工作流。
- 在完成真实抓包、契约和黑盒测试前，禁止复用 UploadImage/CreateImagePost 伪装视频发布。

<a id="workflow-collect_profile_threads"></a>

### 分页采集主页帖子 `collect_profile_threads`

- 状态：`available`
- 目标：读取指定账号主页帖子，并使用游标持续翻页。
- 前置条件：已有有效 IGT:2 token。；准备目标 user_id。

| 步骤 | 调用接口 | 目的 | 输入与参数来源 | 响应与下一步 |
| ---: | --- | --- | --- | --- |
| 1 | `ProfileClient.list_profile_threads`<br>`threads.profile.v1.ProfileService/ListProfileThreads` | 首次调用不传 max_id，读取 threads 强类型结果。 | user_id 为目标账号；exclude_reposts 按采集需求设置。 | 消费 threads[].items[].post，并读取 next_cursor。 |
| 2 | `ProfileClient.list_profile_threads`<br>`threads.profile.v1.ProfileService/ListProfileThreads` | next_cursor 非空时继续请求下一页。 | 将上一页 next_cursor 传入下一次 max_id。 | next_cursor 为空时结束；raw_json 只用于未建模字段排错。 |

#### 步骤间参数传递

- `ProfileThreadsPage.next_cursor` → `ListProfileThreadsRequest.max_id`：分页游标来自上一次真实响应。

## Module And Method Reference

## 登录与当前账号 — `client.auth`

登录、校验会话并读取当前账号身份。

### 账号登录 — `AuthClient.login`

```python
async def login(*, username: str, password: str, device, two_factor_seed: str | None=None, app_version: str | None=None, allow_unattested_login: bool=False)
```

- RPC：`threads.auth.v1.AuthService/Login`
- 状态：`experimental`
- 何时调用：没有可用 IGT:2，并且需要通过账密建立新会话时。
- 前置条件：持久 LoginDevice；生产环境通过 THREADS_KEYBOX_FILE 加载与设备匹配的 keybox signer。
- 响应用途：提取 session.token，核对 verified_user.username 和 assurance；stable_for_automation 不能由即时成功推断。
- 业务成功：success=true、session.token 为 IGT:2，且 verified_user.username 与请求账号一致；stable_for_automation 仍必须为 false，直到硬件证明和长期存活观测另行通过。
- 后续接口：threads.auth.v1.AuthService/GetCurrentUser
- 所属工作流：login

#### 参数与来源

| 字段 | 类型 | 字段号 | 规则 | 参数来源 | 说明 |
| --- | --- | ---: | --- | --- | --- |
| `username` | `string` | 1 | 单值 | 用户输入：Threads/Instagram 登录账号。 | - |
| `password` | `string` | 2 | 单值 | 受保护账号配置：禁止写入源码和日志。 | - |
| `two_factor_seed` | `string` | 3 | 可选 | 受保护账号配置：启用 TOTP 时提供。 | - |
| `device` | `LoginDevice` | 4 | 单值 | 持久设备身份：同一账号长期复用，不能每次随机生成。 | - |
| `app_version` | `string` | 5 | 可选 | 运行配置：通常省略并使用服务端支持版本。 | 当前只支持 421；空值也使用 421。 |
| `allow_unattested_login` | `bool` | 6 | 单值 | 固定安全开关：默认 false；仅经明确高风险授权后设为 true。 | 高风险开关。false（默认）时，服务端未配置硬件 signer 或签名失败都会阻止发送登录凭据。 true 只表示调用方接受软件登录尝试，不表示返回 session 可长期用于自动化。 |

#### 返回 `threads.auth.v1.LoginResponse`

| 字段 | 类型 | 字段号 | 规则 | 说明 |
| --- | --- | ---: | --- | --- |
| `success` | `bool` | 1 | 单值 | success 只有在取到 IGT:2 且用同一代理、设备完成 whoami 身份校验后才为 true。 |
| `session` | `LoginSession` | 2 | 可选 | - |
| `steps` | `LoginStep` | 3 | 数组 | - |
| `device` | `LoginDevice` | 4 | 单值 | 含登录期间服务端下发并回填的 machine_id。 |
| `verified_user` | `CurrentUser` | 5 | 可选 | - |
| `assurance` | `LoginAssurance` | 6 | 单值 | - |
| `stable_for_automation` | `bool` | 7 | 单值 | 当前实现永不把即时登录成功等价为长期稳定；必须由真实硬件证明和存活观测另行确认。 |
| `stability_warning` | `string` | 8 | 单值 | - |

### 读取当前账号 — `AuthClient.get_current_user`

```python
async def get_current_user(edit=None)
```

- RPC：`threads.auth.v1.AuthService/GetCurrentUser`
- 状态：`available`
- 何时调用：校验 token 身份、取得当前 uid，或在资料编辑前读取完整原值时。
- 前置条件：ThreadsClient 已注入有效 IGT:2 token。
- 响应用途：pk 是发帖 uid 和当前账号 user_id；edit=true 时使用完整资料字段回填 EditProfile。
- 业务成功：响应 pk 与注入 token 所属账号一致。
- 后续接口：threads.profile.v1.ProfileService/EditProfile；threads.posts.v1.PostsService/CreateTextPost；threads.posts.v1.PostsService/CreateImagePost
- 所属工作流：verify_session；login；edit_profile；create_text_post；create_image_post

#### 参数与来源

| 字段 | 类型 | 字段号 | 规则 | 参数来源 | 说明 |
| --- | --- | ---: | --- | --- | --- |
| `edit` | `bool` | 1 | 可选 | 调用场景：普通身份校验传 false；资料编辑前传 true。 | edit=true：编辑资料页的读取场景（字段更全）。 |

#### 返回 `threads.auth.v1.CurrentUser`

| 字段 | 类型 | 字段号 | 规则 | 说明 |
| --- | --- | ---: | --- | --- |
| `pk` | `string` | 1 | 单值 | - |
| `username` | `string` | 2 | 单值 | - |
| `full_name` | `string` | 3 | 可选 | - |
| `biography` | `string` | 4 | 可选 | - |
| `profile_pic_url` | `string` | 5 | 可选 | - |
| `email` | `string` | 6 | 可选 | - |
| `text_app_biography` | `string` | 7 | 可选 | - |
| `external_url` | `string` | 8 | 可选 | - |
| `bio_links` | `BioLink` | 9 | 数组 | - |
| `text_app_cover_photo_url` | `string` | 10 | 可选 | - |
| `is_private` | `bool` | 11 | 可选 | - |
| `is_verified` | `bool` | 12 | 可选 | - |
## 资料与主页帖子 — `client.profile`

读取用户资料、保存当前账号资料并分页采集主页帖子。

### 读取指定用户资料 — `ProfileClient.get_user_info`

```python
async def get_user_info(user_id: str) -> 'profile_pb2.User'
```

- RPC：`threads.profile.v1.ProfileService/GetUserInfo`
- 状态：`available`
- 何时调用：已知用户 ID，需要读取公开资料和账号统计时。
- 前置条件：已有有效 IGT:2 token。
- 响应用途：核对 pk 等于请求 user_id，再使用 username、计数和隐私状态。
- 业务成功：响应 pk 与请求 user_id 一致。
- 后续接口：threads.profile.v1.ProfileService/ListProfileThreads；threads.friendships.v1.FriendshipsService/GetFriendshipStatus
- 所属工作流：无

#### 参数与来源

| 字段 | 类型 | 字段号 | 规则 | 参数来源 | 说明 |
| --- | --- | ---: | --- | --- | --- |
| `user_id` | `string` | 1 | 单值 | 目标对象：来自搜索结果、帖子作者 pk 或业务数据库。 | Threads/IG 用户数字 id（pk）。 |

#### 返回 `threads.profile.v1.User`

| 字段 | 类型 | 字段号 | 规则 | 说明 |
| --- | --- | ---: | --- | --- |
| `pk` | `string` | 1 | 单值 | 用户数字 id |
| `username` | `string` | 2 | 单值 | - |
| `full_name` | `string` | 3 | 可选 | - |
| `biography` | `string` | 4 | 可选 | - |
| `profile_pic_url` | `string` | 5 | 可选 | - |
| `follower_count` | `int64` | 6 | 可选 | - |
| `following_count` | `int64` | 7 | 可选 | - |
| `media_count` | `int64` | 8 | 可选 | - |
| `is_private` | `bool` | 9 | 可选 | - |
| `is_verified` | `bool` | 10 | 可选 | - |

### 分页读取主页帖子 — `ProfileClient.list_profile_threads`

```python
async def list_profile_threads(user_id: str, max_id: str | None=None, exclude_reposts: bool | None=None) -> 'profile_pb2.ProfileThreadsPage'
```

- RPC：`threads.profile.v1.ProfileService/ListProfileThreads`
- 状态：`available`
- 何时调用：采集指定账号主页帖子、验证发帖或验证删除结果时。
- 前置条件：已有有效 token 和目标 user_id。
- 响应用途：优先消费 threads[].items[].post；next_cursor 传给下一页 max_id；raw_json 仅用于兼容排错。
- 业务成功：status=ok，threads[].items[].post 可直接读取，帖子作者与请求 user_id 的业务语义一致。
- 后续接口：threads.profile.v1.ProfileService/ListProfileThreads
- 所属工作流：create_text_post；create_image_post；collect_profile_threads

#### 参数与来源

| 字段 | 类型 | 字段号 | 规则 | 参数来源 | 说明 |
| --- | --- | ---: | --- | --- | --- |
| `user_id` | `string` | 1 | 单值 | 目标对象：当前账号用 GetCurrentUser.pk，其他账号来自搜索或资料接口。 | - |
| `max_id` | `string` | 2 | 可选 | 上一页响应：使用 ProfileThreadsPage.next_cursor；首次调用省略。 | 分页 |
| `exclude_reposts` | `bool` | 3 | 可选 | 采集策略：是否排除转发，可省略使用上游默认。 | - |

#### 返回 `threads.profile.v1.ProfileThreadsPage`

| 字段 | 类型 | 字段号 | 规则 | 说明 |
| --- | --- | ---: | --- | --- |
| `raw_json` | `string` | 1 | 单值 | - |
| `next_cursor` | `string` | 2 | 可选 | - |
| `threads` | `ProfileThread` | 3 | 数组 | - |
| `status` | `string` | 4 | 单值 | - |

### 保存当前账号资料 — `ProfileClient.edit_profile`

```python
async def edit_profile(username, first_name, biography, is_private, uuid, external_url=None, url_title=None)
```

- RPC：`threads.profile.v1.ProfileService/EditProfile`
- 状态：`available`
- 何时调用：修改用户名、显示名、简介、隐私状态或外部链接时。
- 前置条件：先调用 GetCurrentUser(edit=true) 读取完整原值。
- 响应用途：核对返回资料，并再次 GetCurrentUser(edit=true) 验证真实保存结果。
- 业务成功：保存后重新读取当前账号，资料字段与请求值一致。
- 后续接口：threads.auth.v1.AuthService/GetCurrentUser
- 所属工作流：edit_profile

#### 参数与来源

| 字段 | 类型 | 字段号 | 规则 | 参数来源 | 说明 |
| --- | --- | ---: | --- | --- | --- |
| `username` | `string` | 1 | 单值 | 前置响应或用户修改：未修改时回填 CurrentUser.username。 | 必填 |
| `first_name` | `string` | 2 | 单值 | 前置响应或用户修改：未修改时回填 CurrentUser.full_name。 | 必填，显示名（= full_name） |
| `biography` | `string` | 3 | 单值 | 前置响应或用户修改：未修改时回填 CurrentUser.biography。 | 必填，个性签名（无签名传空串） |
| `is_private` | `bool` | 4 | 单值 | 前置响应或用户修改：未修改时回填 CurrentUser.is_private。 | 必填 |
| `uuid` | `string` | 5 | 单值 | 持久设备身份：当前账号绑定的 uuid。 | 必填，客户端 UUID（_uuid） |
| `external_url` | `string` | 6 | 可选 | 前置响应或用户修改：未修改时回填 CurrentUser.external_url。 | 可选，链接 URL；服务端写入 bio_links[] |
| `url_title` | `string` | 7 | 可选 | 前置响应或用户修改：从 CurrentUser.bio_links 对应链接标题回填。 | 可选，链接标题，配合 external_url |

#### 返回 `threads.auth.v1.CurrentUser`

| 字段 | 类型 | 字段号 | 规则 | 说明 |
| --- | --- | ---: | --- | --- |
| `pk` | `string` | 1 | 单值 | - |
| `username` | `string` | 2 | 单值 | - |
| `full_name` | `string` | 3 | 可选 | - |
| `biography` | `string` | 4 | 可选 | - |
| `profile_pic_url` | `string` | 5 | 可选 | - |
| `email` | `string` | 6 | 可选 | - |
| `text_app_biography` | `string` | 7 | 可选 | - |
| `external_url` | `string` | 8 | 可选 | - |
| `bio_links` | `BioLink` | 9 | 数组 | - |
| `text_app_cover_photo_url` | `string` | 10 | 可选 | - |
| `is_private` | `bool` | 11 | 可选 | - |
| `is_verified` | `bool` | 12 | 可选 | - |
## 帖子发布与删除 — `client.posts`

检查文本、发布文本帖、上传图片、发布单图帖和删除帖子。

### 检查冒犯文本 — `PostsClient.check_offensive_text`

```python
async def check_offensive_text(text_list, media_id: str | None=None)
```

- RPC：`threads.posts.v1.PostsService/CheckOffensiveText`
- 状态：`known-upstream-error`
- 何时调用：发帖前希望执行上游文本风险检查时；当前真实上游返回 404。
- 前置条件：已有有效 IGT:2 token。
- 响应用途：当前以 gRPC 错误路径处理，不能把未返回结果视为安全。
- 业务成功：当前真实上游返回 HTTP 404，测试以真实错误映射为验收结果。
- 后续接口：无
- 所属工作流：无

#### 参数与来源

| 字段 | 类型 | 字段号 | 规则 | 参数来源 | 说明 |
| --- | --- | ---: | --- | --- | --- |
| `text_list` | `string` | 1 | 数组 | 用户内容：待检查的一段或多段文本。 | 待检测文本（对应 form text_list，JSON 数组）。 |
| `media_id` | `string` | 2 | 可选 | 已有媒体上下文：编辑或关联媒体时提供，否则省略。 | 可选关联 media_id。 |

#### 返回 `threads.posts.v1.OffensiveCheck`

| 字段 | 类型 | 字段号 | 规则 | 说明 |
| --- | --- | ---: | --- | --- |
| `is_offensive` | `bool` | 1 | 单值 | - |
| `minimum_next_timestamp` | `int64` | 2 | 可选 | - |
| `text_language` | `string` | 3 | 可选 | - |

### 创建文本帖 — `PostsClient.create_text_post`

```python
async def create_text_post(caption, uid, device_id, uuid, upload_id=None, reply_control=0, device=None, camera_session_id=None, nav_chain=None, timezone_offset=None, tag_header=None, location=None, poll=None)
```

- RPC：`threads.posts.v1.PostsService/CreateTextPost`
- 状态：`available`
- 何时调用：发布不带图片或视频的 Threads 文本帖子时。
- 前置条件：有效 token、当前账号 uid、持久 device_id/uuid、写操作授权。
- 响应用途：保存 id/code/permalink，并核对 caption、user 和主页重新读取结果。
- 业务成功：返回帖子属于请求 uid，caption 与请求一致，并能从账号主页重新读取。
- 后续接口：threads.profile.v1.ProfileService/ListProfileThreads
- 所属工作流：create_text_post

#### 参数与来源

| 字段 | 类型 | 字段号 | 规则 | 参数来源 | 说明 |
| --- | --- | ---: | --- | --- | --- |
| `caption` | `string` | 1 | 单值 | 用户输入：帖子正文。 | 正文（对应 caption）。 |
| `uid` | `string` | 2 | 单值 | 前置响应：GetCurrentUser.pk。 | 发布主体与设备三件套（对应 _uid / device_id / _uuid），须由调用方提供。 |
| `device_id` | `string` | 3 | 单值 | 持久设备身份：账号绑定的 Android device_id。 | - |
| `uuid` | `string` | 4 | 单值 | 持久设备身份：账号绑定的 uuid。 | - |
| `upload_id` | `string` | 5 | 可选 | 运行时生成：可省略，由 Go SDK 生成；重试时不得随意复用。 | 幂等批次 id（缺省由 SDK 生成 upload_id、publish_id 固定 "1"）。 |
| `reply_control` | `int32` | 6 | 可选 | 产品设置：回复权限枚举，默认 0。 | 回复权限：0=everyone（对应 text_post_app_info.reply_control）。 |
| `device` | `Device` | 7 | 单值 | 持久设备身份：与 device_id/uuid 属于同一设备。 | device 对象（必填，apis/post-media/发帖.md）。 |
| `camera_session_id` | `string` | 8 | 可选 | 运行时生成：模拟发布会话时提供，否则省略。 | 埋点/会话（必填，缺省由 SDK 生成/留空）。 |
| `nav_chain` | `string` | 9 | 可选 | 运行时上下文：导航链，普通调用可省略。 | - |
| `timezone_offset` | `string` | 10 | 可选 | 运行环境：账号所在地时区偏移。 | - |
| `tag_header` | `string` | 11 | 可选 | 主题功能：先用 ValidateTag 校验后按上游格式提供。 | 可选功能：主题 / 位置 / 投票（勾选才传）。 主题 display_text |
| `location` | `Location` | 12 | 可选 | 用户选择：posts_pb2.Location，未选择位置时省略。 | - |
| `poll` | `Poll` | 13 | 可选 | 用户输入：posts_pb2.Poll，未创建投票时省略。 | - |

#### 返回 `threads.posts.v1.Media`

| 字段 | 类型 | 字段号 | 规则 | 说明 |
| --- | --- | ---: | --- | --- |
| `id` | `string` | 1 | 单值 | - |
| `pk` | `int64` | 2 | 单值 | - |
| `fbid` | `int64` | 3 | 单值 | - |
| `taken_at` | `int64` | 4 | 单值 | - |
| `media_type` | `int32` | 5 | 单值 | 19 = text_post |
| `code` | `string` | 6 | 单值 | 短代码 |
| `product_type` | `string` | 7 | 单值 | text_post |
| `permalink` | `string` | 8 | 单值 | - |
| `integrity_review_decision` | `string` | 9 | 单值 | pending 表示异步审核中 |
| `caption` | `Caption` | 10 | 单值 | - |
| `text_post_app_info` | `TextPostAppInfo` | 11 | 单值 | - |
| `has_liked` | `bool` | 12 | 单值 | - |
| `like_count` | `int64` | 13 | 单值 | - |
| `meta_place` | `MetaPlace` | 14 | 可选 | 位置回显（带位置发帖时） |
| `image_versions2` | `ImageVersions2` | 15 | 可选 | - |
| `original_width` | `int32` | 16 | 单值 | - |
| `original_height` | `int32` | 17 | 单值 | - |
| `user` | `MediaUser` | 18 | 可选 | 列表端点的顶层作者 |

### 上传图片 — `PostsClient.upload_image`

```python
async def upload_image(image_data: bytes, original_width: int, original_height: int, upload_id: str | None=None, mime_type: str | None=None, is_optimistic_upload: bool | None=None, msssim: float | None=None, ssim: float | None=None, waterfall_id: str | None=None) -> 'posts_pb2.UploadImageResult'
```

- RPC：`threads.posts.v1.PostsService/UploadImage`
- 状态：`available`
- 何时调用：创建单图帖前上传 WebP 图片二进制时。
- 前置条件：有效 token、WebP 图片字节、真实宽高、写操作授权。
- 响应用途：status 必须为 ok；将 upload_id 原样传给 CreateImagePost。
- 业务成功：响应 status=ok 且 upload_id 非空；该 upload_id 可被 CreateImagePost 接受。
- 后续接口：threads.posts.v1.PostsService/CreateImagePost
- 所属工作流：create_image_post

#### 参数与来源

| 字段 | 类型 | 字段号 | 规则 | 参数来源 | 说明 |
| --- | --- | ---: | --- | --- | --- |
| `image_data` | `bytes` | 1 | 单值 | 本地文件或内存：WebP 原始 bytes。 | 原始图片字节；当前已确认样本为 image/webp。 |
| `original_width` | `int32` | 2 | 单值 | 图片元数据：解码后的真实像素宽度。 | - |
| `original_height` | `int32` | 3 | 单值 | 图片元数据：解码后的真实像素高度。 | - |
| `upload_id` | `string` | 4 | 可选 | 运行时生成：通常省略并由 Go SDK 生成。 | 缺省由 SDK 生成；后续 CreateImagePost 必须使用响应中的 upload_id。 |
| `mime_type` | `string` | 5 | 可选 | 固定协议值：当前确认 image/webp。 | 缺省 image/webp；当前仅确认 image/webp。 |
| `is_optimistic_upload` | `bool` | 6 | 可选 | 上传策略：通常省略使用 SDK 默认值。 | App 抓包存在乐观预上传与正式上传两种模式；缺省为正式上传。 |
| `msssim` | `double` | 7 | 可选 | 图片质量计算：调用方实际计算时提供，否则省略。 | 图片压缩质量指标；调用方掌握真实编码结果时再传。 |
| `ssim` | `double` | 8 | 可选 | 图片质量计算：调用方实际计算时提供，否则省略。 | - |
| `waterfall_id` | `string` | 9 | 可选 | 运行时生成：上传链路追踪 ID，通常省略。 | 缺省由 SDK 生成。 |

#### 返回 `threads.posts.v1.UploadImageResult`

| 字段 | 类型 | 字段号 | 规则 | 说明 |
| --- | --- | ---: | --- | --- |
| `upload_id` | `string` | 1 | 单值 | - |
| `status` | `string` | 2 | 单值 | - |

### 创建单图帖 — `PostsClient.create_image_post`

```python
async def create_image_post(caption: str, uid: str, device_id: str, uuid: str, upload_id: str, original_width: int, original_height: int, reply_control: int=0, device: 'posts_pb2.Device | None'=None, camera_session_id: str | None=None, nav_chain: str | None=None, timezone_offset: str | None=None, tag_header: str | None=None, location: 'posts_pb2.Location | None'=None, poll: 'posts_pb2.Poll | None'=None, custom_accessibility_caption: str | None=None) -> 'posts_pb2.Media'
```

- RPC：`threads.posts.v1.PostsService/CreateImagePost`
- 状态：`available`
- 何时调用：UploadImage 成功后，把已上传图片发布为单图帖子时。
- 前置条件：UploadImageResult.status=ok、当前账号 uid、持久设备身份、写操作授权。
- 响应用途：核对 media_type=1、image_versions2、caption 和 user，并保存 id/code/permalink。
- 业务成功：返回 media_type=1，正文与请求一致，image_versions2 非空，并能从账号主页重新读取。
- 后续接口：threads.profile.v1.ProfileService/ListProfileThreads
- 所属工作流：create_image_post

#### 参数与来源

| 字段 | 类型 | 字段号 | 规则 | 参数来源 | 说明 |
| --- | --- | ---: | --- | --- | --- |
| `caption` | `string` | 1 | 单值 | 用户输入：帖子正文。 | 图片帖允许空正文。 |
| `uid` | `string` | 2 | 单值 | 前置响应：GetCurrentUser.pk。 | - |
| `device_id` | `string` | 3 | 单值 | 持久设备身份：账号绑定的 Android device_id。 | - |
| `uuid` | `string` | 4 | 单值 | 持久设备身份：账号绑定的 uuid。 | - |
| `upload_id` | `string` | 5 | 单值 | 前置响应：UploadImageResult.upload_id。 | 必须使用 UploadImage 返回的 upload_id。 |
| `reply_control` | `int32` | 6 | 可选 | 产品设置：回复权限枚举，默认 0。 | - |
| `device` | `Device` | 7 | 单值 | 持久设备身份：与 device_id/uuid 属于同一设备。 | - |
| `camera_session_id` | `string` | 8 | 可选 | 运行时生成：模拟发布会话时提供，否则省略。 | - |
| `nav_chain` | `string` | 9 | 可选 | 运行时上下文：导航链，普通调用可省略。 | - |
| `timezone_offset` | `string` | 10 | 可选 | 运行环境：账号所在地时区偏移。 | - |
| `tag_header` | `string` | 11 | 可选 | 主题功能：先用 ValidateTag 校验后按上游格式提供。 | - |
| `location` | `Location` | 12 | 可选 | 用户选择：posts_pb2.Location，未选择位置时省略。 | - |
| `poll` | `Poll` | 13 | 可选 | 用户输入：posts_pb2.Poll，未创建投票时省略。 | - |
| `original_width` | `int32` | 14 | 单值 | 前置请求：与 UploadImage.original_width 保持一致。 | - |
| `original_height` | `int32` | 15 | 单值 | 前置请求：与 UploadImage.original_height 保持一致。 | - |
| `custom_accessibility_caption` | `string` | 16 | 可选 | 用户输入：图片无障碍说明，可省略。 | - |

#### 返回 `threads.posts.v1.Media`

| 字段 | 类型 | 字段号 | 规则 | 说明 |
| --- | --- | ---: | --- | --- |
| `id` | `string` | 1 | 单值 | - |
| `pk` | `int64` | 2 | 单值 | - |
| `fbid` | `int64` | 3 | 单值 | - |
| `taken_at` | `int64` | 4 | 单值 | - |
| `media_type` | `int32` | 5 | 单值 | 19 = text_post |
| `code` | `string` | 6 | 单值 | 短代码 |
| `product_type` | `string` | 7 | 单值 | text_post |
| `permalink` | `string` | 8 | 单值 | - |
| `integrity_review_decision` | `string` | 9 | 单值 | pending 表示异步审核中 |
| `caption` | `Caption` | 10 | 单值 | - |
| `text_post_app_info` | `TextPostAppInfo` | 11 | 单值 | - |
| `has_liked` | `bool` | 12 | 单值 | - |
| `like_count` | `int64` | 13 | 单值 | - |
| `meta_place` | `MetaPlace` | 14 | 可选 | 位置回显（带位置发帖时） |
| `image_versions2` | `ImageVersions2` | 15 | 可选 | - |
| `original_width` | `int32` | 16 | 单值 | - |
| `original_height` | `int32` | 17 | 单值 | - |
| `user` | `MediaUser` | 18 | 可选 | 列表端点的顶层作者 |

### 删除帖子 — `PostsClient.delete_post`

```python
async def delete_post(media_id, uid, uuid)
```

- RPC：`threads.posts.v1.PostsService/DeletePost`
- 状态：`available`
- 何时调用：删除当前账号已有帖子或清理测试帖子时。
- 前置条件：有效 token、目标 media_id、当前账号 uid/uuid、写操作授权。
- 响应用途：did_delete 必须为 true，并通过 ListProfileThreads 确认目标帖子消失。
- 业务成功：did_delete=true，且重新读取主页确认目标帖子已经消失。
- 后续接口：threads.profile.v1.ProfileService/ListProfileThreads
- 所属工作流：无

#### 参数与来源

| 字段 | 类型 | 字段号 | 规则 | 参数来源 | 说明 |
| --- | --- | ---: | --- | --- | --- |
| `media_id` | `string` | 1 | 单值 | 前置响应：创建接口返回的 Media.id，或主页帖子中的 post.id。 | = URL 中的 media_id（pk_uid） |
| `uid` | `string` | 2 | 单值 | 前置响应：GetCurrentUser.pk。 | _uid |
| `uuid` | `string` | 3 | 单值 | 持久设备身份：当前账号绑定的 uuid。 | _uuid |

#### 返回 `threads.posts.v1.DeletePostResult`

| 字段 | 类型 | 字段号 | 规则 | 说明 |
| --- | --- | ---: | --- | --- |
| `did_delete` | `bool` | 1 | 单值 | - |
| `cxp_deep_deletion_waterfall_id` | `string` | 2 | 可选 | - |
## 回复采集 — `client.replies`

分页读取指定账号的回复列表。

### 分页读取账号回复 — `RepliesClient.list_profile_replies`

```python
async def list_profile_replies(user_id, max_id=None)
```

- RPC：`threads.replies.v1.RepliesService/ListProfileReplies`
- 状态：`available`
- 何时调用：采集指定账号发布的回复时。
- 前置条件：已有有效 token 和目标 user_id。
- 响应用途：解析 raw_json 获取回复对象；next_cursor 用于下一页 max_id。
- 业务成功：RawList.raw_json 可解析，响应属于请求 user_id 的回复列表。
- 后续接口：threads.replies.v1.RepliesService/ListProfileReplies
- 所属工作流：无

#### 参数与来源

| 字段 | 类型 | 字段号 | 规则 | 参数来源 | 说明 |
| --- | --- | ---: | --- | --- | --- |
| `user_id` | `string` | 1 | 单值 | 目标对象：来自当前账号 pk、搜索结果或资料接口。 | - |
| `max_id` | `string` | 2 | 可选 | 上一页响应：使用 RawList.next_cursor；首次调用省略。 | 分页 |

#### 返回 `threads.common.v1.RawList`

| 字段 | 类型 | 字段号 | 规则 | 说明 |
| --- | --- | ---: | --- | --- |
| `raw_json` | `string` | 1 | 单值 | - |
| `next_cursor` | `string` | 2 | 可选 | - |
## 搜索与主题校验 — `client.search`

关键词搜索以及发帖主题标签校验。

### 关键词搜索 — `SearchClient.keyword_search`

```python
async def keyword_search(query, page_token=None, rank_token=None, search_session_id=None)
```

- RPC：`threads.search.v1.SearchService/KeywordSearch`
- 状态：`available`
- 何时调用：按关键词查找 Threads 账号或内容时。
- 前置条件：已有有效 IGT:2 token。
- 响应用途：解析 raw_json，保留未知字段；分页参数必须来自同一轮搜索响应和会话。
- 业务成功：RawList.raw_json 可解析，结果与请求 query 相关。
- 后续接口：threads.search.v1.SearchService/KeywordSearch
- 所属工作流：无

#### 参数与来源

| 字段 | 类型 | 字段号 | 规则 | 参数来源 | 说明 |
| --- | --- | ---: | --- | --- | --- |
| `query` | `string` | 1 | 单值 | 用户输入：搜索关键词。 | - |
| `page_token` | `string` | 2 | 可选 | 上一页响应或 raw_json：按上游分页字段传递；首次调用省略。 | - |
| `rank_token` | `string` | 3 | 可选 | 首次响应或搜索会话上下文：后续页保持同一值。 | - |
| `search_session_id` | `string` | 4 | 可选 | 运行时生成：同一轮搜索分页复用同一个会话 ID。 | - |

#### 返回 `threads.common.v1.RawList`

| 字段 | 类型 | 字段号 | 规则 | 说明 |
| --- | --- | ---: | --- | --- |
| `raw_json` | `string` | 1 | 单值 | - |
| `next_cursor` | `string` | 2 | 可选 | - |

### 校验发帖主题 — `SearchClient.validate_tag`

```python
async def validate_tag(tag_name)
```

- RPC：`threads.search.v1.SearchService/ValidateTag`
- 状态：`available`
- 何时调用：发帖前需要确认主题标签是否有效或敏感时。
- 前置条件：已有有效 IGT:2 token。
- 响应用途：is_valid=true 且业务允许时，才构造发帖 tag_header；is_sensitive 需触发产品确认。
- 业务成功：返回 is_valid/is_sensitive 对应请求 tag_name。
- 后续接口：threads.posts.v1.PostsService/CreateTextPost；threads.posts.v1.PostsService/CreateImagePost
- 所属工作流：无

#### 参数与来源

| 字段 | 类型 | 字段号 | 规则 | 参数来源 | 说明 |
| --- | --- | ---: | --- | --- | --- |
| `tag_name` | `string` | 1 | 单值 | 用户输入：不含 # 的主题名称。 | - |

#### 返回 `threads.search.v1.TagValidation`

| 字段 | 类型 | 字段号 | 规则 | 说明 |
| --- | --- | ---: | --- | --- |
| `is_valid` | `bool` | 1 | 单值 | - |
| `is_sensitive` | `bool` | 2 | 单值 | - |
## 推荐用户 — `client.feed`

分页读取 Threads 推荐账号。

### 读取推荐用户 — `FeedClient.list_recommended_users`

```python
async def list_recommended_users(paging_token=None, recommendation_type=None)
```

- RPC：`threads.feed.v1.FeedService/ListRecommendedUsers`
- 状态：`available`
- 何时调用：需要获取 Threads 推荐账号或继续推荐流分页时。
- 前置条件：已有有效 IGT:2 token。
- 响应用途：解析 raw_json 获取推荐对象；next_cursor 用于下一页 paging_token。
- 业务成功：RawList.raw_json 可解析，且分页游标来自同一次真实响应。
- 后续接口：threads.feed.v1.FeedService/ListRecommendedUsers
- 所属工作流：无

#### 参数与来源

| 字段 | 类型 | 字段号 | 规则 | 参数来源 | 说明 |
| --- | --- | ---: | --- | --- | --- |
| `paging_token` | `string` | 1 | 可选 | 上一页响应：使用 RawList.next_cursor；首次调用省略。 | 分页游标（对应 paging_token）。 |
| `recommendation_type` | `string` | 2 | 可选 | 产品场景配置：按上游支持的推荐类型填写，未知时省略。 | 推荐类型（recommended_users / great_accounts / ...，见报告 §5.1）。 |

#### 返回 `threads.common.v1.RawList`

| 字段 | 类型 | 字段号 | 规则 | 说明 |
| --- | --- | ---: | --- | --- |
| `raw_json` | `string` | 1 | 单值 | - |
| `next_cursor` | `string` | 2 | 可选 | - |
## 关系状态 — `client.friendships`

读取与目标账号之间的关注、拉黑和静音状态。

### 读取关系状态 — `FriendshipsClient.get_friendship_status`

```python
async def get_friendship_status(user_id: str, is_external_deeplink_profile_view: bool=False) -> 'friendships_pb2.FriendshipStatus'
```

- RPC：`threads.friendships.v1.FriendshipsService/GetFriendshipStatus`
- 状态：`available`
- 何时调用：需要确认当前账号是否关注、被关注、拉黑或静音目标账号时。
- 前置条件：已有有效 IGT:2 token。
- 响应用途：直接读取 following、followed_by、blocking、muting 等强类型字段。
- 业务成功：返回关系状态属于请求 user_id 对应的目标账号。
- 后续接口：无
- 所属工作流：无

#### 参数与来源

| 字段 | 类型 | 字段号 | 规则 | 参数来源 | 说明 |
| --- | --- | ---: | --- | --- | --- |
| `user_id` | `string` | 1 | 单值 | 目标对象：来自 GetUserInfo.pk、搜索结果或业务数据库。 | 目标用户数字 id。 |
| `is_external_deeplink_profile_view` | `bool` | 2 | 可选 | 调用场景：普通 API 调用使用 false。 | 对应请求参数 is_external_deeplink_profile_view（默认 false）。 |

#### 返回 `threads.friendships.v1.FriendshipStatus`

| 字段 | 类型 | 字段号 | 规则 | 说明 |
| --- | --- | ---: | --- | --- |
| `following` | `bool` | 1 | 可选 | - |
| `followed_by` | `bool` | 2 | 可选 | - |
| `outgoing_request` | `bool` | 3 | 可选 | - |
| `incoming_request` | `bool` | 4 | 可选 | - |
| `blocking` | `bool` | 5 | 可选 | - |
| `is_blocking_reel` | `bool` | 6 | 可选 | - |
| `muting` | `bool` | 7 | 可选 | - |
| `is_muting_reel` | `bool` | 8 | 可选 | - |
| `is_muting_notes` | `bool` | 9 | 可选 | - |
| `is_muting_media_notes` | `bool` | 10 | 可选 | - |
| `is_muting_media_reposts` | `bool` | 11 | 可选 | - |
| `is_private` | `bool` | 12 | 可选 | - |
| `subscribed` | `bool` | 13 | 可选 | - |
| `is_eligible_to_subscribe` | `bool` | 14 | 可选 | - |
| `is_viewer_unconnected` | `bool` | 15 | 可选 | - |
| `should_show_profile_upsell` | `bool` | 16 | 可选 | - |
| `is_banner_profile_upsell` | `bool` | 17 | 可选 | - |
| `reachability_status` | `int64` | 18 | 可选 | int 枚举（apis/friendships/关注关系.md） |
## 账号洞察 — `client.insights`

读取账号洞察；当前缺少真实 GraphQL doc_id，尚不可用。

### 读取账号洞察 — `InsightsClient.get_account_insights`

```python
async def get_account_insights(user_id=None)
```

- RPC：`threads.insights.v1.InsightsService/GetAccountInsights`
- 状态：`unimplemented`
- 何时调用：需要账号洞察数据时；当前接口尚不可用。
- 前置条件：当前缺少真实 GraphQL doc_id。
- 响应用途：当前固定得到 UNIMPLEMENTED，不能解析为真实洞察。
- 业务成功：当前固定返回 gRPC UNIMPLEMENTED。
- 后续接口：无
- 所属工作流：无

#### 参数与来源

| 字段 | 类型 | 字段号 | 规则 | 参数来源 | 说明 |
| --- | --- | ---: | --- | --- | --- |
| `user_id` | `string` | 1 | 可选 | 目标对象：省略表示当前账号，或传目标账号 ID。 | - |

#### 返回 `threads.common.v1.RawList`

| 字段 | 类型 | 字段号 | 规则 | 说明 |
| --- | --- | ---: | --- | --- |
| `raw_json` | `string` | 1 | 单值 | - |
| `next_cursor` | `string` | 2 | 可选 | - |
## 提及建议 — `client.mentions`

获取 @ 提及建议；当前缺少真实 GraphQL doc_id，尚不可用。

### 获取提及建议 — `MentionsClient.get_mention_suggestions`

```python
async def get_mention_suggestions(query)
```

- RPC：`threads.mentions.v1.MentionsService/GetMentionSuggestions`
- 状态：`unimplemented`
- 何时调用：输入 @ 用户名时需要候选建议；当前接口尚不可用。
- 前置条件：当前缺少真实 GraphQL doc_id。
- 响应用途：当前固定得到 UNIMPLEMENTED，不能作为发帖候选。
- 业务成功：当前固定返回 gRPC UNIMPLEMENTED。
- 后续接口：无
- 所属工作流：无

#### 参数与来源

| 字段 | 类型 | 字段号 | 规则 | 参数来源 | 说明 |
| --- | --- | ---: | --- | --- | --- |
| `query` | `string` | 1 | 单值 | 用户输入：@ 后正在输入的用户名片段。 | - |

#### 返回 `threads.common.v1.RawList`

| 字段 | 类型 | 字段号 | 规则 | 说明 |
| --- | --- | ---: | --- | --- |
| `raw_json` | `string` | 1 | 单值 | - |
| `next_cursor` | `string` | 2 | 可选 | - |
