Markdown を X Article として公開する方法
多くのコンテンツチームは、X の画面で直接長文を書いていません。原稿は GitHub、Notion から書き出した Markdown、CMS の下書き、社内ナレッジベースなどにあります。
難しいのは .md を読むことではありません。その Markdown を X Article として安定して公開し、画像の再アップロードや手作業の貼り付けを減らし、途中で失敗しても同じドラフトから復旧できるようにすることです。
TwexAPI Article endpoints は、公開を状態のあるワークフローとして扱う場合に向いています。ドラフト作成で article_id を取得し、必要ならカバーを設定し、タイトルと Markdown 本文を書き込み、プレビュー確認後に公開して tweet_id を受け取る、という 5 つの再試行しやすいステップに分けられます。
使用例:コンテンツリポジトリから公開する
次のような公開パイプラインを想定します:
- 編集者がリポジトリで
article.mdを管理する - CI、バックグラウンドジョブ、運用ツールが Markdown を読み込む
- システムが本文・タイトル・カバーを X Article に同期する
- 公開後、返却された
tweet_idを DB や CMS に保存する
重要なのは「出せるか」だけでなく「失敗時に安全に復旧できるか」です。例えばカバーのアップロードに失敗しても記事全体を捨てないこと、本文の形式が悪ければ内容を再設定できること、公開前に X のプレビュー URL でドラフトを確認できること、です。
そのため、ここではまずステップごとのフローを使います。一括公開 API は短い作業には便利ですが、プレビュー、再試行、原因切り分けが必要な運用では分割された流れの方が扱いやすくなります。
API フロー概要
TwexAPI の 5 ステップは次のとおりです:
| 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 ではなくプレーンテキストです。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 では、見出し・太字・斜体・リスト・コードブロック・インライン画像などが使えます。例:
# 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:ドラフトを公開
タイトルと本文の設定が終わってから公開 API を呼びます。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 投稿 ID です。表示・分析・コメント監視・再配信のためにコンテンツシステムに保存してください。
公開前に Step 3 と Step 4 が完了していることを確認してください。API はタイトルや本文のないドラフトも公開できますが、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 全文をログに出さない:ログには存在有無程度に留め、値は出さない
- Step 4 後に
blocksを確認する:ブロック数が異常なら公開を止める - 公開を明示操作にする:承認状態または確認フラグがある場合だけ Step 5 を実行する
- 公開後に
tweet_idを保存する:コメント監視・パフォーマンス分析・アーカイブに使える
one-step publish を使うタイミング
Markdown を素早く 1 本だけ公開したい場合は POST /x/articles/publish も使えます。cookie、title、markdown と、任意の cover_image、visibility を一度に送ります。
コンテンツプラットフォーム、自動化パイプライン、プレビューが必要な場合、失敗時のリトライが必要な場合は、5 ステップの方が安定します。どのステップで失敗したか分かり、記事を作り直さずにカバー・タイトル・本文だけ直せます。最終公開も、人手確認の後に切り分けられます。
まとめ
Markdown を X Article に公開する流れは明確です:ドラフトを作成して article_id を取得し、必要ならカバーを設定し、プレーンテキストのタイトルを設定し、.md の内容を markdown として本文に書き込み、プレビュー確認後に公開して tweet_id を保存する。
このフローは既存のコンテンツシステムに X Article を組み込むのに向いています。編集者は Markdown で書き続け、開発者はそれを安定・追跡可能・リトライ可能な公開パイプラインにします。