نسخه بتا · دانا به‌زودی عرضه می‌شود
مستندات

محدودیت‌ها

سقف نرخ درخواست API دانا، هدرهای پاسخ و روش درست مدیریت خطای ۴۲۹

هر کلید API سقف نرخ خودش را دارد. پیش‌فرض ۶۰ درخواست در دقیقه است و از رد شدن یک کلید، بقیه کلیدهای شما آسیب نمی‌بینند.

پنجره چطور شمرده می‌شود

شمارش روی پنجره ثابت است، نه لغزان. دقیقه به بازه‌های سرراست تقسیم می‌شود و شمارنده سر هر بازه از صفر شروع می‌کند.

این یک نتیجه عملی دارد. فرض کنید ۶۰ درخواست را در ثانیه‌های پایانی یک بازه بفرستید و ۶۰ تای بعدی را در ثانیه‌های آغازین بازه بعد. هر دو دسته می‌گذرند، یعنی برای یک لحظه دو برابر سقف رد شده است. پس بار سنگین را پخش کنید و به مرز پنجره تکیه نکنید.

هدرهای پاسخ روی ۴۲۹

وقتی از سقف رد شوید، پاسخ 429 این چهار هدر را همراه دارد:

هدرمعنا
Retry-Afterچند ثانیه دیگر پنجره باز می‌شود
X-RateLimit-Limitسقف همین کلید
X-RateLimit-Remainingباقی‌مانده، که اینجا همیشه صفر است
X-RateLimit-Resetزمان باز شدن پنجره به شکل unix timestamp

بدنه پاسخ روی رابط OpenAI:

{
  "error": {
    "message": "Rate limit exceeded.",
    "type": "rate_limit_error",
    "param": null,
    "code": null
  }
}

اول type را بخوانید

کد 429 چهار علت جدا دارد و دو موردشان با تلاش مجدد حل می‌شوند. پیش از تلاش مجدد، فیلد type را نگاه کنید:

مقدار typeمعناتلاش مجدد؟
rate_limit_errorسقف نرخ همین کلیدبله، بعد از Retry-After
capacity_busyصف موقت سرویسبله، بعد از eta_seconds
insufficient_quotaاعتبار تمام شدهخیر، باید شارژ کنید
window_capسقف پنجره توکنخیر، تا زمان reset_at صبر کنید

شرح کامل هر چهار حالت با نمونه بدنه در خطاها آمده است.

مدیریت خطا در کد

به Retry-After گوش بدهید؛ حدس زدن فاصله، یا زودتر می‌زند یا بی‌خود صبر می‌کند. کمی jitter هم اضافه کنید تا چند worker هم‌زمان دوباره به دیوار نخورند.

import random
import time

import httpx


def call_with_retry(payload, api_key, max_attempts=3):
    for attempt in range(max_attempts):
        response = httpx.post(
            "https://api.dana.expert/v1/chat/completions",
            headers={"Authorization": f"Bearer {api_key}"},
            json=payload,
            timeout=120,
        )
        if response.status_code != 429:
            response.raise_for_status()
            return response.json()

        kind = response.json().get("error", {}).get("type")
        if kind not in ("rate_limit_error", "capacity_busy"):
            raise RuntimeError(f"۴۲۹ غیرقابل تلاش مجدد: {kind}")

        wait = float(response.headers.get("Retry-After", 2**attempt))
        time.sleep(wait + random.random())

    raise RuntimeError("سقف نرخ بعد از سه تلاش هنوز باز نشده بود")

سقف هر کلید

سقف روی خودِ کلید می‌نشیند، نه روی حساب. در کنسول دانا می‌توانید برای هر کلید مقدار جداگانه بگذارید.

الگویی که معمولا جواب می‌دهد: یک کلید پرسقف برای سرویس اصلی، یک کلید کم‌سقف برای اسکریپت‌های آزمایشی. اسکریپتی که از کنترل خارج شود، آن‌وقت فقط سهم خودش را می‌سوزاند.

نکته‌ها

  • درخواست‌های ناموفق هم شمرده می‌شوند. شمارش پیش از رسیدن به مدل انجام می‌شود، پس یک 400 هم یک درخواست است.
  • streaming و non-streaming فرقی ندارند؛ هر کدام یک درخواست‌اند، هرچند پاسخ streaming دقایقی طول بکشد.
  • شمارش با شناسه کلید انجام می‌شود. چند سرور که یک کلید مشترک دارند، در یک پنجره سهیم‌اند.
  • برای حجم بالا با پشتیبانی دانا تماس بگیرید.

صفحه‌های مرتبط