OpenAPI es un formato para describir una API HTTP en un archivo YAML o JSON corriente. Ahí quedan listadas las rutas que expone la API, los métodos que aceptan, los campos que viajan en la petición y lo que devuelve la respuesta. El archivo lo leen tanto las personas como los programas, y de esa misma fuente se generan la documentación, las bibliotecas cliente y los simuladores para pruebas.
Su nombre formal es OpenAPI Specification, abreviado OAS. La mantiene la OpenAPI Initiative, un proyecto de gobernanza abierta dentro de la Linux Foundation.
A continuación: cómo se arma el documento, en qué se diferencia de Swagger, qué cambió en las versiones 3.1 y 3.2, y por qué la promesa central del formato —documentación que nunca queda desactualizada— no se cumple sola.
Cómo se ve un documento OpenAPI
Un archivo que funciona es más corto de lo que suele imaginarse:
openapi: 3.1.0
info:
title: Enlaces
version: "1.0"
paths:
/links:
post:
summary: Crear un enlace corto
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [url]
properties:
url:
type: string
format: uri
responses:
'201':
description: Enlace creado
Solo dos campos de nivel superior son obligatorios, openapi e info, y el documento debe incluir al menos uno de paths, components o webhooks. Así lo define la especificación OAS 3.1.1.
En esas primeras líneas ya hay una trampa: openapi e info.version no son lo mismo. El primero es la versión del formato, la que las herramientas leen para saber cómo interpretar el documento. El segundo es la versión de tu API. La especificación lo dice de forma explícita, pero la intuición sugiere lo contrario, y así aparecen archivos que declaran openapi: 1.0.
Las especificaciones grandes suelen repartirse en varios archivos mediante $ref: el documento raíz apunta a ./paths/links.yaml, y ese a ./schemas/link.yaml. Es cómodo y genera un problema al que vuelvo al final.
OpenAPI y Swagger no son lo mismo
Es la confusión más habitual del tema.
| Qué es | Quién lo mantiene | |
|---|---|---|
| OpenAPI | el estándar, el formato en sí | OpenAPI Initiative, Linux Foundation |
| Swagger | un conjunto de herramientas que leen ese formato | SmartBear |
La historia explica el enredo. Al principio Swagger designaba a la vez la especificación y las herramientas. Después la especificación pasó a la OpenAPI Initiative y se convirtió en OpenAPI Specification, mientras que el nombre Swagger quedó para las herramientas: Swagger UI, Swagger Editor y compañía.
En la práctica, «tenemos Swagger» casi siempre significa «tenemos un archivo OpenAPI y lo mostramos con Swagger UI». Nada ata una cosa a la otra: el mismo archivo se abre igual de bien en Redoc, en Scalar o en un generador de clientes.
Versiones: 3.0, 3.1 y 3.2
Las diferencias entre ramas no son cosméticas.
3.0 sigue siendo la más extendida. Su punto débil son los esquemas de datos: se parecen a JSON Schema sin ser del todo compatibles con él, y eso rompe la interoperabilidad entre herramientas una y otra vez.
3.1 cerró esa brecha. El objeto Schema pasó a ser un superconjunto de JSON Schema Draft 2020-12, así que los esquemas que ya mantienes en JSON Schema se reutilizan en lugar de reescribirse. La revisión vigente, la 3.1.1, se publicó el 24 de octubre de 2024.
3.2 llegó en septiembre de 2025. Según el anuncio de la OpenAPI Initiative, lo más destacable es:
- el método HTTP
query, para lecturas idempotentes cuyos parámetros no caben en la URL; additionalOperationspara métodos no estándar;- tipos de medios en streaming: Server-Sent Events, JSON Lines, secuencias JSON;
- etiquetas jerárquicas, con
summary,parentykind; - flujo de dispositivo de OAuth 2.0, para aparatos con entrada incómoda.
Conviene elegir la versión que tus herramientas soportan de verdad, no el número más alto. El soporte de 3.1 en el ecosistema es sólido; 3.2 es más reciente y parte de las herramientas todavía está poniéndose al día. Nuestra propia especificación declara 3.1.0.
Para qué sirve en la práctica
Un solo archivo cubre varias tareas a la vez:
- documentación interactiva: Swagger UI o Redoc construyen la página directamente desde el archivo, con formulario para probar peticiones;
- bibliotecas cliente: los generadores producen un SDK para tu lenguaje en lugar de envoltorios escritos a mano;
- simuladores: un servidor simulado puede levantarse a partir de la descripción antes de que exista el backend, así el frontend no queda bloqueado;
- pruebas de contrato: los tests comparan las respuestas reales con los esquemas y detectan el momento en que una respuesta deja de coincidir con su descripción.
De esa lista sale el argumento principal a favor de OpenAPI: la documentación deja de ser un texto aparte que alguien se olvida de actualizar.
El argumento es correcto. Lo que no es, es automático.
Nada conecta la especificación con el código
Esta es la parte que los artículos de «qué es OpenAPI» casi nunca mencionan, y la que golpea en producción.
El archivo de especificación es solo un archivo. El enrutador no sabe de su existencia. El controlador tampoco. Puedes eliminar un endpoint, añadir otro o convertir un campo obligatorio en opcional, y no fallará ni una sola prueba. La documentación no se sincroniza sola: sigue siendo exacta exactamente mientras alguien se acuerde de editarla.
La divergencia ocurre en ambos sentidos, y cada uno duele de forma distinta:
- documentado pero no funciona. La persona lee la documentación, envía la petición y recibe un 404. La confianza en el resto de la documentación se derrumba en el acto: si aquí mintieron, ¿dónde más?
- funciona pero no está documentado. La capacidad sencillamente no existe para quien la busca. Nadie la encuentra, nadie paga por ella.
El segundo caso es el más peligroso, porque nadie se queja. No hay quién: el usuario no sabe que ese endpoint está ahí.
Es justo lo que nos pasó. La API de Lix.li tenía tres endpoints de conversiones en funcionamiento —un listado de eventos y la recepción de postbacks, individual y por lotes— y ninguno figuraba en la especificación. El código funcionaba, las pruebas pasaban, y mientras tanto la página de la API mostraba dos rutas distintas que no existían en el código. Salió a la luz en una revisión completa, no por un reporte de error.
Cómo detectar la divergencia de forma automática
Esto no se arregla con disciplina, sino con una prueba. La idea es simple: tomar la lista de rutas de la especificación, tomar la lista de rutas del enrutador y comparar ambos conjuntos en las dos direcciones.
rutas de openapi.yaml → ¿existe ese endpoint? → si no, la documentación miente
rutas del enrutador → ¿está documentada? → si no, la función es invisible
La mitad de «lo documentado funciona» se comprueba cómodamente con una petición real: llamar al endpoint sin credenciales y verificar que la respuesta sea 401 y no 404. La distinción importa. 401 significa que la ruta existe y pide clave; 404, que no hay tal ruta. No se escribe nada en el proceso, porque la autenticación se ejecuta antes que el controlador.
La comprobación inversa se hace leyendo la configuración de rutas. Además obliga a enumerar las excepciones de forma explícita: endpoints internos que no pintan nada en un contrato público. Esa lista ya vale por sí sola, porque te hace decidir una vez, de forma deliberada, qué consideras tu API pública.
Escribimos esa prueba después del episodio de las conversiones. Se justificó de inmediato: si vuelves a quitar las conversiones de la especificación, la prueba falla y nombra las dos rutas ausentes.
Dos trampas que se descubren tarde
$ref y las rutas relativas. Cuando una especificación está repartida en varios archivos, las referencias internas se resuelven en relación con la URL desde la que se sirvió el archivo. Un documento raíz disponible en /openapi buscará ./paths/links.yaml en /paths/links.yaml y no encontrará nada. En el navegador todo parece correcto, porque Swagger UI carga el archivo desde su ubicación real, pero un generador de clientes apuntado al mismo documento se va a tropezar. O sirves la especificación únicamente desde su propio directorio, o la empaquetas en un archivo único sin $ref externos.
Swagger UI está vacío para un rastreador. Una página de Swagger UI son unas pocas líneas de marcado más un script que dibuja el contenido en el navegador. En el HTML de origen no hay ni una sola ruta de endpoint. Para quien lee, la página está bien; para la indexación, está en blanco. Súmale un error frecuente en robots.txt: una regla Disallow: /api/ escrita para ocultar los endpoints JSON oculta también la documentación para humanos que vive bajo el mismo prefijo. Conviene bloquear la ruta exacta de la versión —/api/1.0/, por ejemplo— y no la sección entera.
Por dónde empezar
Si la API ya existe y la descripción no, el orden es más o menos este:
- Describe un endpoint a mano. Con eso basta para entender la estructura.
- Elige la versión que tus herramientas soportan con soltura; por defecto, la 3.1.
- Reparte el archivo con
$refcuando deje de caber en una pantalla, y decide de inmediato desde qué URL se sirve. - Añade una prueba que compare la especificación con el enrutador en las dos direcciones. Hasta ese paso, cualquier afirmación sobre documentación al día es una promesa, no una propiedad.
Para ver cómo queda esto en una API real, la documentación de la API de Lix.li cubre enlaces, grupos, pruebas A/B y conversiones. Del servicio construido alrededor de esa API habla la página de la API para acortar enlaces, y en temas vecinos están qué son los webhooks, el análisis de las redirecciones 301, 302, 307 y 308 y el SDK de PHP para integrarla desde código.