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

ในหน้านี้
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_ และ Anthropic's 529 overloaded_ เป็นสภาวะโอเวอร์โหลด ไม่ใช่ rate limit ของคุณ โดยปกติจัดการด้วยลูป retry เดียวกัน แต่บ่งบอกสาเหตุที่ต่างออกไป
ดังนั้นมีสามกรณี และแต่ละกรณีต้องตอบสนองต่างกัน:
เลื่อนตารางไปทางขวา →
| การตอบกลับ | สาเหตุ | สิ่งที่ต้องทำ |
|---|---|---|
| 429 rate limit | คำขอหรือโทเคนต่อนาทีมากเกินไป | รอ ลองใหม่ด้วย backoff |
| 429 ขีดจำกัดการใช้จ่ายหรือยอดคงเหลือ | ถึงขีดจำกัดบัญชี | อย่าลองใหม่; แก้ขีดจำกัดหรือเติมยอดคงเหลือ |
| 503 / 529 overloaded | ฝั่งผู้ให้บริการไม่ว่าง | ลองใหม่ด้วย backoff พร้อมเพดาน |
หากยอดคงเหลือของคุณหมด อ่าน ข้อผิดพลาด "not enough tokens" แทน สำหรับความล้มเหลวระดับเครือข่าย เช่น timeout และการปฏิเสธการเชื่อมต่อ ดู ข้อผิดพลาดการเชื่อมต่อทั่วไป
อ่านเฮดเดอร์ก่อนเดาระยะเวลารอ#
OpenAI ระบุเฮดเดอร์เหล่านี้บนการตอบกลับที่ถูกจำกัดอัตรา: Retry-After และ x-, x-, x- พร้อมตัวแปร -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; ไอดีแตกต่างกันระหว่างผู้ให้บริการ
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: ลูปเดียวกัน#
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 อย่างไร#
ลูปเอเจนต์ส่งคำขอสั้น ๆ จำนวนมาก และเอเจนต์หรือการเรียกเครื่องมือหลายตัวอาจทำงานพร้อมกัน การลองใหม่เพียงอย่างเดียวไม่พอ; ลดอัตราคำขอที่ต้นทาง:
- จำกัด concurrency เซมาฟอร์ที่ 3–5 การเรียกพร้อมกันเป็นจุดเริ่มต้นที่สมเหตุสมผล; เพิ่มขึ้นเฉพาะเมื่อข้อผิดพลาดยังเกิดน้อย
- ใช้ limiter ตัวเดียวร่วมกัน หากเวิร์กเกอร์หลายตัวใช้คีย์เดียวกัน พวกมันใช้ขีดจำกัดเดียวกัน ดังนั้นลูป retry หนึ่งต่อเวิร์กเกอร์ไม่พอ: ใช้คิวเดียวหรือเซมาฟอร์เดียวสำหรับโปรเซส
- ถอยกลับทั้งระบบ เมื่อการเรียกหนึ่งได้ 429 พร้อมความหน่วง ให้หยุดพูลทั้งหมดตามเวลานั้น ไม่ใช่แค่การเรียกที่ล้มเหลว
- ทำให้คำขอเล็กลง โทเคนต่อคำขอน้อยลงหมายถึงแรงกดดันต่อขีดจำกัดโทเคนต่อนาทีน้อยลง: ตัดประวัติและเอาต์พุตของเครื่องมือ ดู วิธีประมาณต้นทุนของเอเจนต์ AI ว่าโทเคนไปไหน
- หยุดเมื่อเจอข้อผิดพลาดที่ลองใหม่ไม่ได้ 401, 400 หรือข้อผิดพลาดยอดคงเหลือจะไม่แก้ตัวเอง; ล้มเหลวเร็วและรายงาน
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:/ โค้ดข้างต้นชี้ไปที่ endpoint นี้แล้ว; ตั้งไอดีโมเดลจากเอกสาร CheapTokens แล้วรัน
ราคาสดสำหรับงานอินพุต 500,000 เอาต์พุต 100,000:
| โมเดล | Input $/1M | Output $/1M | ค่าใช้จ่าย | OpenRouter |
|---|---|---|---|---|
| GLM-5.3-Flash | $0.02 | $0.04 | $0.014 | $0.0282 |
| DeepSeek-V4-Flash | $0.015 | $0.10 | $0.0175 | $0.0394 |
| MiniMax-M2.7 | $0.05 | $0.15 | $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 ดังนั้นไม่ต้องเปลี่ยนแปลง



