ব্লগে ফিরে যান

সমস্যা সমাধানগাইড

একটি AI API থেকে 429 Too Many Requests: backoff সহ retries

একটি AI API থেকে 429 মানে কী এবং এটি কীভাবে সামলাবেন: jitter সহ exponential backoff, Retry-After, এবং এজেন্ট concurrency, Python ও JavaScript কোডসহ।

CheapTokens Team দ্বারাপ্রকাশিত হয়েছে আপডেট করা হয়েছে 10 মিনিট পড়ার সময়

একটি অনুরোধ লুপের ডায়াগ্রাম: একটি 429 রেসপন্স, ক্রমবর্ধমান বিলম্বসহ একটি অপেক্ষার ধাপ, এবং একটি retry
এই পৃষ্ঠায়
  1. 429 মানে কী
  2. একটি বিলম্ব অনুমান করার আগে হেডারগুলো পড়ুন
  3. Python: jitter সহ backoff
  4. JavaScript: একই লুপ
  5. এজেন্টদের 429 কীভাবে সামলানো উচিত
  6. CheapTokens-এ এজেন্ট লুপ চালান
  7. যদি তবুও ব্যর্থ হয়

একটি AI API থেকে 429 Too Many Requests মানে হলো অনুরোধটি প্রত্যাখ্যাত হয়েছে কারণ আপনি খুব বেশি খুব দ্রুত পাঠিয়েছেন, অথবা আপনার অ্যাকাউন্টের একটি সীমা ছুঁয়েছেন। সমাধান হলো অপেক্ষা করা এবং exponential backoff ও jitter সহ পুনরায় চেষ্টা করা, রেসপন্সে Retry-After হেডার থাকলে তা ব্যবহার করা, এবং আপনার এজেন্ট সমান্তরালে কতগুলো অনুরোধ পাঠায় তার একটি সীমা নির্ধারণ করা। যে 429 আসলে একটি spend বা balance সীমা, সেটি পুনরায় চেষ্টা করবেন না: অপেক্ষা করলে তা ঠিক হবে না।

এই গাইডটি OpenAI এবং Anthropic-এর অফিসিয়াল rate-limit গাইডের উপর ভিত্তি করে তৈরি, দুটোই ২২ অক্টোবর ২০২৬-এ যাচাই করা। কোনও পেজেই সর্বশেষ আপডেটের তারিখ দেখানো হয়নি।

429 মানে কী#

OpenAI একটি 429-কে বর্ণনা করে এমন একটি অনুরোধ হিসেবে যা rate limit অতিক্রম করার কারণে প্রত্যাখ্যাত হয়েছে (rate_limit_error, ট্রাফিক খুব দ্রুত বাড়লে কোড slow_down সহ), অথবা একটি hard spend limit-এ পৌঁছানোর কারণে। Anthropic-ও 429-এর জন্য rate_limit_error ব্যবহার করে। spend-cap 429-এ কোনও retry-after হেডার থাকে না এবং অ্যাক্সেস পুনরায় চালু না হওয়া পর্যন্ত এটি ব্যর্থ হতে থাকে।

এর সাথে দুটি প্রতিবেশীকে সহজেই গুলিয়ে ফেলা যায়। OpenAI-এর 503 server_is_overloaded এবং Anthropic-এর 529 overloaded_error হলো overload অবস্থা, আপনার rate limit নয়। এগুলো সাধারণত একই retry লুপ দিয়ে সামলানো হয়, কিন্তু কারণ সম্পর্কে এগুলো ভিন্ন কিছু বলে।

তাহলে তিনটি ক্ষেত্র আছে, এবং প্রতিটির জন্য ভিন্ন প্রতিক্রিয়া প্রয়োজন:

Response Cause What to do
429 rate limit প্রতি মিনিটে খুব বেশি অনুরোধ বা টোকেন অপেক্ষা করুন, backoff সহ পুনরায় চেষ্টা করুন
429 spend বা balance limit অ্যাকাউন্টের সীমা ছুঁয়েছে পুনরায় চেষ্টা করবেন না; সীমা ঠিক করুন বা ব্যালেন্স টপ আপ করুন
503 / 529 overloaded প্রোভাইডারের দিক ব্যস্ত একটি সীমা সহ backoff দিয়ে পুনরায় চেষ্টা করুন

যদি আপনার ব্যালেন্স শেষ হয়ে যায়, তাহলে এর পরিবর্তে "not enough tokens" ত্রুটি পড়ুন। টাইমআউট এবং প্রত্যাখ্যাত সংযোগের মতো নেটওয়ার্ক-স্তরের ব্যর্থতার জন্য, সাধারণ সংযোগ ত্রুটি দেখুন।

একটি বিলম্ব অনুমান করার আগে হেডারগুলো পড়ুন#

OpenAI rate-limited রেসপন্সে এই হেডারগুলো ডকুমেন্ট করে: Retry-After, এবং x-ratelimit-limit-requests, x-ratelimit-remaining-requests, x-ratelimit-reset-requests -tokens ভ্যারিয়েন্ট সহ। অন্যান্য API এগুলোর একটি উপসেট বা কোনওটিই পাঠাতে পারে, তাই ক্লায়েন্ট এমনভাবে লিখুন যেন Retry-After উপস্থিত থাকলে তা ব্যবহার করে এবং না থাকলে আপনার নিজের সময়সূচিতে ফিরে যায়।

OpenAI-এর retry সংক্রান্ত নির্দেশনা, সংক্ষেপে:

  • Retry-After-কে সর্বনিম্ন অপেক্ষা হিসেবে ধরুন এবং তার উপরে একটি ছোট র‍্যান্ডম বিলম্ব যোগ করুন।
  • এটি না থাকলে, jitter সহ exponential backoff ব্যবহার করুন।
  • চেষ্টার সংখ্যা এবং মোট retry সময়—দুটোরই একটি সীমা নির্ধারণ করুন।
  • SDK-এর retry-এর উপরে আপনার নিজের retry স্তূপ করবেন না।
  • quota বা billing ত্রুটি পুনরায় চেষ্টা করবেন না।
  • ব্যর্থ অনুরোধও প্রতি-মিনিটের সীমার মধ্যে গণনা হয়, তাই একটি আক্রমণাত্মক লুপ পরিস্থিতি আরও খারাপ করে।
  • স্ট্রিমিংয়ের ক্ষেত্রে, ইতিমধ্যে আউটপুট গ্রহণ করার পর একটি অনুরোধ পুনরায় চালাবেন না।

Python: jitter সহ backoff#

অফিসিয়াল openai প্যাকেজ যেকোনো OpenAI-সামঞ্জস্যপূর্ণ এন্ডপয়েন্টের সাথে কাজ করে। SDK-এর নিজস্ব retry বন্ধ করুন (max_retries=0) যাতে সেগুলো আপনার retry-এর সাথে গুণিত না হয়। মডেলের id আসে CheapTokens ডকস থেকে; প্রোভাইডারভেদে id ভিন্ন হয়।

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)

হেডার ছাড়া বিলম্বের ঊর্ধ্বসীমা ১, ২, ৪, ৮ সেকেন্ড বাড়ে (সর্বোচ্চ ৩০-এ সীমাবদ্ধ), এবং random.uniform তার নিচে একটি র‍্যান্ডম মান বেছে নেয়, তাই অনেক worker একই মুহূর্তে জেগে ওঠে না। Retry-After একটি HTTP date-ও হতে পারে; উপরের কোডটি সংখ্যাসূচক রূপটি সামলায় এবং অন্যথায় 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);
    }
  }
}

যখন এটি কাজ করে, একটি rate-limited কল কেবল বেশি সময় নেয় এবং তারপর একটি স্বাভাবিক completion ফেরত দেয়। যখন শেষ চেষ্টার পরেও এটি ব্যর্থ হয়, মূল ত্রুটিটি উত্থাপিত হয়, যাতে আপনার কোড এটি লগ করতে বা কাজটি পরবর্তীর জন্য কিউ করতে পারে।

এজেন্টদের 429 কীভাবে সামলানো উচিত#

একটি এজেন্ট লুপ অনেক ছোট কল পাঠায়, এবং একসাথে একাধিক এজেন্ট বা tool কল চলতে পারে। কেবল পুনরায় চেষ্টা করাই যথেষ্ট নয়; উৎসেই অনুরোধের হার কমান:

  1. Concurrency সীমাবদ্ধ করুন। ৩–৫টি সমান্তরাল কলের একটি semaphore একটি যুক্তিসঙ্গত সূচনা; ত্রুটি বিরল থাকলে তবেই তা বাড়ান।
  2. একটি limiter শেয়ার করুন। যদি একাধিক worker একই key ব্যবহার করে, তারা একই সীমা শেয়ার করে, তাই প্রতি worker-এ একটি retry লুপ যথেষ্ট নয়: প্রসেসের জন্য একটি কিউ বা একটি semaphore ব্যবহার করুন।
  3. সামগ্রিকভাবে back off করুন। যখন একটি কল একটি বিলম্বসহ 429 পায়, কেবল ব্যর্থ কলটি নয় বরং পুরো pool-কে সেই সময়ের জন্য থামিয়ে দিন।
  4. অনুরোধ ছোট রাখুন। প্রতি অনুরোধে কম টোকেন মানে token-per-minute সীমার উপর কম চাপ: history এবং tool আউটপুট ছাঁটাই করুন। টোকেনগুলো কোথায় যায় তা জানতে একটি AI এজেন্টের খরচ কীভাবে অনুমান করবেন দেখুন।
  5. non-retryable ত্রুটিতে থামুন। একটি 401, 400, বা balance ত্রুটি নিজে থেকে ঠিক হবে না; দ্রুত ব্যর্থ হয়ে রিপোর্ট করুন।
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 অফার করে: দিনের যেকোনো সময়ে একই দাম, ১৮০K কনটেক্সট, প্রতি উত্তরে সর্বোচ্চ ৮,১৯২ টোকেন, এবং সব মডেলের জন্য একটি ব্যালেন্স। আউটপুটের খরচ প্রতি ১M টোকেনে $0.15, এবং মোটে, ইনপুট ও আউটপুট একসাথে গণনা করলে, এটি OpenRouter-এর চেয়ে সস্তা। এটি OpenAI-সামঞ্জস্যপূর্ণ এন্ডপয়েন্ট https://api.cheaptokens.dev/v1-এর মাধ্যমে কাজ করে। উপরের কোডটি ইতিমধ্যে এই এন্ডপয়েন্টের দিকে নির্দেশ করে; CheapTokens ডকস থেকে মডেল id সেট করুন এবং চালান।

৫০০,০০০ ইনপুট, ১০০,০০০ আউটপুট ওয়ার্কলোডের জন্য লাইভ দাম:

মডেল খরচ OpenRouter
GLM-5.3-Flash $0.014 $0.0282
DeepSeek-V4-Flash $0.0175 $0.0394
MiniMax-M2.7 $0.04 $0.27

মোট ৫,০০,০০০ ইনপুট এবং ১,০০,০০০ আউটপুট টোকেন। প্রতি 1M টোকেনের দাম বর্তমান এবং আমাদের ট্যারিফের সাথে পরিবর্তিত হয়; OpenRouter কলামটি একই ওয়ার্কলোডের জন্য রেফারেন্স মূল্য দেখায়।

সব দাম, সেটআপ ডকস দেখুন, এবং আপনার প্রথম অনুরোধ পাঠাতে একটি key তৈরি করুন।

যদি তবুও ব্যর্থ হয়#

  • একেবারে প্রথম অনুরোধেই 429। রেসপন্স বডিতে ত্রুটির ধরন ও বার্তা পড়ুন: একটি spend বা account-limit 429 সীমা বাড়ানো বা অ্যাক্সেস পুনরায় চালু না হওয়া পর্যন্ত retry-তে ব্যর্থ হতে থাকে, অন্যদিকে একটি rate-limit 429 বিরতির পরে কেটে যায়।
  • একটি দীর্ঘ লুপের সাথে 429 আসতেই থাকে। আপনি সম্ভবত retry স্তূপ করছেন: SDK retry গুণ আপনার retry। উদাহরণগুলোর মতো SDK-কে শূন্যে সেট করুন।
  • একটি ব্যাচের শুরুতে ত্রুটি ঝাঁকে ঝাঁকে আসে। সব worker একসাথে চালানোর বদলে ধীরে ধীরে বাড়ান; OpenAI খুব দ্রুত বাড়া ট্রাফিকের জন্য slow_down রিপোর্ট করে।
  • স্ট্রিমিং কল মাঝপথে ব্যর্থ হয়েছে। আউটপুট গ্রহণ করার পর এটি অন্ধভাবে পুনরায় চালাবেন না; পুরো উত্তর পুনরায় শুরু করবেন নাকি যা আছে তা থেকে চালিয়ে যাবেন তা সিদ্ধান্ত নিন।
  • রেসপন্সে Retry-After নেই। কোডটি jitter সহ backoff-এ ফিরে যায়, তাই কোনও পরিবর্তন প্রয়োজন নেই।