Integración y APIs20 min

Códigos HTTP en una API de negocio: qué responder cuando buscan un contacto, y qué le cuesta equivocarse

Por · fundador y CEO de Hábil ·

Cuándo responder 200, 404, 400, 422, 409, 429 o 503 al buscar un contacto, y cuánto cuesta elegir mal: reintentos, tableros engañosos y agentes.

Un integrador de su empresa llama a una API para buscar a un contacto por su correo. La respuesta llega con código 200 y un cuerpo que dice {"error": "contacto no encontrado"}. El programa del integrador ve el 200, da la operación por buena y sigue adelante con un contacto que no existe. Nadie se entera hasta que, semanas después, alguien pregunta por qué faltan pedidos.

Nada falló en la red, ni en el servidor, ni en la base de datos. Falló una decisión que parece menor: qué número poner en la primera línea de la respuesta. El código de estado es lo único que leen, sin interpretar, los balanceadores, los tableros de monitoreo, las librerías de reintento, las puertas de enlace y los programas de sus socios. Una frase en el cuerpo la entiende una persona; el número lo entiende todo lo demás.

Este artículo es para CTO y líderes de tecnología, y está escrito para que un director que lo lea por encima se lleve lo esencial en las tablas y en los cierres de sección. Recorre los códigos que de verdad se usan en una API de negocio con un solo ejemplo, buscar un contacto, y explica cuándo corresponde cada uno y qué cuesta equivocarse. Las afirmaciones técnicas se apoyan en los estándares de HTTP y se citan al final; los criterios de diseño que son nuestros se marcan como tales.

Una respuesta HTTP trae un código, unas cabeceras y un cuerpo. El código y las cabeceras son señales estandarizadas que muchas herramientas usan para decidir; el cuerpo lo lee una persona o, si el contrato lo define, un programa.

Códigos HTTP en una API de negocio: qué responder cuando buscan un contacto, y qué le cuesta equivocarse
Si la respuesta es…La familia es…Regla general
Un éxito real2xxSeguir adelante
Una petición que debe corregirse4xxCorregir, identificarse o esperar según el código y el contrato
Una falla del servidor o de una dependencia5xxReintentar solo si la operación es repetible o el contrato ofrece idempotencia verificable

Las tres partes de una respuesta, y por qué el número manda

En HTTP/1.1 la respuesta empieza con una línea de estado: el código y, opcionalmente, una frase; en HTTP/2 y versiones posteriores el código viaja como metadato de la respuesta, con el mismo significado. El estándar aclara que las frases son solo recomendaciones: pueden cambiarse por equivalentes locales o dejarse fuera sin afectar el protocolo [1, §15.1]. El que cuenta es el número.

El primer dígito define la familia, y el estándar exige a todo cliente entenderla aunque no conozca el código exacto [1, §15]:

Las tres partes de una respuesta, y por qué el número manda
FamiliaSignificado en el estándarQué le dice a quien llama
2xxLa petición se recibió, se entendió y se aceptóSiga adelante
3xxHace falta otra acción para completarlaVaya a otro sitio o reutilice lo que tiene
4xxLa petición contiene un error o no puede cumplirseRevise el código específico y el contrato; repetir idéntica casi nunca ayuda
5xxEl servidor falló al atender una petición aparentemente válidaRevise el código específico y el contrato; no reintente a ciegas una escritura no idempotente

Hay una regla que explica por qué elegir bien importa más que elegir con detalle: un cliente que reciba un código que no reconoce debe tratarlo como el x00 de su familia. El propio estándar lo ilustra: un hipotético 471 se trata como un 400 [1, §15]. Dicho de otro modo, la familia es lo mínimo que toda máquina va a entender. Si usted pone un 200 a un error, la familia que las máquinas leen es la del éxito.

Las cabeceras completan al número. Algunas son obligatorias con ciertos códigos: un 401 debe llevar WWW-Authenticate y un 405 debe llevar Allow con los métodos que sí acepta el recurso [1, §15.5.2 y §15.5.6]. Y el cuerpo, como se verá más adelante, conviene que tenga un formato estándar, para que un programa no tenga que adivinar.

¿Cuántas de las respuestas de error de sus APIs actuales se distinguen de un éxito mirando solo la primera línea?

El caso: buscar un contacto

Imagine una API de contactos de una empresa, para uso de sus canales y de sus socios. Tiene dos formas de encontrar a alguien:

GET /contactos/{id}                 -> obtener un contacto por su identificador
GET /contactos?correo=ana@ejemplo.com -> buscar contactos por un criterio

Parecen la misma cosa. No lo son, y la diferencia ya produce la primera decisión. Con el identificador, la persona que llama afirma «este contacto existe; démelo». Con la búsqueda, pregunta «¿hay alguno que cumpla esto?». Y la respuesta correcta a una pregunta que admite «ninguno» no es un error.

Un contrato en estilo OpenAPI, simplificado, para tener el mapa completo antes de recorrerlo:

paths:
  /contactos/{id}:
    get:
      summary: Obtener un contacto por identificador
      responses:
        "200": { description: El contacto }
        "400": { description: El identificador no tiene forma válida }
        "401": { description: Falta identificarse }
        "403": { description: Identificado, pero sin permiso }
        "404": { description: No existe, o no se revela que exista }
        "410": { description: Existió y se retiró de forma definitiva }
        "429": { description: Demasiadas peticiones; ver Retry-After }
        "503": { description: Servicio no disponible por ahora; ver Retry-After }
  /contactos:
    get:
      summary: Buscar contactos por criterio
      responses:
        "200": { description: Lista de coincidencias (puede estar vacía) }
        "400": { description: Criterio ilegible o parámetro desconocido }
        "422": { description: Criterio bien formado, pero que el negocio no admite }
    post:
      summary: Crear un contacto
      responses:
        "201": { description: Creado; la cabecera Location apunta a él }
        "409": { description: Ya existe uno que choca con este }
        "422": { description: Datos bien formados pero que incumplen una regla }

Los recorremos de menor a mayor gravedad, empezando por lo que sale bien.

Si su equipo tuviera que dibujar este mapa para la API de clientes o de pólizas, ¿coincidirían todos en cuántas respuestas distintas debe poder dar?

Cuando sale bien: 200, y la tentación de usarlo para todo

La búsqueda sin coincidencias merece cuidado. Una petición como esta:

curl -i "https://api.ejemplo.com/contactos?correo=nadie@ejemplo.com"

debería responder:

HTTP/1.1 200 OK
Content-Type: application/json

{ "resultados": [], "total": 0 }

El 200 es el código del éxito de una consulta, y el estándar define su contenido para GET como la representación del recurso objetivo [1, §15.3.1]. Aquí el recurso objetivo es la búsqueda, que existe y se ejecutó correctamente; su resultado es una lista vacía. Es una lectura razonable de la definición, y es un criterio de diseño nuestro: lo que importa es que se decida, se documente y se aplique igual en todas las rutas. Si una ruta devuelve lista vacía y otra devuelve 404 para el mismo tipo de pregunta, el cliente tendrá que escribir un caso especial por ruta.

Lo contrario, el error disfrazado de éxito, es la fuente de las fallas más caras de detectar:

HTTP/1.1 200 OK
Content-Type: application/json

{ "error": "contacto no encontrado" }

Un monitor que solo mira el código de estado marcará esto como sano. Una librería de reintentos lo dará por resuelto. Un socio que integra su API validará el cuerpo solo si alguien se lo dijo. Un 200 con un error dentro es un fallo que pasó por la puerta de la oficina con el gafete del éxito.

Hay tres hermanos del 200 que conviene distinguir cuando el caso lo pide, a los que dedicaremos una sola línea cada uno: 201 cuando se creó algo (normalmente con la ubicación del nuevo recurso en Location), 202 cuando se aceptó el trabajo pero aún no termina (si el contrato publica una consulta de estado, se revisa ahí) y 204 cuando salió bien y no hay nada que devolver, por ejemplo al retirar un contacto [1, §15.3].

¿Sabe su organización qué porcentaje de lo que sus tableros llaman «éxito» incluye cuerpos con errores adentro?

Cuando el cliente se equivocó: la familia 4xx

La familia 4xx dice «el problema está en la petición». Para elegir dentro de ella, puede servir una política de precedencia, es decir, un orden de preguntas que reduce las discusiones. Es criterio nuestro, no mandato del estándar, y un contrato puede alterarla (por ejemplo, autenticar antes de validar es una decisión de seguridad legítima):

  1. ¿Se entiende la petición? (400)
  2. ¿Sabemos quién es? (401)
  3. ¿Tiene permiso? (403)
  4. ¿Existe lo que pide? (404, 410)
  5. ¿Se permite este método sobre este recurso? (405)
  6. ¿Viene en un formato que aceptamos? (415)
  7. ¿El contenido, ya entendido, respeta las reglas de negocio? (422)
  8. ¿Choca con lo que ya está guardado? (409)

Con esta política, cuando una petición tiene dos defectos, responde el primero de esa lista que falla. Eso vuelve las respuestas predecibles, y lo predecible se puede probar y documentar.

400: no se entiende

El estándar describe el 400 como la respuesta cuando el servidor no puede o no quiere procesar la petición por algo que percibe como un error del cliente: por ejemplo, sintaxis malformada [1, §15.5.1]. En el caso del contacto:

curl -i "https://api.ejemplo.com/contactos/abc%20%20%20"      # identificador ilegible
curl -i "https://api.ejemplo.com/contactos?correo=&color=azul" # parámetro desconocido

Si el contrato rechaza esos valores, ambas pueden responder 400. La señal es que la petición no se pudo leer como lo que dice ser. Repetirla tal cual daría el mismo resultado, y por eso un cliente bien hecho no la reintenta: la corrige o se la muestra a una persona.

401 y 403: quién es usted y qué puede hacer

Son dos preguntas distintas y se confunden con frecuencia. El 401 dice que la petición no incluye credenciales válidas, y el estándar obliga a incluir WWW-Authenticate [1, §15.5.2]. El 403 dice que el servidor entendió la petición pero rehúsa atenderla; si ya se enviaron credenciales, el cliente no debería repetir automáticamente con las mismas [1, §15.5.4].

La diferencia tiene consecuencias de operación. Ante un 401, un cliente puede renovar su sesión y repetir. Ante un 403, repetir con las mismas credenciales no cambia el resultado. Un servidor que confunde ambos pone a los clientes en un bucle de renovar credenciales que no resuelve nada.

Hay un matiz de seguridad que nos importa en una API de contactos: el estándar permite a un servidor que quiera ocultar que un recurso prohibido existe responder 404 en lugar de 403 [1, §15.5.4]. Si el contacto de un cliente es visible solo para su dueño, decirle a otro cliente «prohibido» le confirma que ese identificador existe. Responder «no encontrado» no le confirma nada. Es una decisión de política que conviene escribir, no improvisar por ruta.

404 y 410: no está

El 404 indica que no se encontró una representación vigente del recurso o que el servidor no quiere revelar que existe; no dice si la ausencia es temporal o permanente [1, §15.5.5]. Si el servidor sabe que es permanente, el estándar prefiere el 410 [1, §15.5.11].

En un contacto esa diferencia tiene valor de negocio. «Nunca existió ese identificador» (404) y «existió y fue fusionado con otro o retirado» (410) llevan a acciones distintas del lado del cliente: en el primero revisa si tuvo un error de captura; en el segundo, deja de pedirlo y busca el contacto que lo sustituyó. Si su operación no distingue ambos casos, use 404, que es el predeterminado, tal como el propio estándar recomienda cuando el servidor no sabe si la condición es permanente [1, §15.5.11].

Un dato práctico que sorprende: el 404 está entre los códigos que el estándar califica como cacheables heurísticamente [1, §15.1]. Una respuesta de «no existe» puede quedar guardada en una caché intermedia, de modo que un contacto recién creado sigue sin aparecer por un rato. Si la creación y la consulta ocurren en secuencia inmediata, conviene revisar las cabeceras de caché de esa respuesta.

405 y 415: la ruta existe, la forma no

Un 405 dice que el método es conocido pero este recurso no lo admite, y exige la cabecera Allow con lo que sí admite [1, §15.5.6]. Un 415 dice que el formato del contenido enviado no se acepta. Son errores de integración, no de negocio, y aparecen sobre todo en los primeros días de una conexión nueva.

Cuando un socio recibe un error de su API, ¿puede saber por la primera línea si le conviene corregir la petición, renovar su sesión o dejar de pedir ese recurso?

400 contra 422: la diferencia que más se discute

La discusión más frecuente en los equipos es cuál usar cuando los datos vienen mal. La política que sugerimos para este contrato (no es una regla universal de HTTP), apoyada en las definiciones del estándar, es corta:

400 contra 422: la diferencia que más se discute
SituaciónCódigoPor qué
El JSON está roto, falta una llave de cierre o un parámetro no existe400La petición no se puede leer [1, §15.5.1]
El JSON es válido y el formato se acepta, pero el correo no tiene arroba o el teléfono no cumple la regla de su país422El servidor entiende el contenido, la sintaxis es correcta, pero no puede procesar lo que pide [1, §15.5.21]
El contenido es válido, pero choca con lo que ya está guardado (otro contacto con el mismo correo)409Conflicto con el estado actual del recurso [1, §15.5.10]

El 422 se define como la respuesta en la que el servidor entiende el tipo de contenido de la petición (por eso 415 no aplica) y su sintaxis es correcta, pero no pudo procesar las instrucciones contenidas [1, §15.5.21]. Muchos equipos usan 400 para todo y no es un error de protocolo: el 400 es amplio. La ventaja de separar es de operación: con 400 el cliente sabe que su código produjo algo ilegible (un defecto de integración) y con 422 sabe que los datos no pasaron una regla (un caso para mostrar a un usuario). Dos causas distintas, dos reparaciones distintas, dos equipos distintos que atienden la alerta.

Un ejemplo de creación con un dato inválido:

curl -i -X POST https://api.ejemplo.com/contactos \
  -H "Content-Type: application/json" \
  -d '{"nombre": "Ana Pérez", "correo": "ana-at-ejemplo"}'
HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json

{
  "type": "https://api.ejemplo.com/problemas/correo-invalido",
  "title": "El correo no tiene un formato válido",
  "status": 422,
  "detail": "El campo 'correo' debe contener una dirección de correo.",
  "instance": "/contactos"
}

El formato del cuerpo no es casual; se explica en la sección de Problem Details.

Si hoy responde 400 a todo, ¿puede su equipo de soporte distinguir una integración mal hecha de un dato mal capturado sin abrir los registros?

409: cuando choca con lo que ya existe

El 409 se reserva para la petición que no puede completarse por un conflicto con el estado actual del recurso, en situaciones donde la persona que llama podría resolverlo y reenviar; el estándar pide que el servidor incluya información suficiente para reconocer la fuente del conflicto [1, §15.5.10].

En contactos aparece de dos formas. La primera es la obvia: crear un contacto cuyo correo ya pertenece a otro. La segunda es más cara y menos visible: dos canales editan al mismo contacto casi a la vez. Si ambos hacen PUT con la versión completa que leyeron, el que llega segundo borra en silencio el cambio del primero. Nadie vio un error; el sistema guardó lo que le pidieron.

El estándar de HTTP tiene herramientas para esto: las peticiones condicionales. Un contrato puede devolver una etiqueta de versión (ETag) en cada lectura y exigir que el cliente la devuelva en If-Match al modificar; si la versión ya cambió, la respuesta es 412 Precondition Failed [1, §15.5.13], y si el cliente ni siquiera mandó la condición y el servidor la exige, 428 Precondition Required, pensado justamente para evitar la «actualización perdida» [3, §3]. Es la forma de que un cambio perdido se vuelva un error visible en lugar de un hueco silencioso. El detalle de cada uno de estos dos códigos queda fuera de este artículo; lo que importa aquí es el principio: un conflicto se puede detectar, y detectarlo es más barato que descubrirlo en una auditoría.

¿Qué pasa en sus sistemas cuando dos canales modifican al mismo cliente al mismo tiempo: hay un error visible o gana el último?

429, 500, 502, 503 y 504: cuando el problema es del servidor, o de su capacidad

La familia 5xx es donde más dinero cuesta elegir mal, porque su efecto práctico es decirle a las máquinas «puede ser buena idea reintentar». Reintentar bien salva una operación; reintentar mal la duplica o tumba el servicio.

429: demasiadas peticiones

Aunque no está en el estándar principal de HTTP sino en una extensión, el 429 es la herramienta correcta para limitar el ritmo. Indica que quien llama envió demasiadas peticiones en un periodo, y puede incluir Retry-After para decir cuánto esperar; la misma especificación aclara que no define cómo se identifica al usuario ni cómo se cuentan las peticiones, y que estas respuestas no deben guardarse en caché [3, §4].

HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/problem+json

{ "type": "https://api.ejemplo.com/problemas/limite-excedido",
  "title": "Se superó el límite de peticiones",
  "status": 429 }

Observe que el 429 pertenece a la familia de errores del cliente aunque trate de capacidad; puede reflejar una cuota o política compartida, no solo el ritmo de quien llama. Y observe Retry-After, que es opcional: si viene, respételo; si no, siga la política documentada, porque sin ninguna señal el cliente adivina. En una integración que consulta contactos en lote, la diferencia es la que hay entre un cliente que se acomoda y uno que insiste cada segundo y empeora la saturación que se quería evitar.

500: no esperábamos esto

El 500 indica una condición inesperada que impidió cumplir la petición [1, §15.6.1]. Es el código de «ocurrió una condición inesperada de nuestro lado»; el servidor puede conocer la causa, pero la respuesta no debe divulgarla. Dos reglas para un 500 sano. Primera: el cuerpo no debe mostrar la pila de ejecución, la consulta, la ruta de archivos ni claves; el estándar de Problem Details advierte expresamente contra exponer detalles de implementación como un volcado de pila, porque pueden revelar información explotable [2, §5]. Segunda: el mensaje debe ser general y llevar un identificador que su equipo pueda buscar en sus registros.

502 y 504: un intermediario no obtuvo respuesta

Cuando su API depende de otro sistema (el CRM, el ERP, el core) y actúa como puerta de enlace o proxy, dos códigos describen lo que ocurre entre ambos (en otras topologías el contrato puede elegir otra respuesta). El 502 es la respuesta cuando el servidor recibió una respuesta inválida del sistema de atrás; el 504, cuando no recibió respuesta a tiempo [1, §15.6.3 y §15.6.5].

Un 502 o un 504 en una escritura es especialmente delicado, y lo mismo vale para un 500 o un 503 que llegue después de que la operación se aplicó, porque no dice si la operación se aplicó o no. Si el cliente reintenta a ciegas el alta de un contacto, puede crear dos. La salida está en la sección de reintentos.

503: ahora no puedo, vuelva en un rato

El 503 indica que el servidor no puede atender la petición en este momento por sobrecarga temporal o mantenimiento programado, algo que probablemente se alivie con el tiempo; puede acompañarse de Retry-After [1, §15.6.4]. El mismo estándar aclara que su existencia no obliga a un servidor sobrecargado a usarlo: algunos simplemente rechazan la conexión [1, §15.6.4].

Retry-After acepta un número de segundos o una fecha [1, §10.2.3]. Con un 503 indica cuánto se espera que el servicio no esté disponible para el cliente [1, §10.2.3]. En un mantenimiento planeado, ese dato permite que las integraciones de sus socios se pausen en orden en lugar de fallar en cascada.

Resumen de la familia, con la pregunta que más duele en cada caso:

503: ahora no puedo, vuelva en un rato
CódigoQué diceQué debería hacer quien llamaEl error típico
429Va demasiado rápidoEsperar lo que diga Retry-After y volverNo mandar Retry-After; el cliente insiste
500Falló algo inesperadoReintentar con cautela si la operación es repetible; avisarMostrar pila, consulta o rutas
502El sistema de atrás respondió algo inválidoComprobar si la operación se aplicó antes de repetirReintentar un alta a ciegas
503Ahora no puedo atenderEsperar y reintentarDevolver 200 con «en mantenimiento» en el cuerpo
504El sistema de atrás no respondió a tiempoComprobar el estado antes de repetirConfundirlo con un fallo del cliente

Cuando uno de sus sistemas internos se pone lento, ¿sus API le dicen a quien llama cuánto esperar o lo dejan decidir a su suerte?

Problem Details (RFC 9457): un cuerpo de error que un programa sí puede leer

Con los códigos elegidos, falta el cuerpo. Sin acuerdo, cada API inventa el suyo: {"error": "..."}, {"mensaje": "..."}, {"codigo": 7}. El estándar RFC 9457 (que reemplaza al 7807) define un formato común para eso [2]. Identificado con el tipo de medio application/problem+json, define cinco miembros base que puede incluir [2, §3.1]:

Problem Details (RFC 9457): un cuerpo de error que un programa sí puede leer
MiembroPara qué sirve
typeUn identificador del tipo de problema, en forma de URI; los consumidores deben usarlo como identificador principal del problema [2, §3.1.1]
titleUn resumen breve del tipo de problema, legible por personas [2, §3.1.3]
statusEl código HTTP; es informativo, y el generador debe usar el mismo código en la respuesta real [2, §3.1.2]
detailUna explicación de esta ocurrencia concreta; los consumidores no deben analizarla para extraer información [2, §3.1.4]
instanceUn identificador de esta ocurrencia, para rastrearla [2, §3.1.5]

Si no hay type, se asume about:blank, que significa que el problema no tiene más semántica que la del código HTTP [2, §3.1.1 y §4.2.1]. Los clientes deben ignorar los miembros de extensión que no reconozcan [2, §3.2], lo que permite agregar campos propios (por ejemplo, la lista de campos inválidos) sin romper a nadie.

Lo valioso para el negocio está en dos reglas. Primera: el programa decide por type, no por la frase. Si la frase de detail cambia de «intente de nuevo» a «reintente más tarde», el comportamiento del cliente no debe cambiar. Segunda: el status del cuerpo y el código de la primera línea deben coincidir; el propio estándar advierte que duplicar la información abre la posibilidad de que las dos discrepen [2, §5]. Un error con status: 404 en el cuerpo sobre una respuesta 200 es el caso de arriba, vestido de formato estándar.

Con un type estable (y normalmente un title estable), un mismo problema produce el mismo identificador; detail, instance y las extensiones pueden variar por ocurrencia. El generador de la respuesta es quien debe hacer coincidir status con el código; un intermediario puede alterarla después. Eso permite contar ocurrencias por tipo en un tablero y no por texto.

¿Podría hoy su organización contar cuántas veces ocurrió un mismo tipo de error en una semana, sin leer mensajes uno por uno?

Reintentos: lo que se repite y lo que no

Elegir el código es la mitad del trabajo. La otra mitad es lo que el cliente hace con él. La regla de fondo viene del estándar de métodos: una petición es idempotente si repetirla produce el mismo efecto que hacerla una vez. PUT, DELETE y los métodos seguros, como GET, lo son; POST no [1, §9.2.2]. Y el estándar indica que un cliente no debería reintentar automáticamente un método no idempotente, salvo que tenga forma de saber que la semántica es idempotente en ese caso o de detectar que la petición original nunca se aplicó [1, §9.2.2].

Aplicado al contacto:

Reintentos: lo que se repite y lo que no
PeticiónPropiedad HTTP del métodoConsecuencia de equivocarse
GET /contactos/{id}Seguro e idempotenteCarga adicional
PUT /contactos/{id} con el contacto completoIdempotenteMismo efecto intencional
DELETE /contactos/{id}IdempotenteLa segunda vez puede responder 404/410, y es correcto
POST /contactos (alta)No idempotente por definiciónDos contactos iguales, salvo que el contrato lo evite

Para volver segura el alta existe una práctica extendida: que el cliente envíe una llave de idempotencia en una cabecera Idempotency-Key, de modo que el servidor reconozca el reintento. Una nota de cautela: la cabecera se describe en un borrador del IETF (revisión 07) que, al 20 de octubre de 2026, figura vencido (18-abr-2026) y no es un estándar publicado [4]; úselo como referencia, no como norma. Si su contrato adopta el patrón, documente retención de llaves y respuestas. Lo que propone el borrador: la misma llave con la misma petición devuelve el resultado original; la misma llave con otra carga, 422; y una repetición mientras la original sigue en proceso, 409 [4].

La seguridad de reintento depende también del contrato, de los efectos secundarios y de la incertidumbre de la red, no solo del método. Sobre esa base, el cliente puede tener una tabla de decisión por código. Una versión comentada para nuestro caso:

Reintentos: lo que se repite y lo que no
RespuestaAcción del clientePor qué
200, 201, 204ContinuarSalió bien
400, 422No reintentar; corregir o mostrarRepetir igual da lo mismo
401Renovar credenciales una vez; si vuelve, detenerseUn bucle de renovación no resuelve nada
403No reintentar; escalarHay una política que lo niega
404, 410No reintentar; tratar «no existe» como un resultadoEs información, no una falla
409, 412Releer el estado, decidir, reenviarEl estado cambió desde que se leyó
429Esperar Retry-After y volverRespeta la capacidad del servidor
502, 504 en escrituraConsultar el estado antes de repetirPudo haberse aplicado
500, 503Solo si el método es idempotente o el contrato ofrece idempotencia verificable: reintentar con espera creciente, aleatoria y con tope, respetando Retry-After si viene; si no, consultar el estadoFalla probablemente temporal, pero una escritura pudo aplicarse
Código no reconocidoTratarlo como el x00 de su familia [1, §15]Regla del estándar

La fila de 500 y 503 pide un matiz: la espera creciente con un componente aleatorio y un tope es una práctica común de ingeniería, no un mandato del estándar. Su propósito es que mil clientes no vuelvan todos al segundo exacto y derriben otra vez un servicio que se estaba recuperando.

Si su proveedor responde 503 durante quince minutos, ¿qué hacen sus integraciones: esperan en orden, insisten sin parar o se detienen sin avisar?

Cuánto cuesta equivocarse: tres escenarios

Lo que sigue son escenarios ilustrativos, sin cifras; sirven para ver el mecanismo del daño.

1. La integración que reintenta lo que no debe. Una aplicación de ventas da de alta contactos en su API. Un día la base se pone lenta y la API responde 504 en algunas altas. El código de la aplicación trata todo 5xx como «reintentar», y algunas altas ya se habían aplicado. Resultado: contactos duplicados, campañas que escriben dos veces al mismo cliente y un trabajo de depuración que nadie planeó. Si cliente, servidor y contrato aplican la política de «compruebe el estado antes de repetir» y una llave de idempotencia en el alta, el mismo incidente tiene muchas menos probabilidades de dejar duplicados.

2. El tablero que se ve verde. Un equipo mide la salud de su API por la tasa de respuestas 5xx. La API devuelve 200 con un error en el cuerpo cuando el CRM no responde. El tablero muestra verde mientras los canales no pueden consultar contactos. En sentido contrario también hay costo: si cada búsqueda sin resultados se devuelve como 404, los consumidores de la API acumulan «errores» que no lo son, y el equipo de guardia aprende a ignorar la alerta. Un tablero útil necesita que el código signifique algo: 4xx para lo que corrige quien llama, 5xx para lo que corrige el equipo dueño del servicio.

3. El agente que no sabe qué hacer. Cada vez más APIs las consume un programa que decide solo, incluidos asistentes y agentes de inteligencia artificial que llaman herramientas. Un agente que recibe 200 con la frase «no pude encontrarlo» tiene que interpretar lenguaje natural para saber si falló. Uno que recibe 404 con type estable sabe que puede informar «no existe». Uno que recibe 429 con Retry-After sabe cuánto esperar; uno que recibe 503 sin ninguna indicación puede insistir o rendirse, según cómo se haya programado. Un detail en lenguaje libre no debe analizarse para tomar decisiones [2, §3.1.4]; el type sí. Un código y un tipo bien elegidos reducen la ambigüedad que el agente tiene que manejar. El mismo tema, visto desde lo que necesita su sistema para que los agentes lo usen, lo tratamos en «APIs listas para agentes de IA».

¿Cuál de estos tres escenarios podría estar ocurriendo hoy en alguna de sus integraciones sin que su equipo lo sepa?

Un recorrido completo: el contacto, de punta a punta

Para cerrar el ejemplo, una secuencia con las respuestas que daría una API bien diseñada. Cada fila es una llamada hipotética contra el contrato de arriba.

Un recorrido completo: el contacto, de punta a punta
#LlamadaRespuestaLectura
1GET /contactos?correo=ana@ejemplo.com (existe)200, lista con un contactoÉxito de la consulta
2GET /contactos?correo=nadie@ejemplo.com200, lista vacíaLa búsqueda funcionó; no hay coincidencias
3GET /contactos/c-0001 (existe)200, el contacto, con ETagÉxito con versión
4GET /contactos/c-9999 (nunca existió)404, Problem DetailsResultado, no falla
5GET /contactos/c-0002 (fusionado con otro)410, Problem Details con el sustitutoDejar de pedirlo
6GET /contactos/%%%400Petición ilegible
7POST /contactos con correo sin arroba422Dato que no cumple la regla
8POST /contactos con un correo ya registrado409Choca con lo guardado
9PUT /contactos/c-0001 con If-Match vencido412Alguien cambió antes
10Una ráfaga de búsquedas que supera el límite acordado429 con Retry-AfterRitmo excedido
11GET /contactos/c-0001 con el CRM caído503 con Retry-AfterVuelva en un rato
12POST /contactos y el CRM no contesta a tiempo504Consulte antes de repetir

Doce filas y diez códigos distintos. Las filas 11 y 12 son una política posible del contrato, para una API que actúa como intermediario. Una tabla de contrato documentada y comprobada reduce la ambigüedad entre equipos, y convierte un acuerdo verbal en algo que se puede verificar.

¿Existe hoy, para sus APIs más usadas, una tabla así —escenario, código, lectura— o cada integrador la reconstruye por ensayo y error?

Seis preguntas para su próxima revisión de API

Para llevar a la próxima reunión de arquitectura, sin necesidad de abrir código:

  1. ¿Algún error sale con 200? Pida una muestra de respuestas de error y mire la primera línea.
  2. ¿Lo «no encontrado» tiene una política única? ¿La política distingue búsqueda sin coincidencias (200), recurso inexistente (404) y recurso retirado (410), y se aplica de forma consistente a casos equivalentes en todas las rutas?
  3. ¿Se distingue 400 de 422 y de 409? Si todo es 400, sus equipos de soporte pagan la diferencia.
  4. ¿Los 429 y 503 dicen cuánto esperar? Retry-After es opcional; cuando falta, cada cliente decide solo, así que conviene documentar la política.
  5. ¿Los errores tienen un formato común y un type estable? Es lo que permite a un tablero, a un socio y a un agente decidir sin leer prosa.
  6. ¿Las altas se pueden repetir sin duplicar? Si no, cada 504 es un riesgo de dato doble.

Muchas decisiones empiezan en el contrato; su implementación puede requerir cambios de aplicación o de plataforma (límites de ritmo, persistencia de llaves de idempotencia, observabilidad).

¿Con cuántas de las seis preguntas se sentiría hoy cómodo ante una auditoría o ante un socio que integra su API?

Qué hace Hábil

Hábil trabaja en el espacio entre los sistemas que ya operan en su empresa y las capas nuevas que los necesitan: canales, socios, asistentes y agentes. Parte de este tema —que cada respuesta de una API diga con claridad qué pasó y qué hacer— es lo que hace funcionar una integración cuando algo sale mal, y es parte de lo que ofrecemos en Middleware y plataformas de API. Para que un asistente o un agente de inteligencia artificial use sus sistemas con criterio y límites claros, véase Inteligencia artificial empresarial.

La primera conversación, sin costo, parte del mapa de sus integraciones y de los puntos donde una respuesta ambigua podría costarle dinero, tiempo o evidencia. Escríbanos por WhatsApp.

Consultadas el 20-oct-2026 (los textos de los RFC se leyeron en su versión oficial del RFC Editor y se contrastaron los fragmentos citados).

Referencias

  1. IETF, RFC 9110, HTTP Semantics (junio de 2022). Secciones: §9.2.2 métodos idempotentes; §10.2.3 Retry-After; §15 y §15.1 clases de códigos, frases recomendadas, código no reconocido tratado como x00, códigos cacheables heurísticamente; §15.3.1 200; §15.5.1 400; §15.5.2 401; §15.5.4 403; §15.5.5 404; §15.5.6 405; §15.5.10 409; §15.5.11 410; §15.5.13 412; §15.5.21 422; §15.6.1 500; §15.6.3 502; §15.6.4 503; §15.6.5 504. https://www.rfc-editor.org/rfc/rfc9110.html
  2. IETF, RFC 9457, Problem Details for HTTP APIs (reemplaza al RFC 7807). §3 y §3.1 miembros y tipo de medio application/problem+json; §3.1.1 type; §3.1.2 status; §3.1.4 detail; §3.2 extensiones; §4.2.1 about:blank; §5 consideraciones de seguridad. https://www.rfc-editor.org/rfc/rfc9457.html
  3. IETF, RFC 6585, Additional HTTP Status Codes, §3 (428 Precondition Required) y §4 (429 Too Many Requests, Retry-After, sin almacenamiento en caché). https://www.rfc-editor.org/rfc/rfc6585.html
  4. IETF HTTPAPI, The Idempotency-Key HTTP Header Field (Internet-Draft draft-ietf-httpapi-idempotency-key-header, revisión 07; borrador, no estándar publicado; vencimiento registrado 18-abr-2026 en el Datatracker). https://datatracker.ietf.org/doc/draft-ietf-httpapi-idempotency-key-header/