Una respuesta 500 ante una entrada inválida es siempre un bug: el servidor debería devolver un 4xx, no fallar. Un 403 faltante es un bypass de autorización: si un usuario normal llega a un endpoint de admin y recibe un 200 en lugar de un 403, el control de acceso no está funcionando. Esta guía cubre cada código de estado que vas a encontrar en los tests de API, qué debería y qué no debería devolver cada uno, y ejemplos de Playwright para hacer aserciones de códigos de estado en endpoints de autenticación, validación y creación.

Cómo se organizan los códigos de estado

Los códigos de estado HTTP son números de tres dígitos organizados en cinco clases:

| Rango | Clase | Significado |

|-------|-------|-------------|

| 1xx | Informativo | Request recibido, el procesamiento continúa |

| 2xx | Éxito | Request completado exitosamente |

| 3xx | Redirección | El cliente debe tomar otra acción (normalmente seguir una redirección) |

| 4xx | Error del cliente | El request tiene un problema: el cliente envió algo incorrecto |

| 5xx | Error del servidor | El request era válido: el servidor no pudo procesarlo |

El primer dígito indica la categoría. 4xx = tu request fue incorrecto. 5xx = el servidor falló.

2xx: Códigos de éxito

200 OK

El código de éxito más común. El request funcionó y hay contenido en la respuesta.

Se usa para

Se usa para requests GET que devuelven datos, requests POST que no crean un nuevo recurso (como una búsqueda) y actualizaciones con PUT/PATCH.

Qué verificar en los tests

El cuerpo de la respuesta contiene los datos esperados, la respuesta está en el formato correcto (JSON, XML) y los campos obligatorios están presentes.

201 Created

Un recurso fue creado exitosamente. Debe devolverse para requests POST que crean nuevos ítems.

Qué verificar

La respuesta incluye el recurso creado con su nuevo ID, el header Location suele apuntar a la URL del nuevo recurso y POST /users debe devolver 201 con { id: 123, email: "...", ... }.

Bug común a detectar: La API devuelve 200 en lugar de 201 para los endpoints de creación. Técnicamente funciona, pero es semánticamente incorrecto y puede confundir a los consumidores.

204 No Content

Éxito, pero sin nada que devolver. Se usa para requests DELETE y algunas respuestas de PUT/PATCH.

Qué verificar

El cuerpo de la respuesta debe estar vacío (algunas APIs devuelven un cuerpo incorrectamente con 204) y el recurso fue realmente eliminado (hacer un GET de seguimiento para confirmar).

206 Partial Content

Se devuelve cuando solo se envía una parte de un recurso (descargas fragmentadas, streaming de video). Principalmente relevante para testear descargas de archivos y streaming de medios.

3xx: Códigos de redirección

301 Moved Permanently

El recurso se movió permanentemente a una nueva URL. Los motores de búsqueda actualizan sus índices para los 301.

Relevancia en testing: Verificar que las URLs antiguas redireccionan correctamente, que las redirecciones de HTTPS desde HTTP son 301 (no 302), y que los destinos de la redirección cargan correctamente.

302 Found (Redirección temporal)

El recurso está temporalmente en una URL diferente. Se usa para redirecciones de login ("ve aquí ahora, la URL original sigue siendo válida").

304 Not Modified

El cliente tiene una versión en caché que sigue siendo actual: no se envía contenido, se usa el caché. Lo vas a ver en la pestaña Network del navegador para recursos estáticos.

Relevancia en testing: Si estás testeando el comportamiento del caché, 304 significa que el caché está funcionando. Si ves 304 para datos que deberían haber cambiado, tienes un bug de caché.

4xx: Códigos de error del cliente

Significan que tu request fue el problema: datos incorrectos, autenticación faltante, URL incorrecta.

400 Bad Request

El servidor no pudo entender el request. Generalmente significa sintaxis malformada, JSON inválido o un error de validación.

Cuándo deberías ver 400

JSON inválido en el cuerpo del request, header obligatorio faltante o URL malformada.

Qué verificar en los tests

Devuelve un mensaje de error útil que explica qué está mal, no revela detalles internos (stack traces) y devuelve 400, no 500 (un 500 ante una entrada inválida es un bug del servidor).

401 Unauthorized

El request carece de credenciales de autenticación válidas. "No estás logueado" o "tu token expiró".

Diferencia con 403: 401 = no autenticado (¿quién eres?). 403 = autenticado pero sin permiso (sé quién eres, simplemente no puedes hacer esto).

Qué testear

  • Request sin ningún token → 401
  • Request con token vencido → 401
  • Request con token inválido o falsificado → 401
  • La respuesta de error no revela información sobre tokens válidos

403 Forbidden

Autenticado, pero sin permiso. "Estás logueado, pero no tienes permiso para esto."

Qué testear

Usuario regular accediendo a un endpoint de admin (→ 403), usuario accediendo a los datos privados de otro usuario (→ 403) y que la respuesta no expone ningún dato que el usuario no debería ver.

Relevancia de seguridad: Un 403 faltante significa que el acceso no autorizado funciona: es un bug de seguridad real.

404 Not Found

El recurso solicitado no existe en esa URL.

Qué testear

  • Solicitar un recurso eliminado → 404
  • Solicitar con un ID que nunca fue creado → 404
  • Para recursos sensibles, no debería revelar si el recurso existe (cuentas de usuario: devolver 404 "usuario no encontrado" vs. 403 "prohibido" puede revelar la existencia de la cuenta)
  • ¿Es 404 o 410? (410 = eliminado permanentemente: algunas APIs los distinguen)

405 Method Not Allowed

Usaste el método HTTP incorrecto. GET en un endpoint que solo acepta POST.

Relevancia en testing: Prueba métodos incorrectos intencionalmente: DELETE /users cuando solo debería ser GET. Debería devolver 405, no 500 ni fallo silencioso.

409 Conflict

El request entra en conflicto con el estado actual del recurso.

Usos comunes

Crear un usuario con un email que ya existe, editar un recurso que alguien más ha bloqueado y conflictos de versión (optimistic locking).

Qué testear

Creación duplicada debe devolver 409 (no 200 ni 500) y la respuesta debe explicar el conflicto claramente.

422 Unprocessable Entity

El request era JSON sintácticamente válido, pero semánticamente incorrecto: la validación falló.

Ejemplo: Cuerpo JSON válido, pero age: -5 viola la regla de que la edad debe ser positiva.

400 vs 422

400 es cuando no se puede parsear el request (JSON malo, Content-Type incorrecto); 422 es cuando se parseó bien pero los valores fallan la validación.

Muchas APIs usan 400 para los dos casos. Lo que importa es la consistencia: elige uno y úsalo correctamente en toda la API.

Qué testear

Cada campo obligatorio faltante (→ 422 con el nombre específico del campo en el error), valores de campo fuera del rango válido (→ 422) y violaciones de reglas de negocio (→ 422).

429 Too Many Requests

Rate limiting: enviaste demasiados requests en muy poco tiempo.

Qué testear

Confirmar que se devuelven headers de rate limit (X-RateLimit-Limit, X-RateLimit-Remaining, Retry-After), que después de alcanzar el límite los requests se rechazan y que después de que vence la ventana los requests vuelven a funcionar.

Relevancia de seguridad: Los endpoints de login deberían aplicar rate limiting para prevenir ataques de fuerza bruta. Testear que se devuelve 429 después de X intentos fallidos de login.

5xx: Códigos de error del servidor

Son siempre bugs. El request era válido, el servidor no lo pudo manejar.

500 Internal Server Error

Fallo genérico del servidor. Ocurrió algo inesperado.

En testing: Si puedes reproducir un 500 de forma confiable, es un bug, aunque la entrada haya sido intencionalmente incorrecta. Un buen diseño de API significa que la entrada inválida devuelve 4xx, no 500.

Qué testear

La entrada inválida nunca debería producir 500 (debería producir 400/422), los campos opcionales nulos o faltantes no deberían producir 500 y los datos en casos límite (arrays vacíos, strings muy largos) tampoco.

502 Bad Gateway

El servidor estaba actuando como gateway o proxy y recibió una respuesta inválida de un servidor upstream. Generalmente significa que una dependencia está caída.

503 Service Unavailable

El servidor no está disponible temporalmente: sobrecargado o en modo de mantenimiento.

Relevancia en testing: Verificar cómo maneja el frontend un 503. ¿Muestra una página de error útil? ¿Reintenta de forma inteligente? ¿Degrada de forma elegante?

504 Gateway Timeout

Similar al 502: un gateway expiró esperando un servidor upstream. Aparece frecuentemente bajo alta carga o cuando un servicio de terceros es lento.

Tabla de referencia rápida

| Código | Significado | Causa común en bugs |

|--------|-------------|---------------------|

| 200 | OK | Devuelto para creaciones que deberían ser 201 |

| 201 | Created | Faltante: la API devuelve 200 para POST |

| 204 | No Content | Se devuelve un cuerpo cuando no debería |

| 301 | Redirect permanente | Tipo de redirección incorrecto |

| 304 | Not Modified | Caché mostrando datos desactualizados |

| 400 | Bad Request | Devuelto para entrada válida pero incorrecta (debería ser 422) |

| 401 | Unauthorized | Faltante: vulnerabilidad de bypass de auth |

| 403 | Forbidden | Faltante: vulnerabilidad de bypass de autorización |

| 404 | Not Found | Se devuelve 500 en lugar de 404 para recursos inexistentes |

| 409 | Conflict | Faltante: se crean duplicados silenciosamente |

| 422 | Unprocessable Entity | Confundido con 400 |

| 429 | Too Many Requests | Faltante: fuerza bruta posible |

| 500 | Internal Server Error | Disparado por entrada inválida (debería ser 4xx) |

| 503 | Service Unavailable | Sin degradación elegante en el frontend |

Testear códigos de estado con Playwright

test('crear un usuario devuelve 201', async ({ request }) => {
  const response = await request.post('/api/users', {
    data: { email: 'nuevo@test.com', password: 'PassValida1' },
  });
  expect(response.status()).toBe(201);
});

test('email duplicado devuelve 409', async ({ request }) => {
  // Crear usuario primero
  await request.post('/api/users', {
    data: { email: 'existente@test.com', password: 'PassValida1' },
  });
  // Intentar de nuevo con el mismo email
  const response = await request.post('/api/users', {
    data: { email: 'existente@test.com', password: 'PassValida1' },
  });
  expect(response.status()).toBe(409);
});

test('sin token devuelve 401', async ({ request }) => {
  const response = await request.get('/api/profile');
  expect(response.status()).toBe(401);
});

test('rol incorrecto devuelve 403', async ({ request }) => {
  // Usar token de member, intentar acceder a endpoint de admin
  const response = await request.get('/api/admin/users', {
    headers: { Authorization: `Bearer ${memberToken}` },
  });
  expect(response.status()).toBe(403);
});

Las aserciones de código de estado son los tests de API más rápidos y confiables que puedes escribir. Corren en milisegundos y detectan bugs de autorización, validación faltante y diseño incorrecto de API.

→ See also: API Testing 101: Todo lo que Todo QA Engineer Necesita Saber en 2026 | ¿Qué es una API REST? Guía Práctica para Ingenieros QA | API Testing con Playwright: Más Allá de la UI | Autenticación en Tests de API: Claves API, Tokens Bearer, OAuth2, JWT