Cada rechazo de la API REST de gestión es un cuerpo JSON con un código de estado HTTP. Esta página explica cómo interpretarlo, qué límites existen y cómo debe reaccionar tu integración.
El cuerpo del error
La mayoría de los errores tienen este aspecto:
{
"message": "The device limit of this account is reached.",
"code": "FORBIDDEN",
"details": { "key": "max_devices", "limit": 43, "current": 43, "adding": 1 }
}
| Campo | Significado |
|---|---|
message | Una frase en inglés para personas. No la analices: puede cambiar. |
code | Un código estable para el tipo de error (tabla siguiente) |
details | Presente cuando el cliente puede actuar con más información: reason (un código estable para máquinas), key (un límite del plan), keys, issues |
Variantes:
| Caso | Cuerpo |
|---|---|
| Un cuerpo o una consulta que incumple una regla de campo | 400 { "message": "Validation error", "issues": [{ "path": ["name"], "message": "…" }] } |
| Algunas rutas comprueban el cuerpo por sí mismas | 400 { "message": "<the problems, joined with commas>" } |
| JSON mal formado | 400 { "message": "Malformed request body" } |
| Un error inesperado del servidor | 500 { "message": "Internal server error" } (la causa se registra en nuestro lado y no se muestra) |
Bifurca primero por el código de estado y después por code y details.reason. Cada referencia de ruta enumera sus motivos.
Códigos de estado
| Estado | Significado | Qué hacer |
|---|---|---|
| 200, 201 | Hecho (201: creado) | |
| 202 | Aceptado, todavía en curso (un comando) | Lee el resultado más tarde |
| 400 | La solicitud no es válida | Corrige la solicitud; no la reintentes tal cual |
| 401 | No hay una credencial válida: «Invalid or expired API key.» | Comprueba la clave; no reintentes |
| 403 | La clave o su titular no pueden hacer esto, la clave no está permitida desde esta dirección, o se ha alcanzado un límite del plan | Comprueba los permisos de la clave y el rol del titular; para details.key, el plan |
| 404 | No encontrado, o no visible para ti | Comprueba el id y los permisos de la clave |
| 405 | El método no está permitido aquí (por ejemplo, escribir atributos de cliente) | |
| 409 | Un conflicto: un duplicado, un registro todavía en uso, un estado que no permite la acción, o un cambio hecho por otra persona | Vuelve a leer y luego decide |
| 412 | El registro ha cambiado desde que lo leíste (If-Match) | Vuelve a leer y aplica de nuevo tu cambio |
| 413 | El cuerpo o el archivo es demasiado grande | Envía menos |
| 415 | Content-Type incorrecto (servidores MCP) | Envía application/json |
| 422 | El contenido incumple una regla de negocio | Corrige el contenido |
| 428 | Falta una cabecera de precondición obligatoria (If-Match) | Envíala |
| 429 | Un límite de frecuencia | Espera Retry-After segundos y reintenta |
| 500 | Un error de nuestro lado | Reintenta más tarde con backoff |
| 503 | No disponible temporalmente, por ejemplo porque tu organización se está trasladando: «This workspace is read-only while it is being moved. Please try again later.» (code TENANT_READ_ONLY, Retry-After: 300) | Reintenta después de Retry-After; las lecturas siguen funcionando |
Códigos
code | Estado típico |
|---|---|
VALIDATION_ERROR | 400, 413, 422, 428 |
UNAUTHORIZED | 401 |
FORBIDDEN | 403 |
FEATURE_NOT_AVAILABLE | 403: tu plan no incluye la función |
TENANT_SUSPENDED, TENANT_INACTIVE | 403: tu organización no está activa |
TENANT_IP_NOT_ALLOWED | 403: las reglas de IP de tu organización rechazan esta dirección |
NOT_FOUND | 404 |
CONFLICT | 409, 412 |
RATE_LIMIT_EXCEEDED | 429 |
INTERNAL_ERROR | 500, 503 |
Límites del plan
Un límite del plan es un 403 con details.key que indica el límite, details.limit su valor y details.current tu recuento actual, por ejemplo max_devices al crear un dispositivo. Consulta Uso y cuotas y Planes y el complemento de IoT.
Límites de frecuencia
Varios límites se aplican a cada llamada con clave de API. Cada uno responde 429 cuando se agota.
| Límite | Por defecto | Ámbito | Mensaje |
|---|---|---|---|
| Límite por dirección | 120 solicitudes por minuto, hasta 120 a la vez | Por dirección IP que llama | «Too many requests. Please try again later.» |
| Límite del plan | Definido por tu plan (ninguno si el plan no define ninguno) | Por organización, todas las claves y los miembros con sesión iniciada juntos | «Your plan’s API rate limit has been reached. Please try again shortly or upgrade your plan.» |
| Límite de clave | 1.000 llamadas por minuto de reloj (Per-Key Rate Limit en la configuración de la clave de API) | Por clave | «API key rate limit exceeded. Please slow down.» |
Algunas rutas añaden los suyos:
| Ruta | Límite |
|---|---|
| Comandos | 10 por dispositivo por minuto («Too many commands to this device; try again in a minute.», Retry-After: 60); 600 por organización por minuto («Too many requests for your plan. Please upgrade or try again later.», X-RateLimit-Scope: tenant-plan) |
| Escrituras de ajustes de dispositivo | 600 por organización por minuto |
| Servidores MCP | 120 llamadas de herramienta de lectura y 20 de escritura por credencial por minuto |
Cabeceras en un 429
| Cabecera | Significado |
|---|---|
Retry-After | Segundos hasta que una llamada vuelva a admitirse |
RateLimit-Limit, X-RateLimit-Limit | El límite por minuto |
RateLimit-Remaining, X-RateLimit-Remaining | Llamadas restantes (0) |
RateLimit-Reset | Segundos hasta que el cubo vuelva a estar lleno |
No todos los 429 llevan todas las cabeceras. Si falta Retry-After, espera 60 segundos.
El límite por dirección se recarga de forma continua: vuelve una llamada cada medio segundo, así que un ritmo constante de 2 llamadas por segundo nunca lo alcanza. Reparte las ráfagas en lugar de enviarlas todas a la vez.
Reintentos seguros
- Reintenta solo
429,500,502,503y504, y los errores de red. - Espera
Retry-Aftercuando esté presente; si no, aplica backoff exponencial (1, 2, 4, 8 … segundos, hasta un minuto) con algo de jitter aleatorio. - Desiste tras unos pocos intentos y registra el último cuerpo.
- Antes de reintentar un
POSTque crea algo (un dispositivo, un comando), comprueba si la primera llamada surtió efecto: lista o lee primero. UnPOSTde comando no tiene clave de idempotencia. - Nunca reintentes sin cambios
400,401,403,404o422.
async function call(url, init, tries = 5) {
for (let i = 0; ; i++) {
const res = await fetch(url, init);
if (res.ok || i === tries - 1 || ![429, 500, 502, 503, 504].includes(res.status)) return res;
const wait = Number(res.headers.get('Retry-After')) || Math.min(60, 2 ** i) + Math.random();
await new Promise((r) => setTimeout(r, wait * 1000));
}
}
Errores que quizá veas primero
| Ves | Causa probable |
|---|---|
401 en cada llamada | Una errata en la clave, una clave caducada o revocada, o una clave de otra organización (el tenantId de la ruta debe ser el de la clave) |
403 «API key does not have the required permission.» | La clave no tiene el permiso de la ruta. Edita los Module permissions de la clave. |
403 «You are not permitted to perform this action» | La clave tiene el permiso, pero su titular ya no (cambió el rol, se quitó el grupo, política de acceso) |
403 «This API key may not be used from the current IP address.» | La lista de IP permitidas de la clave, o la lista predeterminada de la organización |
404 para un registro que sí ves en la aplicación | El titular de la clave lo ve mediante un permiso que la clave no tiene, o el id pertenece a otra organización |
429 solo desde un servidor | El límite por dirección: reparte las llamadas o usa menos páginas, pero más grandes |

