กลับไปที่บล็อก

การแก้ไขปัญหาคู่มือ

429 Too Many Requests จาก AI API: การลองใหม่ด้วย backoff

429 จาก AI API หมายความว่าอะไรและจัดการอย่างไร: exponential backoff พร้อม jitter, Retry-After และ concurrency ของเอเจนต์ พร้อมโค้ด Python และ JavaScript

โดย CheapTokens Teamเผยแพร่เมื่อ อัปเดตเมื่อ ใช้เวลาอ่าน 6 นาที

แผนภาพลูปคำขอ: การตอบกลับ 429 ขั้นตอนรอด้วยความหน่วงที่เพิ่มขึ้น และการลองใหม่
ในหน้านี้
  1. 429 หมายความว่าอะไร
  2. อ่านเฮดเดอร์ก่อนเดาระยะเวลารอ
  3. Python: backoff พร้อม jitter
  4. JavaScript: ลูปเดียวกัน
  5. เอเจนต์ควรจัดการ 429 อย่างไร
  6. รันลูปเอเจนต์บน CheapTokens
  7. หากยังล้มเหลว

429 Too Many Requests จาก AI API หมายความว่าคำขอถูกปฏิเสธเพราะคุณส่งมากเกินไปเร็วเกินไป หรือชนขีดจำกัดในบัญชีของคุณ วิธีแก้คือรอและลองใหม่ด้วย exponential backoff และ jitter ใช้เฮดเดอร์ Retry-After เมื่อการตอบกลับมีเฮดเดอร์นี้ และจำกัดจำนวนคำขอที่เอเจนต์ของคุณส่งพร้อมกัน อย่าลองใหม่กับ 429 ที่จริง ๆ แล้วเป็นขีดจำกัดการใช้จ่ายหรือยอดคงเหลือ: การรอไม่ช่วยแก้

คู่มือนี้อ้างอิงจากคู่มือ rate limit อย่างเป็นทางการของ OpenAI และ Anthropic ซึ่งตรวจสอบเมื่อ 22 ตุลาคม 2026 ทั้งสองหน้าไม่แสดงวันที่อัปเดตล่าสุด

429 หมายความว่าอะไร#

OpenAI อธิบาย 429 ว่าคำขอถูกปฏิเสธเพราะเกิน rate limit (rate_limit_error โดยมีโค้ด slow_down เมื่อทราฟฟิกพุ่งขึ้นเร็วเกินไป) หรือเพราะถึงขีดจำกัดการใช้จ่ายแบบตายตัว Anthropic ใช้ rate_limit_error สำหรับ 429 เช่นกัน 429 จากเพดานการใช้จ่ายไม่มีเฮดเดอร์ retry-after และจะยังล้มเหลวต่อไปจนกว่าการเข้าถึงจะกลับมา

มีสองกรณีใกล้เคียงที่สับสนได้ง่าย OpenAI's 503 server_is_overloaded และ Anthropic's 529 overloaded_error เป็นสภาวะโอเวอร์โหลด ไม่ใช่ rate limit ของคุณ โดยปกติจัดการด้วยลูป retry เดียวกัน แต่บ่งบอกสาเหตุที่ต่างออกไป

ดังนั้นมีสามกรณี และแต่ละกรณีต้องตอบสนองต่างกัน:

การตอบกลับ สาเหตุ สิ่งที่ต้องทำ
429 rate limit คำขอหรือโทเคนต่อนาทีมากเกินไป รอ ลองใหม่ด้วย backoff
429 ขีดจำกัดการใช้จ่ายหรือยอดคงเหลือ ถึงขีดจำกัดบัญชี อย่าลองใหม่; แก้ขีดจำกัดหรือเติมยอดคงเหลือ
503 / 529 overloaded ฝั่งผู้ให้บริการไม่ว่าง ลองใหม่ด้วย backoff พร้อมเพดาน

หากยอดคงเหลือของคุณหมด อ่าน ข้อผิดพลาด "not enough tokens" แทน สำหรับความล้มเหลวระดับเครือข่าย เช่น timeout และการปฏิเสธการเชื่อมต่อ ดู ข้อผิดพลาดการเชื่อมต่อทั่วไป

อ่านเฮดเดอร์ก่อนเดาระยะเวลารอ#

OpenAI ระบุเฮดเดอร์เหล่านี้บนการตอบกลับที่ถูกจำกัดอัตรา: Retry-After และ x-ratelimit-limit-requests, x-ratelimit-remaining-requests, x-ratelimit-reset-requests พร้อมตัวแปร -tokens API อื่นอาจส่งเพียงบางส่วนหรือไม่ส่งเลย ดังนั้นเขียนไคลเอนต์ให้ใช้ Retry-After เมื่อมี และถอยกลับไปใช้กำหนดการของคุณเองเมื่อไม่มี

คำแนะนำของ OpenAI เกี่ยวกับการลองใหม่ สรุปสั้น ๆ:

  • ถือว่า Retry-After เป็นเวลารอ ขั้นต่ำ และเพิ่มความหน่วงแบบสุ่มเล็กน้อย
  • หากไม่มี ให้ใช้ exponential backoff พร้อม jitter
  • จำกัดทั้งจำนวนครั้งและเวลารอรวม
  • อย่าซ้อน retry ของคุณเองบน retry ของ SDK
  • อย่าลองใหม่กับข้อผิดพลาดด้านโควตาหรือการเรียกเก็บเงิน
  • คำขอที่ล้มเหลวยังนับรวมในขีดจำกัดต่อนาที ดังนั้นลูปที่ดุดันจะทำให้แย่ลง
  • สำหรับ streaming อย่าเล่นคำขอซ้ำหลังจากที่คุณบริโภคเอาต์พุตไปแล้ว

Python: backoff พร้อม jitter#

แพ็กเกจ openai อย่างเป็นทางการใช้ได้กับ endpoint ที่เข้ากันได้กับ OpenAI ปิด retry ของ SDK เอง (max_retries=0) เพื่อไม่ให้คูณกับของคุณ ไอดีของโมเดลมาจาก เอกสาร CheapTokens; ไอดีแตกต่างกันระหว่างผู้ให้บริการ

python
import os
import random
import time

from openai import OpenAI, APIStatusError

client = OpenAI(
    base_url="https://api.cheaptokens.dev/v1",
    api_key=os.environ["CHEAPTOKENS_API_KEY"],
    max_retries=0,  # we retry ourselves
)

RETRYABLE = {429, 500, 502, 503, 504}


def chat_with_retry(model, messages, max_attempts=5, max_total=60.0):
    started = time.monotonic()
    for attempt in range(max_attempts):
        try:
            return client.chat.completions.create(model=model, messages=messages)
        except APIStatusError as err:
            last = attempt == max_attempts - 1
            if err.status_code not in RETRYABLE or last:
                raise
            header = err.response.headers.get("retry-after")
            if header and header.replace(".", "", 1).isdigit():
                delay = float(header) + random.uniform(0, 1)  # minimum wait + jitter
            else:
                delay = random.uniform(0, min(30.0, 2.0 ** attempt))  # full jitter
            if time.monotonic() - started + delay > max_total:
                raise
            time.sleep(delay)

ขอบเขตบนของความหน่วงเมื่อไม่มีเฮดเดอร์จะเพิ่มเป็น 1, 2, 4, 8 วินาที (สูงสุด 30) และ random.uniform เลือกค่าสุ่มที่ต่ำกว่านั้น เพื่อให้เวิร์กเกอร์จำนวนมากไม่ตื่นพร้อมกันทั้งหมด Retry-After อาจเป็นวันที่แบบ HTTP ได้เช่นกัน โค้ดข้างต้นจัดการรูปแบบตัวเลข และมิฉะนั้นจะถอยกลับไปใช้ backoff

JavaScript: ลูปเดียวกัน#

javascript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.cheaptokens.dev/v1",
  apiKey: process.env.CHEAPTOKENS_API_KEY,
  maxRetries: 0, // we retry ourselves
});

const RETRYABLE = new Set([429, 500, 502, 503, 504]);
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

export async function chatWithRetry(model, messages, maxAttempts = 5, maxTotalMs = 60000) {
  const started = Date.now();
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
    try {
      return await client.chat.completions.create({ model, messages });
    } catch (err) {
      const last = attempt === maxAttempts - 1;
      if (!RETRYABLE.has(err.status) || last) throw err;
      const header = Number(err.headers?.get?.("retry-after"));
      const delayMs = Number.isFinite(header) && header > 0
        ? header * 1000 + Math.random() * 1000 // minimum wait + jitter
        : Math.random() * Math.min(30000, 2 ** attempt * 1000); // full jitter
      if (Date.now() - started + delayMs > maxTotalMs) throw err;
      await sleep(delayMs);
    }
  }
}

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

เอเจนต์ควรจัดการ 429 อย่างไร#

ลูปเอเจนต์ส่งคำขอสั้น ๆ จำนวนมาก และเอเจนต์หรือการเรียกเครื่องมือหลายตัวอาจทำงานพร้อมกัน การลองใหม่เพียงอย่างเดียวไม่พอ; ลดอัตราคำขอที่ต้นทาง:

  1. จำกัด concurrency เซมาฟอร์ที่ 3–5 การเรียกพร้อมกันเป็นจุดเริ่มต้นที่สมเหตุสมผล; เพิ่มขึ้นเฉพาะเมื่อข้อผิดพลาดยังเกิดน้อย
  2. ใช้ limiter ตัวเดียวร่วมกัน หากเวิร์กเกอร์หลายตัวใช้คีย์เดียวกัน พวกมันใช้ขีดจำกัดเดียวกัน ดังนั้นลูป retry หนึ่งต่อเวิร์กเกอร์ไม่พอ: ใช้คิวเดียวหรือเซมาฟอร์เดียวสำหรับโปรเซส
  3. ถอยกลับทั้งระบบ เมื่อการเรียกหนึ่งได้ 429 พร้อมความหน่วง ให้หยุดพูลทั้งหมดตามเวลานั้น ไม่ใช่แค่การเรียกที่ล้มเหลว
  4. ทำให้คำขอเล็กลง โทเคนต่อคำขอน้อยลงหมายถึงแรงกดดันต่อขีดจำกัดโทเคนต่อนาทีน้อยลง: ตัดประวัติและเอาต์พุตของเครื่องมือ ดู วิธีประมาณต้นทุนของเอเจนต์ AI ว่าโทเคนไปไหน
  5. หยุดเมื่อเจอข้อผิดพลาดที่ลองใหม่ไม่ได้ 401, 400 หรือข้อผิดพลาดยอดคงเหลือจะไม่แก้ตัวเอง; ล้มเหลวเร็วและรายงาน
python
import asyncio

limit = asyncio.Semaphore(4)  # at most 4 requests in flight


async def run(job):
    async with limit:
        return await call_model(job)  # your async call with the retry loop above

รันลูปเอเจนต์บน CheapTokens#

สำหรับลูปเอเจนต์ที่มีการเรียกสั้น ๆ จำนวนมาก CheapTokens มี MiniMax-M2.7: ราคาเดียวทุกช่วงเวลาของวัน คอนเท็กซ์ 180K สูงสุด 8,192 โทเคนต่อคำตอบ และยอดคงเหลือเดียวสำหรับทุกโมเดล เอาต์พุตมีราคา $0.15 ต่อ 1M โทเคน และโดยรวมเมื่อนับอินพุตและเอาต์พุตรวมกัน ถูกกว่า OpenRouter ใช้งานผ่าน endpoint ที่เข้ากันได้กับ OpenAI https://api.cheaptokens.dev/v1 โค้ดข้างต้นชี้ไปที่ endpoint นี้แล้ว; ตั้งไอดีโมเดลจากเอกสาร CheapTokens แล้วรัน

ราคาสดสำหรับงานอินพุต 500,000 เอาต์พุต 100,000:

โมเดล ค่าใช้จ่าย OpenRouter
GLM-5.3-Flash $0.014 $0.0282
DeepSeek-V4-Flash $0.0175 $0.0394
MiniMax-M2.7 $0.04 $0.27

500,000 โทเค็นอินพุตและ 100,000 โทเค็นเอาต์พุตรวมทั้งหมด ราคาต่อ 1M โทเค็นเป็นราคาปัจจุบันและเปลี่ยนแปลงไปพร้อมกับอัตราค่าบริการของเรา คอลัมน์ OpenRouter แสดงราคาอ้างอิงสำหรับปริมาณงานเดียวกัน

ดู ราคาทั้งหมด, เอกสารการตั้งค่า และสร้างคีย์เพื่อส่งคำขอแรกของคุณ

หากยังล้มเหลว#

  • 429 ตั้งแต่คำขอแรก อ่านประเภทข้อผิดพลาดและข้อความในเนื้อหาการตอบกลับ: 429 จากขีดจำกัดการใช้จ่ายหรือบัญชีจะยังล้มเหลวเมื่อลองใหม่จนกว่าขีดจำกัดจะถูกเพิ่มหรือการเข้าถึงกลับมา ส่วน 429 จาก rate limit จะหายไปหลังหยุดพัก
  • 429 ยังมาเรื่อย ๆ พร้อมลูปยาว คุณอาจกำลังซ้อน retry: retry ของ SDK คูณกับ retry ของคุณ ตั้ง SDK เป็นศูนย์ดังในตัวอย่าง
  • ข้อผิดพลาดมาเป็นระลอกตอนเริ่มแบตช์ ค่อย ๆ เพิ่มขึ้นแทนการยิงเวิร์กเกอร์ทั้งหมดพร้อมกัน; OpenAI รายงาน slow_down สำหรับทราฟฟิกที่โตเร็วเกินไป
  • การเรียกแบบ streaming ล้มเหลวกลางทาง อย่าเล่นซ้ำแบบไม่คิดหลังจากบริโภคเอาต์พุตแล้ว; ตัดสินใจว่าจะเริ่มคำตอบใหม่ทั้งหมดหรือทำต่อจากที่มีอยู่
  • ไม่มีการตอบกลับ Retry-After โค้ดจะถอยกลับไปใช้ backoff พร้อม jitter ดังนั้นไม่ต้องเปลี่ยนแปลง