Errores y reintentos
Códigos de estado HTTP, el envelope de error de Mono, y cómo reintentar solicitudes fallidas de forma segura.
Cada respuesta de Mono lleva un código de estado HTTP. Las llamadas exitosas devuelven 2xx; las fallas devuelven 4xx (tu solicitud estaba mal) o 5xx (algo salió mal del lado de Mono). Esta página documenta los códigos de estado que usa Mono, el envelope de error que puedes esperar en cada falla, y la política de reintentos que convierte fallas transitorias en éxito eventual sin duplicar efectos secundarios.
Los límites de tasa aún no se aplican uniformemente en todos los endpoints de Mono, pero 429
va a empezar a aparecer a medida que los ajustemos. Las reglas de reintento de abajo describen
el contrato contra el que deberías programar hoy, para que tu integración ya se comporte
correctamente cuando los límites entren en vigor.
Códigos de estado HTTP
| Estado | Significado | ¿Reintentable? |
|---|---|---|
| 200 (OK) | La solicitud se completó correctamente. | n/a |
| 201 (Created) | Se creó un nuevo recurso. | n/a |
| 204 (NoContent) | La solicitud tuvo éxito; no hay cuerpo que devolver. | n/a |
| 400 (BadRequest) | JSON malformado, campo faltante o header no conforme. La solicitud en sí está mal. | No |
| 401 (Unauthorized) | Header Authorization faltante o inválido. | No (corrige credencial) |
| 403 (Forbidden) | Autenticado, pero sin scope para este recurso. | No (corrige scope) |
| 404 (NotFound) | El recurso no existe. | No |
| 408 (RequestTimeout) | Mono no recibió la solicitud completa a tiempo. | Sí, con backoff |
| 409 (Conflict) | Colisión de clave de idempotencia o conflicto de estado — ver Claves de idempotencia. | No (lógica de aplicación) |
| 422 (UnprocessableEntity) | La estructura de la solicitud es válida, pero el contenido fue rechazado (validación de negocio). | No |
| 429 (TooManyRequests) | Límite de tasa excedido. Respeta el header Retry-After. | Sí, después de Retry-After |
| 500 (InternalServerError) | Falla inesperada del lado del servidor. | Sí, con backoff |
| 502, 503, 504 | Fallas transitorias upstream/gateway. | Sí, con backoff |
Envelope de error
Cada respuesta de falla lleva un envelope JSON estándar para que los clientes puedan parsear errores de forma uniforme:
{
"code": "400 Bad Request",
"errors": [
{
"error_code": "item_not_found",
"message": "Item doesn't exist",
"path": "#/item/id",
"url": "https://api.mono.co/docs#errors"
}
],
"id": "log_7MkWaFqvfosB8fzHhb1Eql",
"message": "Malformed request"
}Campos:
code— el estado HTTP y su frase de razón (por ejemplo,"404 Not Found").errors[]— una entrada por cada falla de validación. Útil cuando una sola solicitud tiene varios campos inválidos.error_code— código estable y legible por máquina para esta falla específica.message— descripción humana corta.path— puntero JSON al payload de la solicitud que identifica el campo ofensor.url— link a documentación extendida para este código de error, cuando esté disponible.
id— identificador de solicitud generado por Mono. Inclúyelo cuando contactes a soporte — nos permite localizar la solicitud exacta en nuestros logs.message— descripción humana de alto nivel apta para mostrar a operadores (no a usuarios finales).
Las respuestas de 429 y otros límites de tasa usan el mismo envelope con error_code: "rate_limited".
Cuándo reintentar
Reintenta solo en fallas transitorias, nunca en errores de aplicación:
| Respuesta | ¿Reintentar? | Por qué |
|---|---|---|
429 | Sí, después de Retry-After. | Transitorio. La solicitud no fue procesada; nada cambió del lado de Mono. |
408, 500–504 | Sí, con backoff exponencial. | Transitorio. La solicitud puede o no haber sido procesada — la clave de idempotencia protege. |
400, 422 | No. | La solicitud en sí está malformada. Reintentar no cambiará la respuesta. |
401, 403 | No. | Problema de credenciales o de scope. Corrige la auth, luego envía una solicitud nueva. |
404, 409 | No. | Recurso faltante o conflicto de estado. La lógica de la aplicación debe reaccionar. |
Cuatro reglas aplican a cada reintento:
- Respeta
Retry-After. Si el header está presente (siempre en429, a veces en503), espera al menos esos segundos antes de reintentar. No reintentes antes. - Usa backoff exponencial con jitter cuando
Retry-Afteresté ausente. Empieza en 1 segundo, dobla en cada falla subsecuente, agrega 0–500 ms de jitter aleatorio, y limita el delay a 30 segundos. El jitter previene el problema de thundering herd donde muchos clientes reintentan en sincronía. - Limita el presupuesto de reintentos. Hasta 5 reintentos por operación lógica es un valor por defecto razonable. Después de eso, surface la falla — reintentar para siempre solo desplaza el problema.
- Reusa la clave de idempotencia. Cada reintento debe llevar la misma
X-Idempotency-Keyque el intento original. De lo contrario, un reintento exitoso tras un5xxambiguo podría cobrarle dos veces a un cliente.
Un bucle de reintento mínimo en Python:
import random
import time
import httpx
RETRYABLE_STATUSES = {408, 429, 500, 502, 503, 504}
def request_with_backoff(client, method, url, **kwargs, max_retries=5):
delay = 1.0
for attempt in range(max_retries + 1):
response = client.request(method, url, **kwargs)
if response.status_code not in RETRYABLE_STATUSES:
return response
retry_after = response.headers.get("Retry-After")
wait = float(retry_after) if retry_after else delay + random.uniform(0, 0.5)
time.sleep(min(wait, 30.0))
delay = min(delay * 2, 30.0)
return response # last response, still failingDiseñar para resiliencia desde el inicio
Incluso antes de que los límites de tasa se vuelvan estrictos, tres hábitos mantienen las cargas en ráfaga fuera de problemas:
- Distribuye el trabajo en ráfaga. Trabajos por lotes que disparan miles de llamadas — corridas de nómina, conciliación de fin de día, emisión masiva de tarjetas — deberían ser ritmados del lado del cliente en lugar de dispararse en paralelo. Unas pocas solicitudes por segundo sostenidas le ganan a mil al tiempo seguidas de una ventana de recuperación.
- Cachea lecturas. Los datos de referencia (catálogos, esquemas de tarifas, lookups de capacidades) rara vez cambian dentro de un solo ciclo de solicitud. Cachea la respuesta y solo pega a Mono cuando falla el cache.
- Separa rutas síncronas de las asíncronas. Una llamada de checkout cara a un usuario no debería compartir su presupuesto con una exportación nocturna. Usa API keys distintas para cargas distintas cuando sea posible, para que un trabajo por lotes desbocado no agote el límite que necesita un cliente real.
Errores comunes
| Error | Síntoma | Solución |
|---|---|---|
Reintentar de inmediato en 429. | 429 repetidos y mayor tiempo total de recuperación. | Espera al menos los segundos de Retry-After antes del siguiente intento. |
Ignorar el header Retry-After. | Los reintentos caen antes de que el límite se haya reseteado. | Trata Retry-After como autoritativo cuando esté presente. |
| Reintentar sin clave de idempotencia. | Transferencias duplicadas cuando el reintento eventualmente acierta. | Genera la clave una vez, envíala en cada intento. |
Reintentar 400/422 como si fueran transitorios. | Intentos desperdiciados; el mismo error vuelve cada vez. | Solo reintenta en 408, 429 y 5xx. |
| Sin tope de reintentos. | Workers atorados, colas bloqueadas, fatiga de alertas. | Limita a un número pequeño de reintentos (5 es un default razonable) y surface arriba. |
| Compartir una llave entre servicios y clases de tráfico. | Un trabajo batch ruidoso limita por tasa el tráfico de checkout cara al usuario. | Emite API keys separadas para cargas separadas. |
Omitir el campo id al reportar un problema. | Soporte no puede encontrar la solicitud en logs. | Siempre incluye el id del envelope de error cuando escales. |
Siguientes pasos
- Claves de idempotencia — requeridas en cada escritura reintentada.
- Autenticación — manejo de credenciales para los casos
401y403. - Webhooks — la política de reintentos de Mono para los eventos que envía a tu sistema.