العودة إلى المدونة

استكشاف الأخطاءدليل

429 طلبات كثيرة جدًا من واجهة برمجة تطبيقات ذكاء اصطناعي: إعادة المحاولة مع التراجع

ماذا يعني رمز 429 من واجهة برمجة تطبيقات ذكاء اصطناعي وكيفية التعامل معه: التراجع الأُسّي مع التذبذب العشوائي، وRetry-After، وتزامن الوكلاء، مع كود Python وJavaScript.

بواسطة CheapTokens Teamنُشر في تم التحديث في 6 دقيقة قراءة

مخطط لحلقة طلبات: استجابة 429، وخطوة انتظار بتأخير متزايد، وإعادة محاولة
في هذه الصفحة
  1. ماذا يعني رمز 429
  2. اقرأ الترويسات قبل أن تخمّن مدة التأخير
  3. Python: التراجع مع التذبذب العشوائي
  4. JavaScript: الحلقة نفسها
  5. كيف ينبغي للوكلاء التعامل مع 429
  6. شغّل حلقات الوكلاء على CheapTokens
  7. إذا استمر الفشل

يعني رمز 429 Too Many Requests القادم من واجهة برمجة تطبيقات ذكاء اصطناعي أن الطلب قد رُفض لأنك أرسلت أكثر من اللازم بسرعة كبيرة، أو لأنك بلغت حدًا في حسابك. الحل هو الانتظار وإعادة المحاولة باستخدام التراجع الأُسّي مع التذبذب العشوائي، واستخدام ترويسة Retry-After عندما تتضمنها الاستجابة، وتحديد سقف لعدد الطلبات التي يرسلها وكيلك بالتوازي. لا تُعِد محاولة رمز 429 الذي يمثل في الحقيقة حدًا للإنفاق أو الرصيد: فالانتظار لن يحل المشكلة.

يعتمد هذا الدليل على الأدلة الرسمية لحدود المعدل لدى OpenAI وAnthropic، وكلتاهما تم التحقق منهما في 22 أكتوبر 2026. ولا تعرض أي من الصفحتين تاريخ آخر تحديث.

ماذا يعني رمز 429#

تصف OpenAI رمز 429 بأنه طلب رُفض لتجاوزه حد المعدل (rate_limit_error، مع الرمز slow_down عندما يتزايد الحمل بسرعة كبيرة)، أو لأنه بلغ حد إنفاق صارمًا. وتستخدم Anthropic الرمز rate_limit_error مع 429 أيضًا. أما رمز 429 الناتج عن سقف الإنفاق فلا يتضمن ترويسة retry-after ويستمر في الفشل حتى يُستأنف الوصول.

هناك رمزان مجاوران يسهل الخلط بينهما وبينه. فرمز 503 server_is_overloaded لدى OpenAI ورمز 529 overloaded_error لدى Anthropic هما حالتا تحميل زائد، وليسا حد المعدل الخاص بك. وعادةً ما يُتعامل معهما بحلقة إعادة المحاولة نفسها، لكنهما يقولان شيئًا مختلفًا عن السبب.

إذن هناك ثلاث حالات، وكل واحدة تتطلب رد فعل مختلفًا:

الاستجابة السبب ما يجب فعله
429 حد المعدل عدد كبير جدًا من الطلبات أو الرموز في الدقيقة انتظر، وأعد المحاولة مع التراجع
429 حد الإنفاق أو الرصيد بلوغ حد الحساب لا تُعِد المحاولة؛ عدّل الحد أو اشحن الرصيد
503 / 529 تحميل زائد جانب المزوّد مشغول أعد المحاولة مع التراجع، مع سقف

إذا نفد رصيدك، فاقرأ خطأ "الرموز غير كافية" بدلًا من ذلك. أما الأعطال على مستوى الشبكة مثل انتهاء المهلة ورفض الاتصالات، فراجع أخطاء الاتصال الشائعة.

اقرأ الترويسات قبل أن تخمّن مدة التأخير#

توثّق OpenAI هذه الترويسات في الاستجابات المحدودة المعدل: Retry-After، وx-ratelimit-limit-requests، وx-ratelimit-remaining-requests، وx-ratelimit-reset-requests مع متغيرات -tokens. وقد ترسل واجهات برمجة أخرى مجموعة فرعية منها أو لا ترسل أيًا منها، لذا اكتب العميل ليستخدم Retry-After عند وجودها وليعود إلى جدولك الخاص عند غيابها.

إرشادات OpenAI بشأن إعادة المحاولة، باختصار:

  • اعتبر Retry-After الحد الأدنى للانتظار وأضف تأخيرًا عشوائيًا صغيرًا فوقه.
  • في غيابه، استخدم التراجع الأُسّي مع التذبذب العشوائي.
  • ضع سقفًا لعدد المحاولات ولإجمالي وقت إعادة المحاولة معًا.
  • لا تُراكم إعادات محاولتك فوق إعادات محاولة SDK.
  • لا تُعِد محاولة أخطاء الحصة أو الفوترة.
  • الطلبات الفاشلة تُحتسب أيضًا ضمن حد الدقيقة، لذا فإن حلقة عدوانية تزيد الأمور سوءًا.
  • في البث، لا تُعِد تشغيل طلب بعد أن استهلكت مخرجات منه بالفعل.

Python: التراجع مع التذبذب العشوائي#

تعمل حزمة openai الرسمية مع أي نقطة نهاية متوافقة مع OpenAI. أوقف إعادات محاولة 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؛ يتعامل الكود أعلاه مع الصيغة الرقمية ويعود إلى التراجع في غير ذلك.

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. حدّد سقفًا للتزامن. إن استخدام سيمافور من 3 إلى 5 استدعاءات متوازية نقطة بداية معقولة؛ لا ترفعه إلا ما دامت الأخطاء نادرة.
  2. شارك محدِّدًا واحدًا. إذا استخدم عدة عمال المفتاح نفسه، فإنهم يتشاركون الحدود نفسها، لذا لا تكفي حلقة إعادة محاولة واحدة لكل عامل: استخدم قائمة انتظار واحدة أو سيمافورًا واحدًا للعملية.
  3. تراجع عالميًا. عندما يتلقى استدعاء واحد رمز 429 مع تأخير، أوقف المجموعة بأكملها لتلك المدة بدلًا من الاستدعاء الفاشل فقط.
  4. اجعل الطلبات أصغر. رموز أقل لكل طلب تعني ضغطًا أقل على حدود الرموز في الدقيقة: قلّم السجل ومخرجات الأدوات. راجع كيفية تقدير تكلفة وكيل ذكاء اصطناعي لمعرفة أين تذهب الرموز.
  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 لكل مليون رمز، وإجمالًا، مع احتساب المدخلات والمخرجات معًا، فهي أرخص من OpenRouter. ويعمل عبر نقطة النهاية المتوافقة مع OpenAI https://api.cheaptokens.dev/v1. الكود أعلاه يشير بالفعل إلى نقطة النهاية هذه؛ عيّن معرّف النموذج من وثائق 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 رمز إخراج إجمالًا. الأسعار لكل مليون رمز حالية وتتغير مع تعريفاتنا؛ يعرض عمود OpenRouter أسعارًا مرجعية لنفس حجم العمل.

راجع جميع الأسعار، ووثائق الإعداد، وأنشئ مفتاحًا لإرسال طلبك الأول.

إذا استمر الفشل#

  • 429 في الطلب الأول تمامًا. اقرأ نوع الخطأ ورسالته في جسم الاستجابة: فرمز 429 الناتج عن الإنفاق أو حد الحساب يستمر في الفشل عند إعادة المحاولة حتى يُرفع الحد أو يُستأنف الوصول، بينما يزول رمز 429 الناتج عن حد المعدل بعد توقف مؤقت.
  • يتكرر 429 مع حلقة طويلة. قد تكون تُراكم إعادات المحاولة: إعادات SDK مضروبة في إعاداتك. اضبط SDK على صفر، كما في الأمثلة.
  • تأتي الأخطاء في دفعات عند بداية الدفعة. زد الحمل تدريجيًا بدلًا من إطلاق جميع العمال دفعة واحدة؛ إذ تذكر OpenAI الرمز slow_down للحمل الذي يتزايد بسرعة كبيرة.
  • فشل استدعاء البث في منتصفه. لا تُعِد تشغيله بشكل أعمى بعد استهلاك المخرجات؛ قرر ما إذا كنت ستعيد تشغيل الإجابة كاملة أو تكمل من حيث توقفت.
  • لا يوجد Retry-After في الاستجابة. يعود الكود إلى التراجع مع التذبذب العشوائي، لذا لا حاجة لأي تغيير.