Envoltura de respuesta
Cada endpoint v6, exitoso o fallido, devuelve las mismas tres llaves de nivel superior: success, exactamente una de data o error, y meta. Una sola rama en tu cliente, sin adivinar la forma.
5 min de lecturaRevisado el
Respuesta exitosa
{
"success": true,
"data": {
"pong": true,
"apiVersion": "v6",
"client": { "ip": "203.0.113.45" }
},
"meta": {
"requestId": "b9b1704baffab211",
"clientIp": "203.0.113.45",
"timestamp": "2026-05-22T19:43:59+00:00",
"responseTimeMs": 4.24
}
}data es el contenido real del endpoint. Su forma varía por endpoint: la referencia tiene el esquema de cada uno.
Respuesta de error
{
"success": false,
"error": {
"code": "AUTH_REQUIRED",
"legacyCode": 40002,
"type": "https://developers.hablame.co/docs/v6/errors/auth-required",
"message": "Authentication is required. Send your API key as `Authorization: Bearer <token>`.",
"details": []
},
"meta": {
"requestId": "a883f4341b91353d",
"clientIp": "203.0.113.45",
"timestamp": "2026-05-22T19:00:00+00:00",
"responseTimeMs": 0.6
}
}Campos del error
| Campo | Obligatorio | Notas |
|---|---|---|
code | Sí | Identificador estable en UPPER_SNAKE_CASE. Ramifica tu cliente sobre este. No cambia entre versiones ni cuando se reescribe el mensaje. |
legacyCode | No | Código numérico del catálogo v5. Presente solo cuando hay un puente; los códigos introducidos en v6 lo omiten. |
type | Sí | Dirección que explica el error: al abrirla, te lleva a su entrada en el catálogo de errores. Siempre tiene la forma https:. |
message | Sí | Texto para quien integra. Según el servicio llega en español o en inglés, y puede cambiar: no lo compares ni decidas por él, ramifica por code. Muéstralo a ingenieros, no a usuarios finales. |
details | Sí (puede ir vacío) | Arreglo de objetos con contexto estructurado. La mayoría de códigos lo deja vacío; los errores de validación lo llenan con incidencias por campo. |
El bloque meta
Presente en toda respuesta, exitosa o de error, con estas cuatro llaves siempre:
requestId- Identificador de la petición, de 16 caracteres hexadecimales, asignado por el servidor. Es el mismo valor de la cabecera
X-Request-Id. Cítalo al escribir a soporte: con él encontramos tu petición exacta. clientIp- La IP con la que la API vio tu petición. Sirve cuando tu clave solo admite ciertas redes: compara este valor con la lista.
timestamp- Fecha y hora en formato ISO 8601 con desplazamiento de zona horaria. El momento en que se construyó el cuerpo de la respuesta.
responseTimeMs- Tiempo que tardó el servidor en construir la respuesta, en milisegundos. No incluye la red: mide solo lo que gastamos procesando.
Tu cliente puede ignorar cualquier llave nueva que aparezca en meta o en data: las llaves nuevas se añaden sin avisar y las existentes no cambian de tipo.
Ramificar tu cliente
El patrón es el mismo en cualquier lenguaje: revisa success primero, nunca inspecciones solo el código HTTP.
type Envelope<T> =
| { success: true; data: T; meta: Meta }
| { success: false; error: ApiError; meta: Meta }
const res = await fetch(url, { headers: { Authorization: `Bearer ${key}` } })
const body = (await res.json()) as Envelope<PingData>
if (body.success) {
console.log(body.data.pong)
} else {
// ramifica sobre el code estable, NO sobre el status HTTP
if (body.error.code === 'RATE_TPS_EXCEEDED') {
const wait = Number(res.headers.get('Retry-After') ?? 5)
await sleep(wait * 1000)
return retry()
}
throw new Error(`${body.error.code}: ${body.error.message}`)
}import time, httpx
r = httpx.get(url, headers={"Authorization": f"Bearer {key}"})
body = r.json()
if body["success"]:
print(body["data"]["pong"])
else:
code = body["error"]["code"]
if code == "RATE_TPS_EXCEEDED":
time.sleep(int(r.headers.get("Retry-After", "5")))
return retry()
raise RuntimeError(f"{code}: {body['error']['message']}")<?php
$body = json_decode($raw, true);
if ($body['success']) {
echo $body['data']['pong'];
} else {
$code = $body['error']['code'];
if ($code === 'RATE_TPS_EXCEEDED') {
sleep((int) ($headers['retry-after'] ?? 5));
return retry();
}
throw new RuntimeException($code . ': ' . $body['error']['message']);
}HTTP status frente a error.code
Sirven a propósitos distintos:
- El status HTTP le dice a routers, CDNs e intermediarios si la respuesta es correcta (2xx), recuperable (4xx) o terminal (5xx). Úsalo para decisiones de transporte: reintentar o fallar.
error.le dice a tu aplicación qué falla ocurrió. Úsalo para decisiones de producto: reautenticar, refrescar, mostrar una pantalla específica.code
Dos códigos distintos pueden compartir el mismo status. Por ejemplo, AUTH_REQUIRED y AUTH_INVALID_KEY son ambos 401, pero el code te dice si el arreglo es "agregar la cabecera" o "revisar el valor de la clave".
No analices error. en código. La redacción puede cambiar entre versiones sin romper el contrato; el code es el contrato.
Invariantes en los que puedes confiar
- La forma de la envoltura nunca cambia entre versiones del mismo endpoint. Se agregan campos nuevos a
dataometa(aditivo) y los existentes mantienen su tipo. successy la presencia dedatafrente aerrorson mutuamente consistentes: uno está siempre, el otro siempre ausente.meta.es único por petición y seguro de registrar.requestId - Los valores de
error.están listados en el catálogo de errores y nunca se reutilizan ni se renombran.code