如何把 Markdown 发布为 X Article
很多内容团队并不直接在 X 里写长文。文章源文件可能在 GitHub、Notion 导出的 Markdown、CMS 草稿库,或者内部知识库里。
难点不是读取 .md 文件,而是如何稳定地把它发布为 X Article:不要反复复制粘贴,不要重新上传图片,也不要因为某一步失败就从头再来。
TwexAPI Article endpoints 适合把发布当成一个有状态流程来做。它把任务拆成五个可重试步骤:创建草稿并拿到 article_id,可选设置封面,设置标题和 Markdown 正文,预览确认,最后发布草稿并拿到 tweet_id。
使用场景:从内容仓库发布 X Article
假设你有一个内容发布流水线:
- 编辑在仓库里维护
article.md - CI、后台任务或运营工具读取 Markdown 文件
- 系统把正文、标题和封面同步到 X Article
- 发布成功后,把返回的
tweet_id保存回数据库或 CMS
这类流程的关键不是“能不能发出去”,而是能不能在出错时安全恢复。例如封面上传失败时,不应该丢掉整个文章;正文格式不对时,应该能重新设置内容;发布前,还应该可以打开 X 的预览链接检查草稿。
因此,这里先讲分步流程。一键发布接口适合简单任务,但分步流程更适合需要预览、重试和问题定位的内容系统。
API 流程概览
TwexAPI 的五步流程如下:
| Step | Method | Endpoint | 作用 |
|---|---|---|---|
| 1 | POST | /x/articles/draft | 创建空草稿,返回 article_id 和 preview_url |
| 2 | PUT | /x/articles/{article_id}/cover | 可选:上传并设置文章封面 |
| 3 | PUT | /x/articles/{article_id}/title | 设置文章标题,标题是纯文本 |
| 4 | PUT | /x/articles/{article_id}/content | 设置文章正文,正文使用 Markdown |
| 5 | POST | /x/articles/{article_id}/publish | 发布草稿,返回 article_id 和 tweet_id |
所有请求都需要 TwexAPI Bearer Token。请求体里还需要传入 X 账号的 cookie,通常包含 auth_token、ct0 和 twid。
export TWEXAPI_TOKEN="your_twexapi_bearer_token"
export X_COOKIE="auth_token=...; ct0=...; twid=..."不要把
X_COOKIE写进代码仓库、日志或前端页面。它应该只存在于服务端环境变量或安全密钥管理系统里;如果泄露,应该立即轮换。
Step 1:创建 X Article 草稿
先创建一个空草稿。成功后,响应里的 data.article_id 会用于后续所有步骤,data.preview_url 可以用于人工预览。
curl --request POST \
--url https://api.twexapi.io/x/articles/draft \
--header "Authorization: Bearer $TWEXAPI_TOKEN" \
--header "Content-Type: application/json" \
--data '{
"cookie": "auth_token=...; ct0=...; twid=..."
}'成功响应会包含:
{
"code": 200,
"msg": "success",
"data": {
"article_id": "article-draft-id",
"preview_url": "https://x.com/compose/articles/edit/article-draft-id"
}
}在自动化系统里,建议立刻保存 article_id。如果后续设置标题或正文失败,你可以用同一个 article_id 重试,而不是重新创建一篇草稿。
Step 2:设置封面图片(可选)
如果你的 Markdown 文章有独立封面,可以在发布前设置。cover_image 支持 https:// 图片 URL,也可以按 API 文档传入本地文件路径。
curl --request PUT \
--url https://api.twexapi.io/x/articles/article-draft-id/cover \
--header "Authorization: Bearer $TWEXAPI_TOKEN" \
--header "Content-Type: application/json" \
--data '{
"cookie": "auth_token=...; ct0=...; twid=...",
"cover_image": "https://example.com/cover.png"
}'成功后会返回上传到 X 的 media_id:
{
"code": 200,
"msg": "success",
"data": {
"media_id": "1234567890"
}
}如果你没有封面,可以直接跳过这一步。
Step 3:设置文章标题
标题是纯文本,不是 Markdown。通常可以从 Markdown 文件的 frontmatter、第一行 # Heading,或 CMS 字段中提取。
curl --request PUT \
--url https://api.twexapi.io/x/articles/article-draft-id/title \
--header "Authorization: Bearer $TWEXAPI_TOKEN" \
--header "Content-Type: application/json" \
--data '{
"cookie": "auth_token=...; ct0=...; twid=...",
"title": "How We Built a Weekly AI Research Digest"
}'这一阶段成功时通常只返回 code 和 msg,data 为 null。
Step 4:把 Markdown 正文写入 Article
正文通过 markdown 字段传入。根据 Article Content API,Markdown 支持标题、粗体、斜体、列表、代码块和内联图片,例如:
# How We Built a Weekly AI Research Digest
Every Friday, our crawler collects public X posts from selected AI builders.
## Workflow
- Collect tweets from a curated account list
- Summarize recurring themes
- Publish the digest as an X Article
内联图片会被自动上传到 X 并嵌入文章。
curl --request PUT \
--url https://api.twexapi.io/x/articles/article-draft-id/content \
--header "Authorization: Bearer $TWEXAPI_TOKEN" \
--header "Content-Type: application/json" \
--data '{
"cookie": "auth_token=...; ct0=...; twid=...",
"markdown": "# How We Built a Weekly AI Research Digest\n\nEvery Friday..."
}'成功响应会返回解析出的内容块数量:
{
"code": 200,
"msg": "success",
"data": {
"blocks": 8
}
}如果 blocks 明显少于预期,通常说明 Markdown 结构或图片 URL 需要检查。此时不要急着发布,先重试 Step 4。
Step 5:发布草稿
标题和正文都设置完成后,再调用发布接口。visibility 可选,默认是 Public,也可以使用 Followers 或 Mentioned。
curl --request POST \
--url https://api.twexapi.io/x/articles/article-draft-id/publish \
--header "Authorization: Bearer $TWEXAPI_TOKEN" \
--header "Content-Type: application/json" \
--data '{
"cookie": "auth_token=...; ct0=...; twid=...",
"visibility": "Public"
}'发布成功后会返回:
{
"code": 200,
"msg": "success",
"data": {
"article_id": "article-draft-id",
"tweet_id": "1880000000000000000"
}
}tweet_id 就是这篇 X Article 对应的 X post ID,可以存回你的内容系统,用于后续展示、分析、评论监控或二次分发。
发布前请确认 Step 3 和 Step 4 已完成。API 允许发布没有标题或正文的草稿,但这样的 Article 在 X 上会显示为空内容。
一个可运行的 Node.js 脚本
下面的脚本会读取本地 Markdown 文件,依次创建草稿、设置封面、设置标题和正文。默认情况下它会停在预览步骤;只有显式设置 PUBLISH_ARTICLE=true,才会真正发布。这样本地试跑或 CI 任务不会误发未确认的内容。
1import { readFile } from "node:fs/promises";
2
3const API_BASE = "https://api.twexapi.io";
4const token = process.env.TWEXAPI_TOKEN;
5const cookie = process.env.X_COOKIE;
6const markdownPath = process.argv[2] || "article.md";
7
8if (!token || !cookie) {
9 throw new Error("Missing TWEXAPI_TOKEN or X_COOKIE environment variable.");
10}
11
12async function request(path, method, body) {
13 const response = await fetch(`${API_BASE}${path}`, {
14 method,
15 headers: {
16 Authorization: `Bearer ${token}`,
17 "Content-Type": "application/json",
18 },
19 body: JSON.stringify(body),
20 });
21
22 const data = await response.json();
23
24 if (!response.ok || data.code !== 200) {
25 throw new Error(`${method} ${path} failed: ${JSON.stringify(data)}`);
26 }
27
28 return data.data;
29}
30
31function inferTitle(markdown) {
32 const h1 = markdown.match(/^#\s+(.+)$/m);
33 return h1 ? h1[1].trim() : "Untitled X Article";
34}
35
36const markdown = await readFile(markdownPath, "utf8");
37const title = process.env.ARTICLE_TITLE || inferTitle(markdown);
38const coverImage = process.env.COVER_IMAGE;
39const visibility = process.env.ARTICLE_VISIBILITY || "Public";
40const shouldPublish = process.env.PUBLISH_ARTICLE === "true";
41
42const draft = await request("/x/articles/draft", "POST", { cookie });
43console.log("Draft created:", draft.article_id);
44console.log("Preview URL:", draft.preview_url);
45
46if (coverImage) {
47 const cover = await request(`/x/articles/${draft.article_id}/cover`, "PUT", {
48 cookie,
49 cover_image: coverImage,
50 });
51 console.log("Cover uploaded:", cover.media_id);
52}
53
54await request(`/x/articles/${draft.article_id}/title`, "PUT", {
55 cookie,
56 title,
57});
58console.log("Title set:", title);
59
60const content = await request(`/x/articles/${draft.article_id}/content`, "PUT", {
61 cookie,
62 markdown,
63});
64console.log("Markdown parsed into blocks:", content.blocks);
65
66if (!shouldPublish) {
67 console.log("Draft ready for review. Set PUBLISH_ARTICLE=true after checking the preview URL.");
68 process.exit(0);
69}
70
71const published = await request(`/x/articles/${draft.article_id}/publish`, "POST", {
72 cookie,
73 visibility,
74});
75
76console.log("Article published:", published);运行方式:
TWEXAPI_TOKEN="your_token" \
X_COOKIE="auth_token=...; ct0=...; twid=..." \
COVER_IMAGE="https://example.com/cover.png" \
ARTICLE_VISIBILITY="Public" \
node publish-x-article.mjs ./article.md如果你不需要封面,去掉 COVER_IMAGE 即可。预览确认后,再加上 PUBLISH_ARTICLE=true 运行发布。
生产环境注意事项
把 Markdown 发布到 X Article 时,建议把每一步都当成一个可恢复任务:
- 保存
article_id:后续步骤失败时可以继续更新同一篇草稿。 - 保存
preview_url:发布前可交给运营或编辑做人工检查。 - 不要记录完整 cookie:日志里最多记录 cookie 是否存在,不要打印具体值。
- Step 4 后检查
blocks:如果解析出的块数异常,先暂停发布。 - 把发布动作做成显式确认:只有审批状态或确认开关满足条件时,才执行 Step 5。
- 发布后保存
tweet_id:后续可以用它做评论监控、表现分析或内容归档。
什么时候用 one-step publish?
如果你只需要快速把一篇 Markdown 发布出去,也可以使用 POST /x/articles/publish。这个接口一次性传入 cookie、title、markdown,可选传入 cover_image 和 visibility。
但对于内容平台、自动化流水线、需要预览或需要失败重试的场景,五步流程更稳。它让你能清楚知道失败发生在哪一步,也能在不重复创建文章的情况下修复封面、标题或正文,并把最终发布放到人工确认之后。
总结
把 Markdown 发布到 X Article 的核心流程很清晰:创建草稿,拿到 article_id;可选设置封面;设置纯文本标题;把 .md 文件内容作为 markdown 写入正文;预览确认;最后发布并保存 tweet_id。
这套流程适合把 X Article 接入现有内容系统。编辑继续用 Markdown 写作,开发者负责把它变成一条稳定、可追踪、可重试的发布链路。