← 所有文章
教學

Telegram Bot API 429:以 retry_after 管理傳送佇列

Telegram Bot API 傳送請求因流量限制回傳 parameters.retry_after 時,應至少等待指定秒數,再重複請求。將截止時間持久保存,並協調使用同一機器人的所有 worker;只讓其中一個程序休眠,無法減慢其他程序。網路逾時則不同:沒有回應時,訊息可能已經送出,不能直接進入相同的自動重試分支。

重點摘要

  • 讀取結構化回應,不要從 description 擷取等待秒數。
  • 官方限制是排程條件,不是吞吐量保證。
  • 入站資料保存與出站傳送冷卻要分開。
  • 重新啟動後仍須遵守截止時間,並確認延遲回覆是否仍有必要。

Telegram 傳送限制的實際範圍

官方 Bots FAQ 建議單一聊天室不要超過每秒一則訊息;群組上限為每分鐘 20 則;未啟用付費廣播時,批次通知約為每秒 30 則。短暫突發也可能最終觸發 429。這些限制的範圍不同:把訊息分散到不同聊天室,不會消除機器人整體的廣播約束。

不能保證低於這些數值的每個請求都會成功。個別方法的規則及實際回應仍需考慮。本文處理一般傳送佇列,不涵蓋付費廣播設定,也不把所有 429 都歸為同一原因。

Bot API 參考文件 定義 ResponseParameters.retry_after 為選用整數:觸發流量控制後,再次請求前還需等待的秒數。它是 JSON 欄位,不是通用 HTTP 標頭契約。缺少此欄位時不能預設為零。

需要檢查結果時,請發出獨立 API 請求。Webhook 回應與獨立 sendMessage 的比較 說明了為何把方法嵌入 Telegram webhook 回應後,應用程式無法取得該方法的執行結果。

建立持久化傳送通道,而非休眠迴圈

以下是應用程式設計建議,不是 Telegram 提供的額外保證。

每個預期回覆都應先建立本地工作,保存機器人身分、目的地、已核准內容、過期策略、嘗試次數及最早執行時間。這些是自訂儲存欄位,不是 Bot API 參數。對業務意圖去重,避免入站更新重複投遞時建立第二份回覆。

設定三層排程條件:

  1. 機器人層:協調所有共用該機器人的程序總流量。
  2. 聊天室層:控制各目的地的節奏,包含群組限制。
  3. 工作層:檢查重試截止時間、過期、取消及執行租約。

以原子操作領取工作,避免兩個 worker 同時傳送。網路呼叫前再次檢查共用條件。收到限流回應後,原子延長冷卻時間,再將工作交回排程器;不要讓 webhook 請求保持開啟來等待。

retry_after 不會指出實際觸發哪一層限制。保守的初始策略是暫停整個機器人的傳送通道,等待回傳的時間。這可能延遲其他聊天室,但不會錯把回應當成僅限當前聊天室的證據。將它明確列為應用策略,再依觀測調整,不猜測伺服器內部演算法。

把 retry_after 轉為可保存的截止時間

以下 JavaScript 僅示範結果分類,不會傳送請求,也不包含資料庫或已執行的正式環境測試。receivedAtMs 是客戶端收到回應的時間;currentNotBeforeMs 是從儲存區讀取的既有共用截止時間。

function telegramFloodDecision(body, receivedAtMs, currentNotBeforeMs = 0) {
  if (body?.ok !== false || body.error_code !== 429) {
    return { action: 'classify_other_result' };
  }

  const seconds = body.parameters?.retry_after;
  if (!Number.isSafeInteger(seconds) || seconds < 0) {
    return { action: 'hold_for_review' };
  }

  const deadline = receivedAtMs + seconds * 1000;
  if (!Number.isSafeInteger(deadline)) {
    return { action: 'hold_for_review' };
  }

  return {
    action: 'reschedule',
    notBeforeMs: Math.max(currentNotBeforeMs, deadline)
  };
}

回傳名稱屬於本地程式。以交易保存最大截止時間;未受保護的先讀後寫,可能覆蓋另一個 worker 剛寫入的更長冷卻。同步各 worker 的時鐘,或採用共用排程時鐘。若加入隨機延遲,只能延後,不能縮短 Telegram 要求的等待。

工作到期代表可再次嘗試,不代表必定成功。設定嘗試次數與工作存續時間上限。後續回應要求更長等待時,更新截止時間;工作已過期或真人已回覆時,應取消或人工確認,避免傳送過時內容。這些上限與取消規則屬於業務策略,不是 Telegram 限制。

區分拒絕、未知結果與入站接收

觀測結果建議處理
有效限流回應且包含 retry_after保存冷卻,所有條件通過後才重試
429 沒有可用等待值暫停並分類,不忙迴圈,也不視為零等待
sendMessage 成功保存回傳的 Message,不要重排同一意圖
網路逾時或回應遺失標記未知,核對或人工確認後再決定
其他 API 錯誤分別檢查權限、目的地與請求內容

本地工作 ID 可以防止系統內重複排程,但不是遠端恰好執行一次的保證。成功傳送後、保存回應前當機,仍會留下未知結果。單靠 outbox 不能宣稱完全消除重複訊息。

入站健康是另一層邊界。驗證並持久保存更新後,即可獨立確認接收,不必等延遲中的傳送完成。若更新本身沒有收到,請使用 getWebhookInfo 診斷指南。pending_update_count 計算等待投遞的更新,不是出站佇列長度。

不要混用 UnifyPort 契約

UnifyPort 非官方介面連接訊息帳號,使用不同的文字訊息契約。錯誤封裝包含 error.code、error.numeric_code、error.message,並以 request_id 支援追蹤。錯誤參考 未定義 Bot API 的 parameters.retry_after,不可直接套用上述分類器。

跨通路 worker 可以共用工作儲存,但結果分類器必須依契約分開。保留 HTTP 狀態及結構化錯誤。請求追蹤指南 也指出,X-Request-Id 不是已記載的傳送冪等鍵。非官方介面不會消除上游限制,也不會讓未知結果變成可以放心重送。

傳送錯誤還要與接收器回傳的狀態分開。Webhook 投遞參考 說明 UnifyPort 立即進行投遞重試,不遵循 Retry-After。讓接收器回傳 429,不是安排出站冷卻的方式。

驗收檢查與常見問題

使用本地回應樣本測試,不要刻意製造 Telegram 流量洪峰。涵蓋兩個 worker 共用機器人、冷卻期間重啟、較長等待晚到、缺少 retry_after、等待中取消與傳送回應遺失。確認工作不會提早執行,未知結果另行處理。這些是建議檢查,不是已完成的測試。

retry_after 是秒還是毫秒?

秒。排程器若以毫秒保存時間,必須明確換算。

增加 worker 可以解決 429 嗎?

不能只靠增加數量。共用機器人也共用其限制;未協調的並行處理可能增加被拒絕的請求。

等待結束就保證成功嗎?

不保證。只滿足此次回應的等待條件;其他門檻、權限及後續 API 回應仍有效。

逾時也套用同樣的重試規則嗎?

不應如此。沒有回應的逾時不是已確認的限流拒絕,傳送結果可能未知。

下一步與參考資料

先替現有傳送器加入持久化冷卻並測試當機邊界,再提高並行度。訊息帳號客戶端應從 UnifyPort 錯誤參考 開始,不假定相容 Bot API。

官方資料核對日期:2026-10-18。

UnifyPort API

讓訊息接入變成一條穩定的產品管線。

先用統一 API 跑通傳送,再用標準事件把所有入站訊息接回業務系統。