> ## 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.

# Limite de débit API : en-têtes et réessai après 429

> Gérez les limites de débit de l'API Didit : en-têtes X-RateLimit, Retry-After et gestion des erreurs 429 pour sécuriser vos flux en production.

Didit applique plusieurs niveaux de limitation de débit afin de préserver la stabilité de l'API tout en autorisant des pics de trafic normaux. Les limites sont appliquées **par identifiant** — votre `x-api-key` ou, si aucune clé API n'est transmise, l'adresse IP de votre client. Chaque portée conserve un compteur indépendant avec une fenêtre glissante de 60 secondes.

## Identifiant et exemptions

Le middleware sélectionne un identifiant de limite de débit dans cet ordre :

1. En-tête `x-api-key` → compteur associé à la clé.
2. Premier saut de `X-Forwarded-For` → compteur associé à l'IP.
3. `REMOTE_ADDR` → compteur associé à l'IP.

Les appels suivants **contournent** entièrement la limitation de débit :

* La clé interne `DIDIT_WEBAPP_API_KEY` (utilisée uniquement par la console Didit).
* Les endpoints protégés par l'autorisation `IsValidSession` (le trafic de session intégré/SDK).
* `GET /system/healthcheck` (traité par `HealthMiddleware` en amont de cette pile).
* Les webhooks des fournisseurs : Stripe, filtrage AML, rappels de vérification faciale, modification de listes KYC.

## Limites globales

Chaque requête authentifiée est également répartie par méthode HTTP dans une portée générique, de sorte que toute nouvelle route hérite d'une valeur par défaut appropriée :

| Portée | Méthode(s) HTTP | Limite | Fenêtre |
| - | - | - | - |
| `generic-get` | `GET` | 600 / min | 60 s |
| `generic-write` | `POST`, `PATCH`, `DELETE` | 300 / min | 60 s |
| `ingest-write` | `POST /v3/transactions/`, `POST /v3/events/`, `POST /v3/events/batch/` | 600 / min | 60 s |

Les créations de transactions et l'ingestion d'événements sont décomptées dans `ingest-write` **au lieu de** `generic-write` : une intégration d'ingestion à volume élevé bénéficie de 600 requêtes par minute et par clé API sans consommer le quota de ses autres écritures.

Les limites spécifiques aux endpoints (section suivante) s'appliquent **en plus** de la limite globale. La première portée dont le compteur est dépassé est celle qui renvoie une erreur `429`.

## Limites spécifiques aux endpoints

Le middleware définit des portées plus strictes pour les routes à plus fort impact :

| Portée | Endpoint(s) | Limite | Constante source |
| - | - | - | - |
| `session-v2-create` | `POST /v2/session/`, `POST /v3/session/` | 600 / min | `SESSION_CREATE_RATE_LIMIT` |
| `session-decision` | `GET /v1/session/<id>/decision/`, `GET /v2/session/<id>/decision/` | 100 / min | `SESSION_DECISION_RATE_LIMIT` |
| `session-generate-pdf` | `GET /v1/session/<id>/generate-pdf/`, `GET /v3/session/<id>/generate-pdf/` | 50 / min | `SESSION_PDF_RATE_LIMIT` |
| `session-add-images` | `POST`/`PATCH /session/<id>/add-images/` | 10 / min | `SESSION_ADD_IMAGES_RATE_LIMIT` |
| `session-update-kyc` | `POST`/`PATCH /session/<id>/update-data/` | 10 / min | `SESSION_UPDATE_KYC_RATE_LIMIT` |
| `session-update-poa` | `POST`/`PATCH /session/<id>/update-poa-data/` | 10 / min | `SESSION_UPDATE_POA_RATE_LIMIT` |

> [!NOTE]
>
> `GET /v3/session/<id>/decision/` est traité par `SessionV3RetrieveView` et ne fait **pas** partie de la portée `session-decision`. Il est uniquement régi par le plafond de 600/min de `generic-get`. La restriction de 100/min sur les décisions s'applique aux chemins de décision v1 et v2.

> [!WARNING]
>
> La base de code définit également `FREE_SESSION_RATE_LIMIT = 10` et `PAID_SESSION_RATE_LIMIT = 600`. Ces constantes ne sont **actuellement pas appliquées** à `POST /v3/session/` — cet endpoint utilise l'unique limite `session-v2-create` de 600/min pour chaque application. Ne planifiez pas votre capacité en fonction d'une restriction d'offre gratuite par workflow tant qu'elle n'est pas documentée comme active.

## Format de la réponse 429

Lorsque vous dépassez une portée quelconque, vous recevez une réponse HTTP `429` avec ce corps et ces en-têtes :

```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."
}
```

| En-tête | Signification |
| - | - |
| `X-RateLimit-Limit` | Nombre maximal de requêtes autorisées dans la fenêtre actuelle pour la portée concernée. |
| `X-RateLimit-Remaining` | Nombre de requêtes restantes avant la réinitialisation de la fenêtre. Ramené à `0` dès que la limite est dépassée. |
| `X-RateLimit-Reset` | Temps Unix (en secondes) où le compteur se réinitialise à zéro. |
| `Retry-After` | Nombre de secondes à attendre avant la tentative suivante, arrondi au chiffre supérieur pour que l'attente aboutisse systématiquement dans la fenêtre suivante. Présent **uniquement sur les réponses `429`**, et non sur chaque requête. |

Les en-têtes `X-RateLimit-*` ne sont émis que sur la réponse `429` — les réponses réussies ne les incluent pas actuellement. Utilisez la valeur `Retry-After` (ou `X-RateLimit-Reset - now`) comme délai avant la tentative suivante.

## Conseils pour les clients

* **Respectez `Retry-After`** comme plancher pour votre délai d'attente. Par-dessus, ajoutez une temporisation exponentielle avec gigue (`5s → 10s → 20s → 40s ± random`) afin que deux clients recevant une réponse `429` au même moment ne se synchronisent pas.
* **Arrêtez les tentatives** après un nombre limité de tentatives (par ex. 5 tentatives). Renvoyez l'erreur à votre appelant et alertez votre équipe — des erreurs 429 continues indiquent généralement une boucle incontrôlée et non un pic passager.
* **Utilisez un identifiant par charge de travail.** Partager une `x-api-key` entre plusieurs backends les rassemble sous le même compteur, ce qui peut amener un voisin bruyant à priver les autres de ressources. Créez une application distincte (et donc une `api_key` distincte) par environnement, marque ou client — voir [Enregistrement programmatique](https://docs.didit.me/fr/integration/programmatic-registration.md#gestion-des-applications).
* **Mettez en cache les lectures de décisions.** Les endpoints `/decision/` ne sont pas conçus pour des boucles d'interrogation intensives ; si vous avez besoin de mises à jour en temps réel, utilisez les [Webhooks](https://docs.didit.me/fr/integration/webhooks.md) et n'appelez `GET /v3/session/<id>/decision/` que pour du rattrapage ou de l'audit.
* **Générez les PDF hors ligne.** Le rendu de PDF consomme beaucoup de CPU (limite de 50/min). Déclenchez-le depuis une file d'attente une fois que la session a atteint un statut terminal, et non à chaque consultation de page.

## Exemple de client prenant en compte les erreurs 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")
```

## Besoin d'une marge supérieure ?

Si 600 sessions par minute ne suffisent pas — par exemple, lors d'un lancement marketing ou d'un rattrapage de données —, envoyez un e-mail à [support@didit.me](mailto:support@didit.me) avec l'UUID de votre `application` ainsi que les débits continus et de pointe prévus. Les dérogations sont configurées par application.


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