Threads APIThreads 自動化自動發文工具Node.jsMindThread

Threads API 自動發文教學 2026:從申請 App、拿 Token 到排程發文(附 Node.js 程式碼)

· 20 分鐘閱讀
目錄
  1. 先搞懂 Threads API 的三個基本事實
  2. 第一步:在 Meta 開發者平台建 Threads App
  3. 權限(scope)要哪些
  4. 第二步:讓你的 Threads 帳號接受「測試者」邀請
  5. 第三步:OAuth 授權,拿 Token
  6. 3a. 把使用者導去授權頁
  7. 3b. 用 code 換短效 Token(1 小時)
  8. 3c. 立刻換成 60 天長效 Token
  9. 3d. 到期前續期
  10. 第四步:發文(兩段式)
  11. 圖片文
  12. 影片文:不能固定等幾秒,要輪詢
  13. 第五步:排程。API 沒有,要自己做
  14. 常見錯誤速查
  15. 不想自己架?

一句話先回答:Threads API 自動發文分五步:①在 Meta 開發者平台建一個 Threads 用途的 App;②讓你的 Threads 帳號接受「測試者」邀請;③走 OAuth 拿短效 Token,立刻換成 60 天長效 Token;④發文是兩段式,先 POST /{user-id}/threads 建立容器,再 POST /{user-id}/threads_publish 發布;⑤API 沒有排程功能,要自己用 cron 或排程工具在指定時間呼叫。每個帳號 24 小時最多 250 篇 API 發文、文字上限 500 字。

下面每一段程式碼都來自 MindThread 線上正在跑的版本,不是照文件抄的範例。MindThread 用這套 API 幫上百個 Threads 帳號自動發文,踩過的坑我會直接寫出來。

如果你不想寫程式、只想要「排程好就自動發」,可以直接跳到文末的「不想自己架」一節。這篇是給想自己接 API 的人。

先搞懂 Threads API 的三個基本事實

1. 它是 Meta 的官方 API,網域是 graph.threads.net。跟 Instagram Graph API 是兩套,App ID 和 App Secret 也是分開的。建 App 的時候會看到兩組 ID,Threads API 要用標的是 Threads 的那組。

2. 發文是「兩段式」。你不能一個請求就把文發出去,要先建立一個「媒體容器」(media container)拿到 creation_id,再用這個 id 去發布。文字文中間等幾秒就夠,影片要輪詢容器狀態等它處理完。

3. 限額是滾動 24 小時。官方數字:每個 Threads 帳號在 24 小時滾動區間內最多 250 篇 API 發文、1,000 則回覆。文字上限 500 字。這個限額是按帳號算,不是按 App 算。

第一步:在 Meta 開發者平台建 Threads App

  1. developers.facebook.com 建立新 App,用途選 Threads(Meta 官方叫 Threads Use Case)。
  2. 建好後到 App Dashboard,找 Threads 的設定頁,記下 Threads App IDThreads App Secret。再說一次:這是 Threads 專用的那組,不是 Facebook App 那組。
  3. 設定 Redirect URI(OAuth 回呼網址)。本機開發可以先填 https://localhost:3000/callback 之類的,正式環境要填真實網址,而且必須是 HTTPS。

權限(scope)要哪些

自動發文最少需要兩個:

  • threads_basic:讀取自己的基本資料與貼文
  • threads_content_publish:發文

這兩個是自動核發的,不用送 App Review。如果你還要自動回覆、讀留言、看洞察數據,才需要 threads_manage_repliesthreads_read_repliesthreads_manage_insights 等,這些要送審才能對一般使用者生效。

MindThread 的做法是把所有用到的 scope 集中寫在一個常數裡,並加註「這是 App Review 送審的唯一來源」。原因是曾經在程式裡多用了一個權限卻忘了加進授權清單,結果使用者授權完、新功能靜默回 401。

// 自動核發(不用送審):threads_basic, threads_content_publish
// 其餘都要送審
const SCOPES = 'threads_basic,threads_content_publish'

第二步:讓你的 Threads 帳號接受「測試者」邀請

這一步是最多人卡住的地方。App 還在開發模式時,只有被加為 Threads 測試者 的帳號能用 API。

  1. App Dashboard → App rolesRolesAdd People → 選 Threads Tester,填你的 Threads 帳號。
  2. 用那個 Threads 帳號登入 Threads(網頁或 App 都可以),到 帳號設定網站權限(Website permissions),會看到一個待接受的邀請,按接受。

沒做第 2 步,後面 OAuth 流程會一路看起來正常,到真正發文時才回「沒有權限」。

第三步:OAuth 授權,拿 Token

3a. 把使用者導去授權頁

https://www.threads.net/oauth/authorize
  ?client_id={THREADS_APP_ID}
  &redirect_uri={REDIRECT_URI}
  &scope=threads_basic,threads_content_publish
  &response_type=code
  &state={隨機字串,防 CSRF}

state 一定要帶,而且回來時要驗。MindThread 用 HMAC 簽過的字串,回呼時驗簽名與時效。我們曾把時效設成 10 分鐘,結果在授權頁猶豫太久的使用者回來就被當成攻擊擋掉,後來放寬到 45 分鐘。

3b. 用 code 換短效 Token(1 小時)

const res = await fetch('https://graph.threads.net/oauth/access_token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({
    client_id: THREADS_APP_ID,
    client_secret: THREADS_APP_SECRET,
    grant_type: 'authorization_code',
    redirect_uri: REDIRECT_URI,
    code,
  }),
})
const { access_token: shortToken, user_id } = await res.json()

3c. 立刻換成 60 天長效 Token

短效 Token 只活 1 小時,拿到就要換,別存短效的。

const params = new URLSearchParams({
  grant_type: 'th_exchange_token',
  client_secret: THREADS_APP_SECRET,
  access_token: shortToken,
})
const res = await fetch(`https://graph.threads.net/access_token?${params}`)
const { access_token: longToken, expires_in } = await res.json()
// expires_in 是秒數,約 60 天;把到期時間存起來,後面要續期

3d. 到期前續期

const params = new URLSearchParams({
  grant_type: 'th_refresh_token',
  access_token: currentLongToken,
})
const res = await fetch(`https://graph.threads.net/refresh_access_token?${params}`)
const { access_token: newToken, expires_in } = await res.json()

自動發文中斷最常見的原因就是忘了續期。MindThread 的做法是每天跑一支排程,把 7 天內要到期的 Token 全部續一輪,而且續期失敗要寫回資料庫標記,不然你會以為帳號還活著,其實它已經發不出文了。

第四步:發文(兩段式)

這是 MindThread 線上發文字文的函式,幾乎原樣貼上:

const THREADS_API = 'https://graph.threads.net/v1.0'

async function publishTextPost(threadsUserId: string, accessToken: string, text: string) {
  // 第一段:建立媒體容器(草稿)
  const createRes = await fetch(`${THREADS_API}/${threadsUserId}/threads`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      media_type: 'TEXT',
      text,
      access_token: accessToken,
    }),
  })
  if (!createRes.ok) {
    const err = await createRes.json().catch(() => ({}))
    throw new Error(`建立容器失敗 ${createRes.status}: ${err?.error?.message}`)
  }
  const { id: creationId } = await createRes.json()

  // 等幾秒讓伺服器把容器處理好(文字文 3 秒足夠)
  await new Promise(r => setTimeout(r, 3000))

  // 第二段:發布
  const publishRes = await fetch(`${THREADS_API}/${threadsUserId}/threads_publish`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      creation_id: creationId,
      access_token: accessToken,
    }),
  })
  if (!publishRes.ok) {
    const err = await publishRes.json().catch(() => ({}))
    throw new Error(`發布失敗 ${publishRes.status}: ${err?.error?.message}`)
  }
  return publishRes.json() // { id: 貼文 id }
}

幾個細節:

  • threadsUserId 是你在 3b 拿到的 user_id,也可以用 me 代替。
  • 兩個請求都用 application/x-www-form-urlencoded,不是 JSON。
  • 錯誤訊息在 error.message,一定要印出來,Meta 的錯誤碼本身沒什麼資訊量。

圖片文

media_type 改成 IMAGE,多帶 image_url(必須是公開可抓的 HTTPS 網址),其餘一樣,等 3 秒再發布就行。

body: new URLSearchParams({
  media_type: 'IMAGE',
  image_url: imageUrl,
  text,
  access_token: accessToken,
})

影片文:不能固定等幾秒,要輪詢

影片上傳後 Meta 要轉檔,時間不固定。固定等 3 秒會間歇性失敗,這是我們實際踩過的。正確做法是輪詢容器狀態直到 FINISHED

async function waitForContainerReady(creationId: string, accessToken: string) {
  const deadline = Date.now() + 120_000
  await new Promise(r => setTimeout(r, 5000))
  while (Date.now() < deadline) {
    const res = await fetch(
      `${THREADS_API}/${creationId}?fields=status,error_message&access_token=${encodeURIComponent(accessToken)}`
    )
    const { status, error_message } = await res.json()
    if (status === 'FINISHED') return
    if (status === 'ERROR' || status === 'EXPIRED') {
      throw new Error(`影片容器 ${status}: ${error_message || '無細節'}`)
    }
    await new Promise(r => setTimeout(r, 3000)) // IN_PROGRESS,繼續等
  }
  throw new Error('影片容器 120 秒內未完成')
}

容器狀態有五種:IN_PROGRESSFINISHEDERROREXPIREDPUBLISHED。建立容器時 media_typeVIDEO、參數帶 video_url

第五步:排程。API 沒有,要自己做

Threads API 沒有「預約在某個時間發」的功能,官方文件只提醒你「如果你的 App 允許使用者排程,請自己遵守發文限額」。所以排程是你的事:

做法 適合誰 要注意
系統 cron / systemd timer 自己的一兩個帳號 機器要一直開著;Token 續期也要排進去
Vercel Cron / Cloudflare Cron Triggers 已經在用 serverless 的人 函式有執行時間上限,影片輪詢要小心
n8n / Make 不想寫程式但能拉流程的人 一樣要自己處理去重與 Token
MindThread 這類現成工具 多帳號、要穩定 排程、去重、續期、失敗重試都做好了

不管用哪種,有三件事一定要做:

  1. 記錄已發過的內容。排程器重跑或重試時,沒有去重就會重複發文,這在 Threads 上很傷帳號。
  2. 自己數每日發文數,別等 API 回你限額錯誤。
  3. 失敗分兩類處理:網路逾時、5xx 這種暫時性錯誤可以退避重試;權限錯誤、Token 失效這種要停下來通知人,重試只會一直錯。

常見錯誤速查

症狀 多半是
授權流程正常,發文時回沒有權限 Threads 帳號沒接受測試者邀請
一小時後全部失敗 存了短效 Token,沒換長效
60 天左右突然失敗 長效 Token 沒續期
影片文間歇性失敗、文字文正常 用固定等待秒數,要改成輪詢容器狀態
圖片文失敗 image_url 不是公開 HTTPS,或圖片太大、格式不支援
一天發到一半開始全錯 撞到 250 篇滾動限額

不想自己架?

上面這套 MindThread 已經做成產品:接上 Threads 帳號、設好時段、把內容丟進去,排程、去重、Token 續期、失敗重試、多帳號管理都在裡面,而且有免費方案可以先用。懶人路線看這篇:脆自動發文與排程完整攻略;想比較市面工具看這篇:Threads 自動發文工具比較

如果你要的是「自動在別人貼文下留言、按讚」那種互動,那不是 Threads API 能做的(留言要送審的權限,而且 API 不提供瀏覽動態),請看 Threads 自動回覆與留言巡邏


本文程式碼來自 MindThread 線上版本,截至 2026 年 8 月有效。Meta 改 API 時我們會更新這篇;限額與權限名稱請以 Meta 官方 Threads API 文件 為準。

常見問題

Threads API 可以免費用嗎?

可以。Threads API 本身不收費,只要到 Meta 開發者平台建一個 Threads 用途的 App 就能開始。自己的帳號走「Threads 測試者」流程,不用送審就能發文。限制是每個帳號 24 小時內最多 250 篇 API 發文、1,000 則回覆。

Threads API 有排程發文功能嗎?

沒有。API 只提供「現在發」,排程要自己做:用 cron、n8n、Vercel Cron 之類的排程器在指定時間呼叫發文端點,並自己記錄已發過的內容避免重複。不想自己架的話,MindThread 這類工具已經把排程、去重、Token 續期做好了。

Threads 的 Access Token 多久過期?

OAuth 換到的短效 Token 只有 1 小時,要立刻用 th_exchange_token 換成 60 天的長效 Token,之後每隔不到 60 天用 th_refresh_token 續期一次。忘記續期是自動發文中斷最常見的原因。

為什麼我呼叫 Threads API 回傳沒有權限?

最常見是你的 Threads 帳號還沒接受測試者邀請。在 Meta App 後台加了測試者之後,必須用那個 Threads 帳號登入 Threads,到「帳號設定」裡的「網站權限」接受邀請,API 才會放行。

每週 AI 自動化實戰筆記

不廢話,只有能直接用的東西。Prompt 模板、自動化 SOP、技術拆解。

加入一人公司實驗室

免費資源包、每日建造日誌、可以對話的 AI Agent。一群用 AI 武裝自己的獨立開發者社群。

需要技術協助?

免費諮詢,24 小時內回覆。