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

# Límites de solicitudes API: encabezados y backoff 429

> Didit aplica límites de solicitudes por endpoint: encabezados X-RateLimit, backoff con Retry-After y manejo de 429 para tráfico de producción.

Didit aplica varias capas de límites de solicitudes para mantener la estabilidad de la API sin dejar de admitir picos de tráfico razonables. Los límites se aplican **por identificador**: tu `x-api-key` o, si no se envía ninguna clave de API, la IP de tu cliente. Cada ámbito mantiene un contador independiente con una ventana deslizante de 60 segundos.

## Identificador y exenciones

El middleware elige el identificador para el límite de solicitudes en este orden:

1. Encabezado `x-api-key` → contador por clave.
2. Primer salto de `X-Forwarded-For` → contador por IP.
3. `REMOTE_ADDR` → contador por IP.

Las siguientes llamadas **quedan totalmente exentas** de los límites de solicitudes:

* La clave interna `DIDIT_WEBAPP_API_KEY` (la usa únicamente la consola de Didit).
* Los endpoints protegidos por el permiso `IsValidSession` (el tráfico de sesiones integradas o del SDK).
* `GET /system/healthcheck` (lo atiende `HealthMiddleware` antes de esta capa).
* Los webhooks de proveedores: Stripe, análisis AML, callbacks de verificación facial y modificación de listas KYC.

## Límites globales

Cada solicitud autenticada se agrupa además según su método HTTP en un ámbito genérico, de modo que cualquier ruta nueva hereda un valor predeterminado razonable:

| Ámbito | Método(s) HTTP | Límite | Ventana |
| - | - | - | - |
| `generic-get` | `GET` | 600 / min | 60 s |
| `generic-write` | `POST`, `PATCH`, `DELETE` | 300 / min | 60 s |

Los límites específicos de cada endpoint (siguiente sección) se aplican **además** del límite global. El primer ámbito cuyo contador supera el límite es el que devuelve `429`.

## Límites específicos por endpoint

El middleware define ámbitos más estrictos para las rutas de mayor impacto:

| Ámbito | Endpoint(s) | Límite | Constante de origen |
| - | - | - | - |
| `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/` lo atiende `SessionV3RetrieveView` y **no** pertenece al ámbito `session-decision`. Solo está sujeto al límite máximo de 600/min de `generic-get`. La limitación de 100/min para decisiones se aplica a las rutas de decisión v1 y v2.

> [!WARNING]
>
> El código también define `FREE_SESSION_RATE_LIMIT = 10` y `PAID_SESSION_RATE_LIMIT = 600`. Estas constantes **no se aplican actualmente** a `POST /v3/session/`: ese endpoint usa el límite único de `session-v2-create`, de 600/min, para todas las aplicaciones. No planifiques la capacidad en función de una limitación del plan gratuito por flujo de verificación hasta que se documente como activa.

## Formato de la respuesta 429

Cuando superas el límite de cualquier ámbito, recibes una respuesta HTTP `429` con este cuerpo y estos encabezados:

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

| Encabezado | Significado |
| - | - |
| `X-RateLimit-Limit` | Número máximo de solicitudes permitidas en la ventana actual para el ámbito que se ha superado. |
| `X-RateLimit-Remaining` | Solicitudes restantes antes de que se reinicie la ventana. Se fija en `0` en cuanto superas el límite. |
| `X-RateLimit-Reset` | Segundos desde la época Unix en los que el contador vuelve a cero. |
| `Retry-After` | Segundos que debes esperar antes del siguiente intento. Aparece **solo en las respuestas `429`**, no en todas las solicitudes. |

Los encabezados `X-RateLimit-*` solo se envían en la respuesta `429`: las respuestas exitosas no los incluyen actualmente. Usa el valor de `Retry-After` (o `X-RateLimit-Reset - now`) como tiempo de espera antes del siguiente intento.

## Recomendaciones para el cliente

* **Respeta `Retry-After`** como valor mínimo de tu espera. Por encima de él, aplica un backoff exponencial con jitter (`5s → 10s → 20s → 40s ± random`) para que dos clientes que reciben `429` al mismo tiempo no se sincronicen.
* **Deja de reintentar** tras un número reducido de intentos (por ejemplo, 5). Devuelve el error a quien hizo la llamada y avisa a tu equipo: los 429 sostenidos suelen indicar un bucle descontrolado, no un pico pasajero.
* **Usa un identificador por cada carga de trabajo.** Compartir una `x-api-key` entre muchos backends los agrupa en el mismo contador, lo que puede hacer que un vecino ruidoso deje sin capacidad al resto. Crea una aplicación independiente (y, por tanto, una `api_key` independiente) por entorno, marca o cliente: consulta [Registro programático](https://docs.didit.me/es/integration/programmatic-registration.md#gestionar-aplicaciones).
* **Almacena en caché las lecturas de decisiones.** Los endpoints `/decision/` no están pensados para bucles de sondeo intensivos; si necesitas actualizaciones en tiempo real, usa [Webhooks](https://docs.didit.me/es/integration/webhooks.md) y llama a `GET /v3/session/<id>/decision/` solo para completar datos faltantes o para auditoría.
* **Genera los PDF de forma diferida.** La generación de PDF consume mucha CPU (límite de 50/min). Lánzala desde una cola cuando la sesión alcance un estado terminal, no en cada visualización de página.

## Ejemplo de cliente que gestiona el 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")
```

## Cómo obtener más margen

Si 600 sesiones por minuto no te bastan, por ejemplo durante un lanzamiento de marketing o una carga retroactiva de datos, envía un correo a [support@didit.me](mailto:support@didit.me) con el UUID de tu `application` y las tasas sostenidas y de pico que esperas. Los ajustes de límites se configuran por aplicación.


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