Formatos de respuesta
Todos los endpoints con API key pueden devolver CSV o XML además de JSON. El endpoint, los datos y los límites son exactamente los mismos: lo único que cambia es la codificación.
Cómo se pide
De dos maneras. Si mandas las dos, gana el query param.
Valores aceptados en format: json (por defecto), csv y xml. En Accept: application/json, text/csv, application/xml y text/xml.
El query param gana sobre el header a propósito. Los navegadores mandan siempre un Accept que incluye application/xml y un comodín */*; si el header tuviera prioridad, abrir una URL de la API en el navegador devolvería un formato impredecible. Por eso un Accept que pida HTML se ignora por completo: no es una petición de formato, es lo que el navegador manda siempre.
Planes
CSV y XML requieren Esencial o superior. Pedirlos desde Gratis o Inicio devuelve 402 feature_not_in_plan. JSON es el formato por defecto y está disponible en todos los planes — pedirlo explícitamente con ?format=json nunca cobra nada.
CSV
Se emite según RFC 4180: coma como separador, punto decimal y comillas escapadas duplicando la comilla. Un CSV pedido desde el navegador se descarga como archivo en vez de mostrarse como texto.
La regla: un CSV es una tabla, y la mayoría de las respuestas de la API no lo son. Cotizave la resuelve así:
- Si la respuesta tiene exactamente una colección (un arreglo de objetos), esa colección son las filas. Los campos sueltos que la acompañan se repiten en cada fila, prefijados los de la colección con su nombre.
- Si no tiene ninguna, la respuesta entera es una sola fila, con los campos anidados como
from.currency. - Si tiene dos o más, se devuelve
406. No se elige una: descartar la otra en silencio te dejaría con la mitad de la respuesta sin forma de notarlo.
Las columnas de la colección van prefijadas (rates.mid) porque sin eso se repetirían nombres: base es la moneda base del snapshot y rates.base es la de esa cotización — eur_reference cotiza en euros. El prefijo va siempre, aunque ese día no haya choque, para que la cabecera no cambie de forma según los datos y puedas parsearla con un script fijo.
Las columnas son la unión de todas las filas. Un campo que solo trae alguna fila — como effective_date, que solo existe en las tasas de referencia del BCV — tiene su columna igual, vacía donde no aplica.
Nota para Excel en español: Excel configurado en español espera punto y coma como separador y coma decimal. Si abres el archivo con doble clic verás todo en una columna; usa Datos → Desde texto/CSV y elige coma como delimitador. Google Sheets lo abre bien sin configurar nada.
XML
Sin atributos, sin namespaces y sin esquema: cada campo es un elemento con el mismo nombre que su clave JSON, así que la traducción entre los dos formatos es mecánica.
Los elementos de un arreglo se llaman siempre <item>, nunca el singular del campo que los contiene. Así /response/rates/item es un XPath que vale para cualquier arreglo de la API, hoy y cuando se agregue uno nuevo.
Un campo nulo sale como elemento vacío (<campo></campo>). Los campos opcionales ausentes simplemente no aparecen, igual que en JSON.
Los errores siempre son JSON
Aunque pidas CSV o XML, cualquier respuesta de error llega en JSON con Content-Type: application/json y la estructura habitual de code y message. Si tu integración parsea CSV, revisa el código de estado antes de parsear el cuerpo.
Los tres errores propios de esta funcionalidad:
400 invalid_format— el valor deformatno esjson,csvnixml.402 feature_not_in_plan— tu plan no incluye formatos alternativos.406 format_not_available— el formato existe pero no puede representar esa respuesta. Hoy solo ocurre con CSV en respuestas con dos colecciones; el mensaje te dice cuáles son.
Dónde funciona
En todos los endpoints que se consumen con API key: los de tasas (/v1/fx/*) y las utilidades de Venezuela (bancos, RIF, cuentas, feriados y días hábiles).
Dos excepciones que conviene tener presentes:
GET /v1/fx/routeno se puede pedir en CSV. Devuelve dos colecciones —alternativesyskipped— y no hay una sola tabla que las represente. Responde406; en XML y JSON funciona con normalidad.- Los endpoints públicos sin credencial siempre responden JSON. Al no llevar API key no hay plan que consultar, así que
?format=ahí devuelve400 unknown_parameteren vez de ignorarse en silencio.