> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chattler.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# حدود المعدل

> نافذة تقنية لكل مفتاح، تُبلَّغ في الترويسات على ردود JSON الناجحة وعلى 429.

لكل مفتاح نافذته الخاصة: **120 طلبًا كل 60 ثانية** افتراضيًا. هذا سقف تشغيلي ضد إساءة الاستخدام، لا حصة خطة. الاستخدام نفسه يُقاس برصيد المالك لا بهذا العدّاد، ومفاتيح المالك نفسه لا تتشارك النافذة.

تعمل المصادقة قبل المحدّد، لذلك الطلب غير المصادق لا ينفق أبدًا من ميزانية المفتاح.

## الترويسات

ردود JSON الناجحة (`200`) والرد `429` تحمل حالة النافذة:

| الترويسة                | المعنى                                       |
| ----------------------- | -------------------------------------------- |
| `X-RateLimit-Limit`     | الطلبات المسموح بها في النافذة               |
| `X-RateLimit-Remaining` | الطلبات المتبقية في النافذة الحالية          |
| `X-RateLimit-Reset`     | توقيت Unix (بالثواني) الذي تُعاد فيه النافذة |

رد `text/event-stream` الخاص بنقطة نهاية الرد المتدفق وأغلفة الأخطاء الأخرى (`401`، `403`، `404`، `409`، `422`، `503`، `500`) لا تحمل هذه الترويسات؛ الترويسة `X-Request-ID` وحدها موجودة في كل رد. إذا كنت تضبط وتيرة عميلك على `X-RateLimit-Remaining`، فعامل الترويسة المفقودة على أنها «غير معروفة» لا صفرًا.

عندما تُستنفد النافذة تجيب API بـ `429 rate_limit_exceeded` وتضيف `Retry-After` (الثواني حتى إعادة النافذة).

```http theme={null}
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1789470060
Retry-After: 17
X-Request-ID: 7f20a5f0-5dde-48cf-94aa-38acbbd218e0

{"error": {"code": "rate_limit_exceeded", "message": "Rate limit exceeded for this API key; try again later", "request_id": "7f20a5f0-5dde-48cf-94aa-38acbbd218e0"}}
```

## عندما يتوقف المحدّد نفسه

المحدّد يفشل **بالإغلاق**. إذا كان مخزنه غير متاح، تجيب API بـ `503 api_rate_limit_unavailable` مع `Retry-After: 5` بدلًا من تمرير الطلبات بلا قياس، وبدلًا من 429 مضلل. عامله كأي خطأ عابر آخر: انتظر ثم أعد المحاولة بنفس `Idempotency-Key`.

<Warning>
  لا توزّع الحركة على عدة مفاتيح للوكيل نفسه لرفع السقف. لكل مفتاح محادثاته الخاصة، فينتهي المستخدم النهائي نفسه بسجل مجزأ. إذا احتجت إلى حد أعلى، تواصل معنا.
</Warning>

## ضبط الوتيرة من جهة العميل

<CodeGroup>
  ```bash curl theme={null}
  # Retries on 429/503 after Retry-After, then prints the window state of the final answer.
  while :; do
    status=$(curl -s -o /dev/null -D headers.txt -w '%{http_code}' \
      "$CHATTLER_BASE_URL/v1/agents/$CHATTLER_AGENT_ID/conversations?limit=20" \
      -H "Authorization: Bearer $CHATTLER_API_KEY")
    if [ "$status" = "429" ] || [ "$status" = "503" ]; then
      sleep "$(grep -i '^Retry-After:' headers.txt | tr -d '\r' | awk '{print $2}')"
      continue
    fi
    grep -i '^X-RateLimit-' headers.txt
    break
  done
  ```

  ```python Python theme={null}
  import time

  import httpx


  def request_with_pacing(method: str, url: str, **kwargs) -> httpx.Response:
      while True:
          response = httpx.request(method, url, headers=HEADERS, timeout=120, **kwargs)
          if response.status_code in (429, 503):
              time.sleep(float(response.headers.get("Retry-After", "5")))
              continue
          if int(response.headers.get("X-RateLimit-Remaining", "1")) == 0:
              reset_at = int(response.headers.get("X-RateLimit-Reset", "0"))
              time.sleep(max(0.0, reset_at - time.time()))
          return response
  ```

  ```javascript Node theme={null}
  async function requestWithPacing(url, init = {}) {
    for (;;) {
      const response = await fetch(url, {
        ...init,
        headers: { Authorization: `Bearer ${apiKey}`, ...(init.headers ?? {}) },
      });
      if (response.status === 429 || response.status === 503) {
        const wait = Number(response.headers.get("Retry-After") ?? 5);
        await new Promise((r) => setTimeout(r, wait * 1000));
        continue;
      }
      if (response.headers.get("X-RateLimit-Remaining") === "0") {
        const resetAt = Number(response.headers.get("X-RateLimit-Reset") ?? 0) * 1000;
        await new Promise((r) => setTimeout(r, Math.max(0, resetAt - Date.now())));
      }
      return response;
    }
  }
  ```
</CodeGroup>
