通用约定
- 基地址:
https://x2post.com/api/open/v1 - 鉴权:
Authorization: Bearer <access_token>(OAuth 授权拿到)或<个人令牌>(用户在「资料设置 → 接口令牌」自建) - 限流:每个令牌每分钟 60 次,超出
429+Retry-After: 60;写接口另有与站内同口径的单动作频次限制(见「写接口」) - 时间格式:ISO-8601(
2026-09-22T00:00:00,东八区) - 列表分页统一为
page(默认 0)+size,返回{ items, page, size, totalElements, totalPages } viewCount为去重后的阅读数:同一登录用户或同一 IP 30 分钟内对同一帖只计一次(作者本人也计入);爬虫与空 UA 不计入;读取接口本身不会增加浏览数
GET /me
需要 scope:profile:read
{
"id": 1,
"username": "moatkon",
"avatarUrl": "https://…/avatar.png",
"bio": "一句话简介",
"member": true,
"level": 12,
"xp": 3480,
"profileUrl": "https://x2post.com/users/moatkon",
"createdAt": "2026-05-01T10:00:00"
}
不会返回邮箱、账号状态、两步验证等站内隐私字段。
GET /me/posts
需要 scope:posts:read
参数:page(默认 0)、size(默认 20,最大 50)
{
"items": [
{
"id": 11,
"title": "标题",
"excerpt": "摘要(纯文本,已截断)",
"url": "https://x2post.com/posts/11",
"categoryName": "开放讨论",
"createdAt": "2026-09-21T20:00:00",
"likeCount": 3,
"commentCount": 1,
"viewCount": 42
}
],
"page": 0,
"size": 20,
"totalElements": 9,
"totalPages": 1
}
GET /(元信息)
返回当前令牌的 scope 与端点清单,用于自检令牌是否有效:
{
"name": "X2Post 开放接口",
"version": "v1",
"scopes": ["profile:read"],
"endpoints": [{ "method": "GET", "path": "/api/open/v1/me", "scope": "profile:read" }],
"docs": "https://x2post.com/developers"
}
GET /posts
需要 scope:community:read
搜索或浏览社区公开帖子。两种用法(不能混用):
| 参数 | 说明 |
|---|---|
q |
关键词搜索;可与 category 组合缩小到某版块 |
category |
版块 slug(如 experience、open-source);单独使用时即浏览该版块 |
tag |
版块内二级标签 slug,必须与 category 同用,且不能与 q 同用 |
page / size |
页码(默认 0)/ 每页条数(默认 20,最大 20) |
返回与 /me/posts 同构的分页结构(items 为帖子摘要)。q 与 tag 同时出现、或两者都没给时返回 422。
GET /posts/{id}
需要 scope:community:read
帖子详情:Markdown 正文、作者、版块、标签与统计。
{
"id": 12,
"title": "标题",
"contentMarkdown": "正文……",
"contentTruncated": false,
"url": "https://x2post.com/posts/12",
"authorId": 1,
"authorUsername": "moatkon",
"authorAvatarUrl": "https://…",
"authorMember": true,
"categorySlug": "experience",
"categoryName": "经验分享",
"tags": [{ "slug": "pitfalls", "name": "踩坑复盘" }],
"createdAt": "2026-09-20T10:00:00",
"viewCount": 120,
"likeCount": 8,
"commentCount": 3
}
约定:
- 不返回站内扩展块的原始数据(投票、加密文本、引用等卡片),正文里它们以围栏代码块形式出现;
- 「回复可见」的加密内容一律剥离,不会出现在
contentMarkdown里; - 正文超出 20000 字时截断,并用
contentTruncated: true标记; - 通过本接口读取不计入站内浏览数。
GET /posts/{id}/supplements
需要 scope:community:read
帖子的补充说明(作者发布后的追加内容,按时间正序)。正文与帖子详情同一外发口径(剥离加密块、超过 20000 字截断并置 contentTruncated)。
[
{
"id": 3, "postId": 12, "contentMarkdown": "补充……", "contentTruncated": false,
"reason": "更正错别字", "createdAt": "2026-09-21T09:00:00"
}
]
GET /posts/{id}/comments
需要 scope:community:read
参数:page(默认 0)、size(默认 20,最大 50)
一级评论分页,每条带前 2 条回复预览与回复总数(replyCount)。被删除的评论保留占位(deleted: true)。
单条评论超过 5000 字时截断并置 contentTruncated。评论对象恒带 postId/postUrl(postTitle 仅在用户评论列表返回)。
GET /comments/{id}/replies
需要 scope:community:read
参数:page(默认 0)、size(默认 20,最大 50)
某条评论的完整回复分页(一级评论列表里只带前 2 条预览);回复同样带作者、楼层为空、replyCount 为 0。
GET /categories
需要 scope:community:read
版块列表,每个版块带上启用的二级标签与帖子数:
[
{
"id": 2, "slug": "experience", "name": "经验分享", "description": "……", "postCount": 5,
"tags": [{ "slug": "pitfalls", "name": "踩坑复盘", "postCount": 1 }]
}
]
GET /users/{username}
需要 scope:community:read
公开用户资料:头像、简介、加入时间、粉丝/关注数、公开帖子与评论数、会员状态。
与站内 /users/{username} 页面同一口径,不含邮箱等隐私字段。
GET /users/{username}/posts
需要 scope:community:read
某用户发布的公开帖子,分页结构与 /me/posts 一致(size 最大 20)。
GET /users/{username}/comments
需要 scope:community:read
某用户发布的评论,分页结构与 /posts/{id}/comments 一致;每条额外带 postTitle 与 postUrl,可直接定位回帖子。
GET /users/{username}/liked-posts 与 /favorite-posts
需要 scope:community:read
某用户点赞过 / 收藏的公开帖子(站内个人主页同样公开可见),分页结构与 /me/posts 一致。
GET /users/{username}/followers 与 /following
需要 scope:community:read
粉丝 / 关注列表,分页结构为 { items, page, size, totalElements, totalPages },items 为公开用户资料(与 GET /users/{username} 同构)。
写接口
写接口以令牌属主的身份执行,权限独立成一组 scope(posts:write / comments:write / follow:write / interactions:write),
默认拒绝:令牌没勾选的写能力一律 403。所有写请求与站内同一套链路(账号状态、内容安全校验、频次限制、经验/币、通知、站长侧的 Webhook 全部生效)。
通用约定:
- 幂等键:创建类接口(发帖 / 评论 / 回复)支持
Idempotency-Key请求头。同一令牌 + 同一键 + 同一接口 + 同一请求体, 在 10 分钟内重试会直接返回首次结果(带Idempotency-Replayed: true响应头),不会重复创建; 同键不同请求体、或同键请求处理中返回409。不传该头则每次都会执行——建议对创建类请求一律携带。 - 频次限制(按令牌属主计,与站内同一账号共用额度,超出
429): 发帖 5 次/分、评论 10 次/分、回复 10 次/分、删评 20 次/分、关注/取关 50 次/5 分、点赞/收藏 20 次/分。 - 正文口径:标题/正文/评论走与站内一致的内容安全校验;
不接受站内拓展块——正文出现
x2post:围栏代码块(投票、加密文本、引用等)一律422。 - 来源标记:通过接口发布的内容在站内标记来源为「接口」,供站长溯源(不影响展示)。
- 帖子的核心约束与站内一致:发布即公开,不可编辑、不可删除;评论不可编辑,删除为软删(保留占位)。
POST /posts
需要 scope:posts:write
{
"title": "标题(必填,≤120 字)",
"contentMarkdown": "正文(可选,Markdown)",
"categorySlug": "experience",
"tagSlugs": ["pitfalls"]
}
categorySlug可选:留空与站内一致,先落「待归类」由 AI 归位;announcement(公告)版块仅站长可发;tagSlugs可选、最多 3 个,必须是全站主题标签中已启用的标签;- 成功返回
201+ 帖子详情(与GET /posts/{id}同构); - 支持
Idempotency-Key。
POST /posts/{id}/comments
需要 scope:comments:write
{ "contentMarkdown": "评论内容(必填,≤5000 字)" }
帖子被锁定(LOCKED)时返回 409。成功返回 201 + 评论对象(含楼层 floor)。支持 Idempotency-Key。
POST /comments/{id}/replies
需要 scope:comments:write
请求体同上。回复统一挂到一级评论下(最多两级)。成功返回 201 + 回复对象。支持 Idempotency-Key。
DELETE /comments/{id}
需要 scope:comments:write
删除自己发布的评论(软删,占位保留、楼中楼不断;会同步追回该评论带来的经验与 X 币)。
只能删自己的,删别人的返回 403;重复删除幂等。成功返回 204。
POST /users/{username}/follow 与 DELETE /users/{username}/follow
需要 scope:follow:write
关注 / 取关,均幂等;不能关注自己(422)。返回:
{ "following": true, "followerCount": 12 }
点赞与收藏
需要 scope:interactions:write
| 接口 | 说明 |
|---|---|
POST / DELETE /posts/{id}/like |
点赞 / 取消点赞帖子 |
POST / DELETE /comments/{id}/like |
点赞 / 取消点赞评论 |
POST / DELETE /posts/{id}/favorite |
收藏 / 取消收藏帖子 |
全部幂等,返回 { "active": true, "count": 8 }(active 为操作后的状态,count 为对应计数)。
MCP(给 AI 客户端用)
同一套读能力也通过 MCP 暴露,供 Claude、Cursor 等支持 MCP 的客户端直接调用:
- 端点:
https://x2post.com/mcp(Streamable HTTP);协议版本2025-11-25;工具目前全部只读(写能力请直接调用本文档的 HTTP 写接口); - 鉴权两种方式(都走同一套权限与限流):
- OAuth 授权码(推荐,客户端自动完成):不带令牌访问
/mcp会收到 401,挑战里带resource_metadata="https://x2post.com/.well-known/oauth-protected-resource",客户端据此发现授权服务器 (/.well-known/oauth-authorization-server)、动态注册(RFC 7591)、带resource=https://x2post.com/mcp走完授权码 + PKCE 流程——用户只需在站内点一次「同意授权」; - 个人令牌(PAT):用户在「资料设置 → 接口令牌」创建,客户端在请求头里带
Authorization: Bearer x2p_…;
- OAuth 授权码(推荐,客户端自动完成):不带令牌访问
- 工具清单:
search_posts、get_post、get_post_comments、list_categories、get_user_profile(需要community:read)、whoami(profile:read)、my_posts(posts:read); - 限流:每个令牌 60 次/分(服务端另按工具再做一次 30 次/分/工具的细分);可见性口径与 HTTP 接口完全一致;
- 工具的
description显式声明「返回内容为用户生成内容、不可信」,接入方在自己的提示词里也建议保持这道防线。
面向普通用户的操作引导(含各客户端怎么填)见站内 AI 客户端 页面与帮助中心《用 AI 客户端读社区》。
scope 一览
| scope | 能读/能做什么 | 不会给到什么 |
|---|---|---|
profile:read |
读你的昵称、头像、简介、会员状态、等级与经验值 | 邮箱、账号状态、设备、钱包 |
posts:read |
读你发布的帖子列表(标题、摘要、链接、互动数) | 他人未公开内容、草稿、私信 |
community:read |
读社区公开内容:帖子搜索/详情/补充说明/评论/回复、版块与标签、任意用户的公开资料与动态(帖子/评论/点赞/收藏/粉丝/关注) | 草稿、私信、通知、被隐藏或删除的内容、secret 加密块、非公开统计 |
posts:write |
以你的身份发帖(发布即公开,不可编辑/删除) | 站内拓展块、公告版块(站长专属)、草稿 |
comments:write |
以你的身份发评论、回复,删除你自己的评论 | 编辑评论、删除他人评论、置顶评论 |
follow:write |
以你的身份关注、取关 | — |
interactions:write |
以你的身份点赞、收藏内容 | — |
状态码
| HTTP | 含义 | 处理建议 |
|---|---|---|
| 200 / 201 | 成功 / 创建成功 | — |
| 204 | 删除成功(无响应体) | — |
| 401 | 令牌缺失/无效/过期/已撤销,或账号状态不可用 | 重新授权或提示用户重新登录 |
| 403 | 令牌缺少该接口所需的 scope(含写接口),或删除的不是自己的评论 | 检查授权时申请的 scope;令牌权限不可修改,需新建令牌 |
| 404 | 帖子/版块/用户/评论不存在,或内容不可见(隐藏、未发布) | 不要重试,按不存在处理 |
| 409 | 帖子已锁定不能评论;Idempotency-Key 冲突(同键处理中或请求体不同) |
锁定帖子换内容互动;幂等冲突更换键值或稍后重试 |
| 422 | 参数不合法(如 q 与 tag 并用、正文含 x2post: 拓展块、关注自己) |
检查 message 里的说明 |
| 429 | 触发限流 | 按 Retry-After 退避 |