Versionado de API REST: URL, cabecera o query, cuál encaja
7 min de lectura
El versionado de API REST es la forma en que una API le indica a un llamante qué contrato está usando una petición, para que el servidor pueda cambiar el contrato sin romper a los llamantes que no se han movido. Hay tres sitios donde puede ir la versión: la ruta de la URL (/v2/orders), una cabecera de la petición (Accept: application/vnd.acme.v2+json o una personalizada como Api-Version: 2026-10-01), o un parámetro de query (/orders?version=2). La ruta es la más común porque se ve en todas partes, la cabecera es la más limpia para la URL, y la query string es la que más equipos acaban lamentando. Cuál encaja depende de quiénes son tus llamantes y de cómo piensas avisarles de los cambios.
| Dónde vive la versión | Ejemplo | Quién la ve | Cacheable por defecto | Quién la usa |
|---|---|---|---|---|
| Ruta de la URL | GET /v2/orders | Cada log, enlace, proxy y navegador | Sí | Twitter, Shopify, la mayoría de las APIs públicas |
| Cabecera personalizada | Api-Version: 2026-10-01 | Solo los clientes que la envían | Con Vary: Api-Version | Stripe (Stripe-Version), GitHub (X-GitHub-Api-Version) |
| Cabecera Accept | Accept: application/vnd.acme.v2+json | Solo los clientes que la envían | Con Vary: Accept | Media types de GitHub, algunas APIs enterprise |
| Parámetro de query | GET /orders?api-version=2 | Logs y enlaces, mezclada con los filtros | Sí, pero contamina las claves de caché | APIs REST de Azure |
| Sin versión | GET /orders más cambios solo aditivos | Nadie | Sí | APIs internas y de estilo GraphQL |
¿Qué son v1 y v2 en una API REST?
v1 y v2 son etiquetas para dos versiones del contrato de la API: los mismos recursos, accesibles en rutas distintas, donde v2 puede renombrar campos, cambiar tipos, retirar endpoints o alterar la forma de los errores de los que dependen los llamantes de v1. Una petición a /v1/orders sigue recibiendo la forma antigua mientras v1 tenga soporte; una petición a /v2/orders recibe la nueva. El número es una promesa de compatibilidad, y esa promesa es lo que lo hace útil. Subirlo en cada lanzamiento convierte la etiqueta en ruido.
La mayoría de las APIs solo necesitan una versión mayor nueva para un cambio que rompe a los llamantes existentes. Añadir un campo, un endpoint nuevo o un parámetro opcional nuevo puede salir bajo la versión actual, porque un cliente bien escrito ignora lo que no conoce. Cuándo versionar una API, y qué hacer en su lugar cubre la decisión; este artículo trata de dónde va la versión en el versionado de API REST una vez que has decidido subirla.
¿El versionado de API REST debería ir en la URL o en una cabecera?
Ponlo en la URL si entre tus llamantes hay personas que escriben peticiones en un navegador, copian ejemplos de la documentación o leen logs; ponlo en una cabecera si quieres una URL estable por recurso y todos tus llamantes son SDKs o código que controlas. La ruta de la URL es explícita como ninguna otra opción: un ticket de soporte con la línea de la petición ya te dice la versión. Una cabecera hay que enviarla en cada petición, es invisible en un enlace y se pierde con facilidad en un proxy, por eso las APIs versionadas por cabecera siempre definen un valor por defecto para las peticiones que no la envían.
El enfoque por cabecera tiene una ventaja real más allá de las URLs limpias: separa la identidad del recurso de su representación, que es como se diseñó HTTP. El RFC 9110 describe la negociación de contenido mediante Accept, y un media type como application/vnd.github.v3+json es una versión expresada de ese modo. En la práctica, la pureza del diseño importa menos que si tus llamantes se acuerdan de enviar la cabecera.
¿Cómo funciona el versionado por fecha?
En lugar de v1 y v2, la versión es una fecha, y cada cambio que rompe algo recibe una fecha nueva. El llamante fija la fecha contra la que integró, normalmente una vez a nivel de cuenta, y conserva ese comportamiento hasta que decide moverse. La cabecera Stripe-Version de Stripe y la X-GitHub-Api-Version de GitHub funcionan así.
El coste está en la implementación. Cada cambio que rompe algo se convierte en una pequeña capa de transformación que convierte entre la fecha fijada y la forma actual, y el servidor las apila: una petición fijada dos años atrás pasa por todos los cambios desde entonces. Eso es más pesado de construir que una segunda copia de los handlers, y premia a una API que cambia poco. Los equipos que publican un cambio que rompe algo al mes acaban con docenas de transformaciones. Versionado de la API de Stripe mira el modelo desde el lado del llamante, incluido lo que el changelog tiene que cargar.
¿Por qué se desaconseja versionar por query string?
Porque mezcla el contrato con los filtros. /orders?status=open&api-version=2 hace que la versión parezca un refinamiento opcional y no lo que decide la forma de cada campo de la respuesta. También es la más fácil de perder por accidente, ya que las librerías cliente reconstruyen las query strings, algunos proxies las recortan y la gente las edita a mano. La propia guía de diseño de APIs REST de Microsoft documenta la forma con parámetro de query que usa Azure, y el ecosistema de SDKs generados a su alrededor es lo que la hace viable; sin esas herramientas, un llamante que olvida el parámetro recibe en silencio la versión por defecto.
¿Qué pasa con la versión antigua?
Sigue funcionando, con una fecha en la que deja de hacerlo. El sentido del versionado es dejar que los llamantes avancen según su propio calendario, y eso solo es cierto si la versión antigua sigue disponible el tiempo suficiente para que lo hagan y se les avisa de cuándo termina. La guía de versionado de APIs de Google describe el ciclo de vida que usa para sus propias APIs: una versión estable tiene soporte hasta una fecha de deprecación publicada, con el reemplazo disponible durante toda la ventana de deprecación.
El aviso es la parte que la mayoría de los equipos construye a medias. Una ruta o cabecera nueva solo sirve a los llamantes que saben que existe, saben qué cambió y saben cuándo desaparece v1. Son tres hechos, y cada uno necesita un sitio: una entrada con fecha en el changelog de API con las diferencias a nivel de campo, un aviso de deprecación que indique la fecha de fin, y una guía de migración que recorra cada cambio para un llamante que sigue en la versión antigua. Retirar una versión de API cubre el paso final, incluida la cabecera Sunset que permite a un cliente descubrir la fecha en cada respuesta.
¿Se puede evitar por completo el versionado de API REST?
Sí, mientras cada cambio sea aditivo. Añade campos, nunca los elimines ni los renombres; añade endpoints, nunca cambies lo que devuelve uno existente; acepta parámetros opcionales nuevos, nunca cambies el significado de uno antiguo. Muchas APIs internas funcionan durante años solo con esta regla, y es la regla que los esquemas GraphQL siguen por diseño. El coste es un contrato que solo crece, con campos deprecados que tienen que quedarse porque alguien, en algún sitio, todavía los lee.
Cuando un cambio aditivo es imposible, la versión es la herramienta. La elección entre ruta, cabecera y fecha decide qué ven los llamantes y cómo se anuncia el cambio; lo que no cambia es que el cambio tiene que quedar escrito en algún sitio que un llamante vaya a leer. Para una API con llamantes externos ese sitio es el changelog, y el artículo sobre breaking changes explica qué cuenta como uno.
FAQ
¿Cuál es la mejor forma de versionar una API REST? Una versión mayor en la ruta de la URL es la más entendida y la más difícil de hacer mal, lo que la convierte en la opción por defecto para una API pública. Una versión en cabecera mantiene las URLs estables y va bien en APIs dirigidas por SDK; una cabecera con fecha va bien en APIs que cambian poco y quieren que los llamantes fijen el comportamiento.
¿Qué es v1 en la URL de una API?
Es la primera versión mayor del contrato de la API. A las peticiones a /v1/... se les promete la forma documentada para la versión 1, incluso después de que exista una /v2/ con campos o comportamiento distintos.
¿Necesito una versión de API nueva para cada cambio? No. Los cambios aditivos, como campos, endpoints y parámetros opcionales nuevos, pueden salir bajo la versión existente. Solo un cambio que rompe a los llamantes existentes necesita una nueva.
¿Cuánto tiempo debería mantenerse una versión de API antigua? El suficiente para que los llamantes migren, lo que en una API pública suele ser de seis a doce meses desde que el reemplazo está disponible, con la fecha publicada en el anuncio y repetida en el aviso de deprecación.
Las afirmaciones técnicas de este artículo no se han revisado de forma independiente. Si algo está mal, avísanos y lo corregiremos.