Formatos de Respuesta API: Mejores Prácticas para APIs Consistentes
Las respuestas API inconsistentes son una de las principales quejas de los desarrolladores frontend. Cuando cada endpoint devuelve datos con una estructura diferente, el código del cliente se llena de casos especiales. Un formato de respuesta consistente mejora la experiencia del desarrollador, reduce errores y hace que tu API se auto-documente.
El Envoltorio de Respuesta
Envuelve cada respuesta en una estructura consistente:
Éxito (Recurso Individual)
{
"data": {
"id": "user_123",
"name": "Alice",
"email": "alice@example.com",
"createdAt": "2024-01-15T10:30:00Z"
},
"meta": {
"requestId": "req_abc123"
}
}
Éxito (Colección)
{
"data": [
{ "id": "user_123", "name": "Alice" },
{ "id": "user_456", "name": "Bob" }
],
"meta": {
"total": 142,
"page": 1,
"perPage": 20,
"requestId": "req_def456"
}
}
Error
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
{ "field": "email", "message": "Invalid email format" }
]
},
"meta": {
"requestId": "req_ghi789"
}
}
El principio clave: los clientes siempre verifican data o error en el nivel superior. Nunca mezcles datos exitosos e información de error en la misma respuesta.
Valida tu formato de respuesta con nuestro JSON Validator.
Códigos de Estado HTTP
Usa los códigos de estado correctamente — son lo primero que verifican los clientes:
Códigos de Éxito
| Código | Cuándo |
|---|---|
| 200 OK | Éxito en GET, éxito en PUT/PATCH |
| 201 Created | POST que creó un recurso |
| 204 No Content | Éxito en DELETE (sin cuerpo) |
Códigos de Error del Cliente
| Código | Cuándo |
|---|---|
| 400 Bad Request | Sintaxis de solicitud malformada |
| 401 Unauthorized | Autenticación faltante o inválida |
| 403 Forbidden | Autenticado pero no autorizado |
| 404 Not Found | El recurso no existe |
| 409 Conflict | Conflicto de estado del recurso (duplicado) |
| 422 Unprocessable | Sintaxis válida pero errores semánticos |
| 429 Too Many Requests | Límite de tasa excedido |
Códigos de Error del Servidor
| Código | Cuándo |
|---|---|
| 500 Internal Server Error | Fallo inesperado del servidor |
| 502 Bad Gateway | Fallo del servicio upstream |
| 503 Service Unavailable | Sobrecarga temporal o mantenimiento |
Diseño de Respuestas de Error
Las buenas respuestas de error ayudan a los desarrolladores a depurar rápidamente:
{
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "User with ID 'user_999' not found",
"details": [],
"documentationUrl": "https://api.example.com/docs/errors#RESOURCE_NOT_FOUND"
},
"meta": {
"requestId": "req_xyz789",
"timestamp": "2024-01-15T10:30:00Z"
}
}
Reglas:
codees legible por máquinas (cadena constante, no el estado HTTP)messagees legible por humanos (puede cambiar sin romper clientes)detailsproporciona errores a nivel de campo para fallos de validaciónrequestIdpermite al equipo de soporte rastrear la solicitud en los logs
Patrones de Paginación
Basada en Offset
Simple y permite saltar a páginas arbitrarias:
{
"data": ["..."],
"meta": {
"page": 2,
"perPage": 20,
"total": 142,
"totalPages": 8
},
"links": {
"first": "/api/users?page=1&per_page=20",
"prev": "/api/users?page=1&per_page=20",
"next": "/api/users?page=3&per_page=20",
"last": "/api/users?page=8&per_page=20"
}
}
Basada en Cursor
Mejor para datos en tiempo real y conjuntos de datos grandes:
{
"data": ["..."],
"meta": {
"hasNext": true,
"hasPrev": true
},
"links": {
"next": "/api/users?cursor=eyJpZCI6MTQzfQ&limit=20",
"prev": "/api/users?cursor=eyJpZCI6MTIzfQ&limit=20&direction=prev"
}
}
Fecha y Hora
Siempre usa ISO 8601 con información de zona horaria:
{
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T14:22:33+00:00",
"expiresAt": "2024-12-31T23:59:59Z"
}
Nunca uses:
- Timestamps Unix (ambiguos: ¿segundos o milisegundos?)
- Fechas locales sin zona horaria
- Formatos personalizados como MM/DD/YYYY
Para más información sobre el manejo de timestamps, consulta nuestra guía de Timestamps Unix.
Null vs Campos Ausentes
Dos enfoques comunes:
Incluir con null (explícito):
{ "name": "Alice", "avatar": null, "bio": null }
Omitir campos ausentes (disperso):
{ "name": "Alice" }
Recomendación: Sé consistente dentro de tu API. Los nulls explícitos son mejores para lenguajes tipados (los clientes saben que el campo existe). El enfoque disperso es mejor para entornos con restricciones de ancho de banda.
Versionado de Respuestas
Cuando el formato de respuesta cambia, versiona tu API:
GET /api/v2/users/123
Cambios que rompen compatibilidad y requieren versionado:
- Eliminar un campo
- Cambiar el tipo de un campo
- Renombrar un campo
- Cambiar la estructura del envoltorio de respuesta
Cambios no disruptivos (seguros sin versionado):
- Agregar nuevos campos
- Agregar nuevos endpoints
- Agregar nuevos valores de enum
FAQ
¿Debería envolver las respuestas exitosas en una clave data o devolver el recurso directamente?
Usar un envoltorio data proporciona una estructura consistente para todas las respuestas y deja espacio para metadatos, paginación y enlaces junto a los datos. Devolver el recurso directamente es más simple para endpoints de recurso individual. La mayoría de las APIs modernas usan el enfoque de envoltorio por consistencia.
¿Cómo debo manejar los fallos parciales en operaciones por lotes?
Devuelve 200 con una respuesta que incluya tanto éxitos como fallos en el objeto data. Usar 207 Multi-Status (WebDAV) es otra opción pero se usa menos comúnmente en APIs REST.
Recursos Relacionados
- JSON Formatter — Formatea respuestas API
- JSON API Design Patterns — Guía completa de diseño de APIs
- JSON Schema Validation — Valida estructuras de respuesta