← 所有文章
教學

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 不是文件所保證的發送冪等鍵。非官方介面不會消除上游限制,也不會令未知結果變成可以安全重送。

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

驗收檢查與常見問題

使用本地回應樣本測試,不要刻意製造 Telegram 流量洪峰。覆蓋兩個 worker 共用機械人、冷卻期間重啟、較長等待較遲抵達、缺少 retry_after、等待中取消及發送回應遺失。確認工作不會早於截止時間執行,未知結果另有分支。這些是建議檢查,不是已完成測試。

retry_after 用秒還是毫秒?

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

增加 worker 能解決 429 嗎?

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

等候完就保證發送成功嗎?

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

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

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

下一步與參考資料

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

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

UnifyPort API

令訊息接入變成一條穩定嘅產品管線。

先用統一嘅 API 跑通發送,再用標準事件將所有入站訊息接返去業務系統。