
En resumen
El código de estado forma parte del contrato de tu API: dice al cliente si debe reintentar, reautenticarse, corregir la petición o mostrar un error. Elegirlo mal rompe reintentos y cachés.
Casi todos los errores de API que sobreviven a la revisión no son errores de lógica: son errores de código de estado. Un handler devuelve 200 con un cuerpo { "error": ... } y, de repente, el cliente móvil muestra una pantalla de éxito, la CDN cachea el fallo diez minutos y la biblioteca de reintentos nunca se activa. La línea de estado es la parte que lee la infraestructura; el cuerpo es lo que lee tu aplicación.
Esta guía recorre los códigos entre los que de verdad eliges, en el orden en que decides. Puedes consultar cualquier código en la referencia de códigos de estado HTTP de UrsaKit, que enlaza cada uno con su RFC.
01El código de estado es un contrato, no una etiqueta
Un código de estado tiene tres lectores, y solo uno es tu aplicación:
- Las cachés deciden si almacenan la respuesta según 2xx, 4xx o 5xx y cabeceras como
Cache-Control. - Los proxies y pasarelas tratan un 5xx como fallo del origen, lo que puede abrir un cortacircuitos.
- Las bibliotecas cliente deciden si reintentan, refrescan un token o muestran un error al usuario.
HTTP/1.1 201 Created
Location: /v1/orders/8f14e45
Content-Type: application/json
{ "id": "8f14e45", "status": "pending" }
Nadie analiza esa línea por diversión: la analiza porque la lógica de reintentos depende de ella.
022xx: lo que el cliente puede dar por hecho
| Código | Úsalo para | Cuidado con |
|---|---|---|
200 OK | Lectura, actualización o acción correcta que devuelve cuerpo | No lo uses para un fallo |
201 Created | Un POST que creó un recurso | Incluye Location con la URL nueva |
202 Accepted | Trabajo en cola sin terminar (jobs, exportaciones, webhooks) | Devuelve una URL de estado que el cliente pueda consultar |
204 No Content | Éxito sin nada que devolver (DELETE, PUT sin cuerpo) | El cuerpo debe estar vacío |
206 Partial Content | Peticiones de rango para medios | Requiere Content-Range |
La distinción que merece la pena acertar es 200 frente a 202. Si el cliente tiene que consultar el estado, la petición no ha terminado, y 202 lo dice antes de que el cliente se pregunte si la escritura se perdió.
033xx: redirecciones y caché
| Código | Significado | Método tras la redirección |
|---|---|---|
301 Moved Permanently | Mudanza permanente | Los navegadores pueden cambiar POST por GET |
302 Found | Mudanza temporal | Los navegadores pueden cambiar POST por GET |
307 Temporary Redirect | Mudanza temporal | Método y cuerpo se conservan |
308 Permanent Redirect | Mudanza permanente | Método y cuerpo se conservan |
304 Not Modified | La petición condicional coincide | Sin cuerpo, reutiliza la copia en caché |
Para endpoints de API, prefiere 307 o 308: son los dos códigos que garantizan que el método y el cuerpo sobreviven al salto. 301 y 302 tienen una larga historia de ser reescritos a GET por los clientes.
044xx: el problema es la petición del cliente
Aquí viven casi todas las decisiones de diseño, porque "petición incorrecta" es una familia, no un código.
| Código | Úsalo cuando | Qué corrige el cliente |
|---|---|---|
400 Bad Request | La petición está mal formada: JSON roto, tipos erróneos, campo requerido ausente | Corrige el cuerpo |
401 Unauthorized | Sin credenciales, o token caducado o inválido | Vuelve a autenticarse; debe enviar WWW-Authenticate |
403 Forbidden | Autenticado, pero sin permiso | Pide acceso; no reintentes igual |
404 Not Found | El recurso no existe o no debe revelarse | Deja de reintentar |
405 Method Not Allowed | La ruta existe, pero no para ese método | Usa otro método; debe enviar Allow |
406 Not Acceptable | Ninguna representación encaja con Accept | Pide un tipo soportado |
409 Conflict | Conflicto de estado: clave duplicada, versión desfasada | Relee y reintenta con el estado nuevo |
410 Gone | El recurso existió y ya no está | Quítalo de marcadores e índices |
412 Precondition Failed | Falló If-Match o If-Unmodified-Since | Vuelve a obtener y reintenta |
413 Content Too Large | La subida supera el límite | Envía menos contenido |
415 Unsupported Media Type | El Content-Type no se acepta | Envía JSON, no formulario |
422 Unprocessable Content | Sintaxis válida, semántica inválida | Corrige los valores |
429 Too Many Requests | Se superó el límite de peticiones | Espera Retry-After |
Tres de ellos llevan una cabecera obligatoria, y omitirla convierte una respuesta útil en una confusa: 401 necesita WWW-Authenticate, 405 necesita Allow y 429 debería enviar Retry-After.
401 y 403 no son intercambiables.
401 significa "no sé quién eres" y el cliente debe autenticarse. 403 significa "sé quién eres y aun así no puedes" — volver a autenticarse no ayuda, así que un cliente que los trata igual acaba en un bucle de login.
La duda entre 403 y 404 es una decisión de seguridad. Si la existencia del recurso ya es sensible —el pedido de otro inquilino, un documento sin publicar— devuelve 404 para que la respuesta no sirva para enumerar. Si el cliente puede saber que existe pero no tocarlo, 403 es más honesto.
055xx: falló el servidor o sus dependencias
| Código | Significado | ¿Reintentable? |
|---|---|---|
500 Internal Server Error | Excepción no controlada | Normalmente no; la misma petición volverá a fallar |
501 Not Implemented | El servidor no soporta el método en absoluto | No |
502 Bad Gateway | El upstream devolvió algo inutilizable | A veces |
503 Service Unavailable | Sobrecarga o mantenimiento | Sí, respetando Retry-After |
504 Gateway Timeout | El upstream no respondió a tiempo | A veces, con presupuesto de timeout |
Devuelve 503 con Retry-After durante mantenimientos y sobrecargas. Sin esa cabecera, los clientes bien comportados recurren al retroceso exponencial, y prácticas como usar HTTP 402 para pagos entre agentes existen precisamente porque las señales legibles por máquina superan a adivinar.
Nunca envuelvas un fallo real en un 200.
Un 200 con un objeto de error rompe la caché, oculta el fallo a la monitorización y desactiva la lógica de reintentos que ya existe en cualquier cliente HTTP. Si la respuesta lleva un error, el código de estado debe decirlo.
06Errores habituales que conviene corregir
| Error | Por qué duele | Solución |
|---|---|---|
| 200 para todo | Monitorización, cachés y reintentos ven éxito | Usa el código que corresponde al resultado |
| 401 para errores de permiso | El cliente borra la sesión y vuelve al login | Usa 403 |
| 400 para toda validación | El cliente no distingue "arregla la sintaxis" de "arregla los valores" | 400 sintaxis, 422 semántica |
| 500 para errores del cliente | Se avisa a guardia por un fallo del llamante | Asigna los fallos de validación a 4xx |
| 404 para un método incorrecto | El cliente no puede descubrir el método permitido | 405 con Allow |
429 sin Retry-After | Los clientes adivinan y se agolpan | Envía la cabecera |
| 204 con cuerpo | Proxies y clientes discrepan del enmarcado | Devuelve 200 o quita el cuerpo |
07Decidir con tres preguntas
Cuando un handler necesita un código, pregunta en este orden:
- ¿Hizo el servidor lo que se le pidió? Si sí, el código es 2xx: 200, 201, 202 o 204 según si se creó, se encoló o se devolvió algo.
- ¿Es culpa del llamante? Petición mal formada es 400; credenciales ausentes o inválidas es 401; autenticado pero sin permiso es 403; recurso inexistente es 404; conflicto de estado es 409; sintaxis válida con valores inválidos es 422; demasiadas peticiones es 429.
- ¿Es culpa nuestra? Un bug es 500, un upstream inutilizable es 502, un servicio sobrecargado o en mantenimiento es 503 con
Retry-After.
Después revisa los detalles del contrato: Location en 201, Allow en 405, WWW-Authenticate en 401, Retry-After en 429 y 503, y cuerpo vacío en 204.
Usa la referencia de códigos de estado HTTP para confirmar el código antes de publicarlo: la descripción está escrita para quien decide, no para un abogado de la especificación. Y si el cuerpo de error es JSON, pásalo también por el validador JSON: una respuesta de error que no se puede analizar es peor que ninguna respuesta de error.
Todos los códigos 1xx–5xx con una explicación clara y la RFC de la que vienen.
Códigos de estado HTTPPreguntas frecuentes
¿Debe una API devolver 404 o 403 para un recurso al que el usuario no accede?
Devuelve 404 cuando revelar que existe ya filtra información, la opción habitual con datos por inquilino. Devuelve 403 cuando el cliente ya sabe que el recurso existe y solo le falta permiso.
¿Es aceptable un 200 con un cuerpo de error?
No. Un 200 dice a cada intermediario, caché y biblioteca de reintentos que la llamada tuvo éxito. El cliente tiene que analizar el cuerpo para descubrir el fallo y la monitorización nunca lo ve.
¿Cuándo uso 422 en lugar de 400?
Usa 400 cuando la petición está mal formada: JSON roto, campo obligatorio ausente, tipo incorrecto. Usa 422 cuando la sintaxis es válida pero la semántica no, como una fecha de fin anterior a la de inicio.
¿Qué diferencia hay entre 401 y 407?
401 significa que la petición no lleva credenciales válidas para el recurso y debe incluir la cabecera WWW-Authenticate. 407 es el equivalente para el proxy y usa Proxy-Authenticate.
¿Qué códigos debería reintentar un cliente?
408, 425, 429 y 503 son reintentables de forma explícita, respetando Retry-After. 500 y 502 suelen indicar un fallo del servidor, así que reintentar repite el mismo error.
¿Puede una respuesta 204 llevar cuerpo?
No. 204 significa sin contenido, así que el cuerpo debe estar vacío. Si necesitas devolver el objeto eliminado, usa 200 con cuerpo.
Artículos relacionados
- Cómo formatear, validar y minificar JSON
Mantén el cuerpo de error tan legible por máquinas como el de éxito.
- Compara dos textos con un diff
Compara dos respuestas de error para encontrar el campo que cambió.
Herramientas relacionadas
- Códigos de estado HTTP
Consulta cualquier código y qué hace el cliente con él.
- Formateador y validador JSON
Valida el cuerpo que acompaña al código.
- Codificador y decodificador Base64
Inspecciona cabeceras de autorización y tokens.
- Probador de expresiones regulares
Filtra y reescribe líneas de log por código de estado.