← บทความทั้งหมด
บทช่วยสอน

Telegram Bot API 429: จัดคิวส่งด้วย retry_after

เมื่อคำขอส่งผ่าน Telegram Bot API ถูกปฏิเสธเพราะการควบคุมอัตราและมี parameters.retry_after ให้รออย่างน้อยตามจำนวนวินาทีที่ระบุก่อนส่งคำขอซ้ำ บันทึกเวลาที่เริ่มลองใหม่ได้ลงในที่เก็บถาวร และให้ worker ทุกตัวที่ใช้บอตเดียวกันใช้สถานะร่วมกัน การพัก worker เพียงตัวเดียวไม่ทำให้ตัวอื่นส่งช้าลง ส่วน network timeout ที่ไม่ได้รับคำตอบเป็นอีกกรณีหนึ่ง เพราะข้อความอาจถูกส่งแล้ว จึงไม่ควรเข้าทาง retry อัตโนมัติแบบเดียวกัน

ประเด็นสำคัญ

  • อ่านฟิลด์ที่มีโครงสร้าง ไม่ดึงตัวเลขจากข้อความ description
  • ใช้ข้อจำกัดทางการเป็นเงื่อนไขจัดคิว ไม่ใช่คำรับประกันปริมาณงาน
  • แยกการบันทึกข้อความขาเข้าออกจากการรอส่งขาออก
  • หลังรีสตาร์ตยังต้องรักษาเวลารอ และตรวจว่าคำตอบที่ล่าช้ายังจำเป็นหรือไม่

ข้อจำกัดการส่งของ Telegram ใช้กับขอบเขตใด

คู่มือ Bots FAQ ทางการ แนะนำให้หลีกเลี่ยงการส่งเกินหนึ่งข้อความต่อวินาทีในแชตเดียว จำกัดการส่งในกลุ่มที่ 20 ข้อความต่อนาที และระบุการแจ้งเตือนจำนวนมากไว้ประมาณ 30 ข้อความต่อวินาทีหากไม่ได้เปิด paid broadcasts การส่งเป็นช่วงสั้น ๆ แบบหนาแน่นก็อาจนำไปสู่ 429 ได้ ข้อจำกัดเหล่านี้มีคนละขอบเขต การกระจายข้อความไปหลายแชตไม่ได้ทำให้ข้อจำกัดรวมของบอตหายไป

อย่ารับประกันว่าทุกคำขอที่ต่ำกว่าตัวเลขเหล่านี้จะสำเร็จ กฎเฉพาะเมธอดและผลตอบกลับจริงยังสำคัญ บทความนี้ครอบคลุมคิวส่งทั่วไป ไม่ใช่การตั้งค่า paid broadcasts หรือทุกสาเหตุที่อาจทำให้เกิด 429

Bot API reference กำหนด ResponseParameters.retry_after เป็นจำนวนเต็มแบบ optional หมายถึงจำนวนวินาทีที่ต้องรอก่อนเรียกซ้ำเมื่อเกินการควบคุมอัตรา นี่คือฟิลด์ JSON ไม่ใช่สัญญา HTTP header แบบทั่วไป หากไม่มีฟิลด์นี้ อย่าแปลงเป็นการรอศูนย์วินาที

เมื่อ worker ต้องตรวจผลการส่ง ให้เรียก API แยกต่างหาก บทความ webhook response เทียบกับ sendMessage แยกคำขอ อธิบายว่าเหตุใดการฝังเมธอดในคำตอบ webhook ของ Telegram จึงไม่ให้แอปเข้าถึงผลของเมธอดนั้น

สร้างช่องทางส่งที่มีสถานะถาวร ไม่ใช่ลูป sleep

ส่วนนี้เป็นข้อเสนอด้านการออกแบบแอป ไม่ใช่คำรับประกันเพิ่มเติมจาก Telegram

ก่อนส่ง ให้สร้างงานภายในหนึ่งรายการต่อหนึ่งความตั้งใจที่จะตอบ เก็บตัวตนบอต ปลายทาง เนื้อหาที่อนุมัติแล้ว นโยบายหมดอายุ จำนวนครั้งที่ลอง และเวลาที่เริ่มทำงานได้ ฟิลด์เหล่านี้เป็นของแอป ไม่ใช่พารามิเตอร์ใหม่ของ Bot API ป้องกันงานซ้ำจากเหตุการณ์ขาเข้าที่ถูกส่งมาอีกครั้ง

ใช้เงื่อนไขจัดคิวสามระดับ:

  1. ระดับบอต: ควบคุมปริมาณรวมของทุกโปรเซสที่ใช้บอตเดียวกัน
  2. ระดับแชต: ควบคุมจังหวะของแต่ละปลายทาง รวมถึงข้อจำกัดกลุ่ม
  3. ระดับงาน: ตรวจเวลารอ การหมดอายุ การยกเลิก และ lease ของผู้ประมวลผล

รับสิทธิ์ทำงานแบบ atomic เพื่อไม่ให้สอง worker ส่งงานเดียวกัน ตรวจเงื่อนไขร่วมอีกครั้งก่อนเรียกเครือข่าย เมื่อได้รับผลจำกัดอัตรา ให้ขยายเวลารอแบบ atomic แล้วคืนงานให้ scheduler ไม่ควรเปิดคำขอ webhook ค้างไว้ระหว่างรอ

retry_after ไม่ได้บอกว่าข้อจำกัดระดับใดเป็นต้นเหตุ นโยบายเริ่มต้นที่ระมัดระวังคือพักช่องทางส่งของบอตนั้นทั้งชุดตามเวลาที่ได้รับ แม้จะทำให้แชตอื่นล่าช้า แต่ก็ไม่ด่วนสรุปว่าเป็นข้อจำกัดเฉพาะแชต ระบุให้ชัดว่าเป็นนโยบายของแอป แล้วปรับจากหลักฐานที่สังเกตได้ ไม่ใช่การเดากลไกภายในเซิร์ฟเวอร์

แปลง retry_after เป็นเวลาที่บันทึกได้

JavaScript ต่อไปนี้แสดงเฉพาะการจำแนกผล ไม่ได้ส่งคำขอหรือทำงานกับฐานข้อมูล และไม่ใช่ scheduler ที่ผ่านการทดสอบจริง 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)
  };
}

ชื่อค่าที่คืนเป็นชื่อภายในแอป บันทึกเวลาที่มากที่สุดด้วย transaction การอ่านแล้วเขียนโดยไม่มีการป้องกันอาจทับเวลารอที่ยาวกว่าซึ่ง worker อื่นเพิ่งบันทึก ซิงก์นาฬิกาของ worker หรือใช้นาฬิกาจัดคิวร่วมกัน หากเพิ่ม jitter ต้องเพิ่มเวลารอ ไม่ใช่ลดเวลาที่ Telegram ระบุ

งานที่ถึงเวลาเพียงมีสิทธิ์ลองอีกครั้ง ไม่ได้แปลว่าจะสำเร็จ กำหนดเพดานจำนวนครั้งและอายุงาน เมื่อคำตอบใหม่ขยายเวลารอให้บันทึกค่าใหม่ หากงานหมดอายุหรือเจ้าหน้าที่ตอบแล้ว ให้ยกเลิกหรือส่งให้ตรวจสอบแทนการส่งเนื้อหาเก่า เพดานเหล่านี้เป็นนโยบายธุรกิจ ไม่ใช่ข้อจำกัด Telegram

แยกการปฏิเสธ ผลที่ไม่ทราบ และการรับข้อมูล

ผลที่พบแนวทางของ worker
ผลจำกัดอัตราที่ถูกต้องพร้อม retry_afterบันทึกคูลดาวน์ แล้วลองเมื่อผ่านทุกเงื่อนไข
429 ที่ไม่มีเวลารอใช้ได้พักเพื่อจำแนก ไม่วนทันทีหรือถือว่าไม่ต้องรอ
sendMessage สำเร็จเก็บ Message ที่คืนมา ไม่จัดงานเดิมซ้ำ
เครือข่าย timeout หรือคำตอบสูญหายระบุผลไม่ทราบ แล้วตรวจสอบก่อนตัดสินใจส่งใหม่
ข้อผิดพลาด API อื่นตรวจสิทธิ์ ปลายทาง และข้อมูลคำขอแยกกัน

รหัสงานภายในช่วยลดการจัดงานซ้ำในระบบ ไม่ใช่การรับประกันว่าเซิร์ฟเวอร์ปลายทางจะทำเพียงครั้งเดียว หากโปรเซสล่มหลังส่งสำเร็จแต่ก่อนบันทึกคำตอบ ผลยังอาจไม่ทราบได้ จึงไม่ควรกล่าวว่า outbox เพียงอย่างเดียวกำจัดข้อความซ้ำทั้งหมด

สถานะขาเข้าก็เป็นคนละขอบเขต ตรวจสอบแหล่งที่มาและบันทึก update ให้ถาวร แล้วตอบรับโดยไม่รอการส่งขาออก หากตัว update ไม่เข้ามา ให้ใช้ คู่มือวิเคราะห์ getWebhookInfo ค่า pending_update_count นับ update ที่รอการนำส่ง ไม่ใช่ความยาวคิวขาออกของแอป

แยกสัญญาของ UnifyPort ออกจาก Bot API

Unofficial interface ของ UnifyPort ใช้บัญชีรับส่งข้อความที่เชื่อมต่ออยู่และมีสัญญาส่งข้อความของตนเอง ข้อผิดพลาดมี error.code, error.numeric_code, error.message และ request_id สำหรับติดตาม เอกสารข้อผิดพลาด ไม่ได้กำหนด parameters.retry_after ของ Bot API จึงไม่ควรคัดลอกตัวจำแนกด้านบนไปใช้โดยไม่เปลี่ยนแปลง

worker หลายช่องทาง เช่น Telegram และ LINE อาจใช้ที่เก็บงานร่วมกัน แต่ต้องจำแนกผลตามสัญญาแต่ละ API เก็บ HTTP status และข้อผิดพลาดแบบมีโครงสร้างไว้ คู่มือ request tracing อธิบายว่า X-Request-Id ไม่ใช่คีย์ idempotency สำหรับการส่งที่มีการรับประกันไว้ การใช้ unofficial interface ไม่ได้ลบข้อจำกัดต้นทาง และไม่ได้ทำให้การส่งที่ไม่ทราบผลปลอดภัยต่อการทำซ้ำ

แยกข้อผิดพลาดในการส่งออกจากสถานะที่ receiver ของคุณตอบด้วย เอกสาร webhook delivery ระบุว่า UnifyPort retry การนำส่งทันทีและไม่ใช้ Retry-After การให้ receiver ตอบ 429 จึงไม่ใช่วิธีตั้งเวลาคูลดาวน์ของการส่งขาออก

รายการตรวจรับและคำถามที่พบบ่อย

ใช้ response fixture ภายในแทนการจงใจส่งถี่ใส่ Telegram ทดสอบสอง worker ที่ใช้บอตเดียวกัน การรีสตาร์ตระหว่างรอ เวลารอที่ยาวกว่ามาทีหลัง ฟิลด์ retry_after หาย การยกเลิกระหว่างรอ และคำตอบการส่งสูญหาย ตรวจว่าไม่มีงานส่งก่อนเวลาที่บันทึกไว้และผลไม่ทราบยังเป็นสถานะแยก รายการนี้เป็นข้อเสนอทดสอบ ไม่ใช่ผลทดสอบที่ทำแล้ว

retry_after มีหน่วยเป็นวินาทีหรือมิลลิวินาที?

วินาที ต้องแปลงอย่างชัดเจนหาก scheduler เก็บเวลาเป็นมิลลิวินาที

เพิ่ม worker แล้วจะแก้ 429 ได้หรือไม่?

เพิ่มจำนวนอย่างเดียวไม่ได้ ทุกตัวที่ใช้บอตเดียวกันยังมีข้อจำกัดร่วม การทำงานพร้อมกันโดยไม่ประสานอาจเพิ่มคำขอที่ถูกปฏิเสธ

รอครบแล้วรับประกันว่าจะส่งได้หรือไม่?

ไม่รับประกัน เพียงผ่านเงื่อนไขรอของคำตอบครั้งนั้น เงื่อนไขอื่น สิทธิ์ และผล API ครั้งต่อไปยังมีผล

timeout ควรใช้กฎ retry เดียวกันหรือไม่?

ไม่ควร timeout ที่ไม่มีคำตอบไม่ใช่หลักฐานยืนยันว่าถูกจำกัดอัตรา และอาจไม่ทราบว่าการส่งเกิดขึ้นแล้วหรือยัง

ขั้นตอนถัดไปและแหล่งข้อมูล

เพิ่มคูลดาวน์แบบถาวรให้ตัวส่งเดิม และตรวจจุดเสี่ยงเมื่อโปรเซสล่มก่อนเพิ่ม concurrency สำหรับไคลเอนต์บัญชีที่เชื่อมต่อ ให้เริ่มจาก เอกสารข้อผิดพลาด UnifyPort ไม่ใช่สมมติว่าเข้ากันได้กับ Bot API

ตรวจสอบแหล่งข้อมูลทางการเมื่อ 2026-10-18:

UnifyPort API

เปลี่ยนการเชื่อมต่อข้อความให้เป็น pipeline ผลิตภัณฑ์ที่เสถียร

เริ่มจากการส่งผ่าน API เดียว แล้วส่งข้อความขาเข้าทั้งหมดกลับสู่ระบบธุรกิจของคุณด้วย event มาตรฐาน