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

# حد المعدل في API: الحدود والرؤوس ومعالجة الخطأ 429

> حدود معدل Didit API، ورؤوس X-RateLimit، والتراجع عبر Retry-After، ونطاقات نقاط النهاية، ومعالجة 429. خطّط لحركة الإنتاج بثقة.

يطبّق Didit عدة طبقات من تحديد المعدل للحفاظ على استقرار واجهة API مع السماح بدفعات معتادة من حركة المرور. تُطبَّق الحدود **لكل معرّف**، وهو مفتاح `x-api-key` الخاص بك، أو عنوان IP لعميلك إذا لم يُرسَل مفتاح API. يحتفظ كل نطاق بعدّاد مستقل بنافذة منزلقة مدتها 60 ثانية.

## المعرّف والاستثناءات

يختار الوسيط معرّف تحديد المعدل بهذا الترتيب:

1. ترويسة `x-api-key`: عدّاد على مستوى المفتاح.
2. أول قفزة في `X-Forwarded-For`: عدّاد على مستوى عنوان IP.
3. `REMOTE_ADDR`: عدّاد على مستوى عنوان IP.

تتجاوز الاستدعاءات التالية **تحديد المعدل** بالكامل:

* مفتاح `DIDIT_WEBAPP_API_KEY` الداخلي (يُستخدم فقط بواسطة وحدة تحكم Didit).
* endpoints المحمية بالإذن `IsValidSession` (حركة الجلسات المضمّنة/SDK).
* `GET /system/healthcheck` (يخدمه `HealthMiddleware` قبل هذه المجموعة من الوسطاء).
* webhooks المزوّدين: Stripe، وفحص AML، وردود استدعاء التحقق من الوجه، وتعديل قوائم KYC.

## الحدود العامة

تُصنَّف كل طلب موثّق أيضاً حسب طريقة HTTP ضمن نطاق عام، بحيث يرث أي مسار جديد حداً افتراضياً معقولاً:

| النطاق | طريقة HTTP | الحد | النافذة |
| - | - | - | - |
| `generic-get` | `GET` | 600 / دقيقة | 60 ثانية |
| `generic-write` | `POST`، `PATCH`، `DELETE` | 300 / دقيقة | 60 ثانية |
| `ingest-write` | `POST /v3/transactions/`، `POST /v3/events/`، `POST /v3/events/batch/` | 600 / دقيقة | 60 ثانية |

تُحتسب عمليات إنشاء المعاملات وإدخال الأحداث ضمن `ingest-write` **بدلاً من** `generic-write`: يحصل تكامل الإدخال عالي الحجم على 600 طلب في الدقيقة لكل مفتاح API دون استنفاد ميزانية عمليات الكتابة الأخرى.

تُطبَّق حدود endpoint (في القسم التالي) **بالإضافة إلى** الحد العام. أول نطاق يتجاوز عدّاده هو الذي يعيد `429`.

## حدود endpoint

يعرّف الوسيط نطاقات أكثر صرامة للمسارات الأعلى تأثيراً:

| النطاق | endpoint | الحد | ثابت المصدر |
| - | - | - | - |
| `session-v2-create` | `POST /v2/session/`، `POST /v3/session/` | 600 / دقيقة | `SESSION_CREATE_RATE_LIMIT` |
| `session-decision` | `GET /v1/session/<id>/decision/`، `GET /v2/session/<id>/decision/` | 100 / دقيقة | `SESSION_DECISION_RATE_LIMIT` |
| `session-generate-pdf` | `GET /v1/session/<id>/generate-pdf/`، `GET /v3/session/<id>/generate-pdf/` | 50 / دقيقة | `SESSION_PDF_RATE_LIMIT` |
| `session-add-images` | `POST`/`PATCH /session/<id>/add-images/` | 10 / دقيقة | `SESSION_ADD_IMAGES_RATE_LIMIT` |
| `session-update-kyc` | `POST`/`PATCH /session/<id>/update-data/` | 10 / دقيقة | `SESSION_UPDATE_KYC_RATE_LIMIT` |
| `session-update-poa` | `POST`/`PATCH /session/<id>/update-poa-data/` | 10 / دقيقة | `SESSION_UPDATE_POA_RATE_LIMIT` |

> [!NOTE]
>
> تخدم `SessionV3RetrieveView` المسار `GET /v3/session/<id>/decision/` ولا يندرج **ضمن** نطاق `session-decision`. يخضع فقط للسقف العام `generic-get` البالغ 600 في الدقيقة. ينطبق حد معدل القرار البالغ 100 في الدقيقة على مسارات القرار في الإصدارين v1 وv2.

> [!WARNING]
>
> يعرّف الكود أيضاً `FREE_SESSION_RATE_LIMIT = 10` و`PAID_SESSION_RATE_LIMIT = 600`. هذه الثوابت **غير مطبّقة حالياً** على `POST /v3/session/`، إذ يستخدم هذا endpoint حد `session-v2-create` الواحد البالغ 600 في الدقيقة لكل تطبيق. لا تبنِ تخطيط السعة على حد معدل للمستوى المجاني لكل سير عمل حتى يُوثَّق بوصفه فعّالاً.

## شكل استجابة 429

عند تجاوز أي نطاق تحصل على استجابة HTTP `429` بهذا الجسم وهذه الترويسات:

```http theme={null}
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1762534870
Retry-After: 37

{
  "detail": "Session creation rate limit exceeded. You can make up to 600 requests per minute."
}
```

| الترويسة | المعنى |
| - | - |
| `X-RateLimit-Limit` | الحد الأقصى للطلبات المسموح بها في النافذة الحالية للنطاق الذي تجاوزته. |
| `X-RateLimit-Remaining` | عدد الطلبات المتبقية قبل إعادة ضبط النافذة. يُثبَّت عند `0` بعد تجاوز الحد. |
| `X-RateLimit-Reset` | عدد الثواني منذ بداية العصر اليونكسي (Unix epoch) عند إعادة ضبط العدّاد إلى الصفر. |
| `Retry-After` | عدد الثواني التي يجب انتظارها قبل المحاولة التالية، مقرّبةً للأعلى بحيث يضمن الانتظار وصول المحاولة إلى النافذة التالية. تظهر **فقط في استجابات `429`**، وليس في كل طلب. |

تظهر ترويسات `X-RateLimit-*` **فقط في استجابة `429`**، ولا تتضمنها الاستجابات الناجحة حالياً. استخدم قيمة `Retry-After` (أو `X-RateLimit-Reset - now`) مدةً للانتظار قبل المحاولة التالية.

## إرشادات للعميل البرمجي

* **احترم `Retry-After`** كحدٍّ أدنى لفترة التراجع. وفوقه، طبّق التراجع الأُسّي مع الجِتر (`5s → 10s → 20s → 40s ± random`) حتى لا يتزامن العميلان اللذان يتلقيان `429` في اللحظة نفسها.
* **توقّف عن إعادة المحاولة** بعد حدٍّ أقصى صغير (مثلاً 5 محاولات). اعرض الخطأ على المستدعي وأبلغ فريقك، فتكرار 429 لفترة طويلة يدل عادةً على حلقة تكرار خارجة عن السيطرة، لا على ارتفاع عابر في الحركة.
* **استخدم معرّفاً واحداً لكل حمل عمل.** مشاركة `x-api-key` بين عدة خوادم خلفية تجمعها في العداد نفسه، وقد يؤدي ذلك إلى أن يستهلك أحدها الحصة فيحرم البقية. أصدر تطبيقاً منفصلاً (وبالتالي `api_key` منفصلاً) لكل بيئة أو علامة تجارية أو عميل. راجع [التسجيل البرمجي](https://docs.didit.me/ar/integration/programmatic-registration.md#إدارة-التطبيقات).
* **خزّن نتائج قراءات القرار مؤقتاً.** نقاط النهاية `/decision/` غير مصممة لحلقات الاستطلاع المتقاربة زمنيًا. إذا كنت تحتاج إلى تحديثات فورية، فاستخدم [webhook](https://docs.didit.me/ar/integration/webhooks.md) ولا تستدعِ `GET /v3/session/<id>/decision/` إلا لاستكمال البيانات المفقودة أو للتدقيق.
* **أنشئ ملفات PDF دون اتصال.** عرض ملفات PDF يستهلك موارد المعالج، والحد الأقصى هو 50 في الدقيقة. شغّله من قائمة انتظار بعد أن تصل الجلسة إلى حالة نهائية، لا عند كل عرض للصفحة.

## مثال على عميل يراعي استجابات 429

```javascript theme={null}
async function postSession(body) {
  for (let attempt = 0; attempt < 5; attempt++) {
    const res = await fetch("https://verification.didit.me/v3/session/", {
      method: "POST",
      headers: {
        "x-api-key": process.env.DIDIT_API_KEY,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(body),
    });

    if (res.status !== 429) return res;

    const retryAfter = Number(res.headers.get("Retry-After") ?? "1");
    const jitter = Math.random() * 1000;
    await new Promise((r) => setTimeout(r, retryAfter * 1000 + jitter));
  }
  throw new Error("Didit: too many 429s, giving up");
}
```

```python theme={null}
import os
import random
import time

import httpx

def post_session(body: dict) -> httpx.Response:
    with httpx.Client(timeout=15) as client:
        for _ in range(5):
            resp = client.post(
                "https://verification.didit.me/v3/session/",
                headers={
                    "x-api-key": os.environ["DIDIT_API_KEY"],
                    "Content-Type": "application/json",
                },
                json=body,
            )
            if resp.status_code != 429:
                return resp
            wait_s = float(resp.headers.get("Retry-After", "1")) + random.random()
            time.sleep(wait_s)
    raise RuntimeError("Didit: too many 429s, giving up")
```

## هل تحتاج إلى سعة أكبر؟

إذا كان 600 جلسة في الدقيقة غير كافٍ، كأن تكون بصدد حملة تسويقية أو استكمال بيانات، فراسل [support@didit.me](mailto:support@didit.me) مع معرّف `application` الخاص بك (UUID) ومعدلات الاستخدام المستمرة والمتوقعة ومعدلات الاندفاع المتوقعة. تُضبط التجاوزات لكل تطبيق على حدة.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.