Actualización de la API pública · 20 de agosto de 2026
API de cotización y variación intradía
Un estudiante de la Universidad Tecnológica (UTEC) nos escribió por correo pidiendo poder consultar, además de la cotización del día, cuánto se movió dentro del mismo día. Lo hicimos: GET /intraday devuelve, para un día calendario de Montevideo, la apertura, el último valor, el máximo, el mínimo y la lista completa de cambios reales de cada casa de cambio.
La solicitud, y qué no vas a leer acá
El pedido llegó por correo electrónico desde una casilla institucional de UTEC. Publicamos qué se pidió —una funcionalidad de una API pública— y qué hicimos. No publicamos el nombre, la dirección de correo, la firma, el curso, la cátedra ni ningún otro dato que permita identificar a quien escribió, ni acá ni en el repositorio, ni en el historial de commits.
- Que el pedido vino de un estudiante de UTEC.
- El contenido funcional del pedido: cotización y variación dentro del día.
- El endpoint, su contrato, sus límites y cómo se calcula cada campo.
- La fecha en que se publicó el cambio.
- Nombre y apellido.
- Dirección de correo electrónico (ni completa ni parcialmente ofuscada).
- Captura, cita textual o reenvío del mensaje.
- Cualquier dato que, cruzado con otro, vuelva determinable a la persona.
Por qué, en concreto
Una dirección de correo electrónico es un dato personal en los términos del artículo 4 literal D de la Ley Nº 18.331 (Protección de Datos Personales y Acción de Habeas Data): «información de cualquier tipo referida a personas físicas o jurídicas determinadas o determinables». Publicarla sería una comunicación de datos (art. 4 literal B: «toda revelación de datos realizada a una persona distinta del titular»), y el artículo 17 sólo la admite con previo consentimiento del titular, informándole la finalidad e identificando al destinatario. Ese consentimiento no existe: nadie escribe un correo con una consulta técnica esperando figurar en una página web.
El artículo 9 establece el mismo principio con carácter general —el tratamiento es lícito cuando el titular prestó consentimiento «libre, previo, expreso e informado»— y su excepción de «fuentes públicas de información» no aplica: un correo dirigido a nosotros no es una fuente pública. Además, el mensaje traía la cláusula de confidencialidad institucional de UTEC, que limita su uso a su destinatario.
Si nos escribiste alguna vez, la Ley 18.331 te da derecho de acceso (art. 14) y de rectificación, actualización, inclusión o supresión (art. 15) sobre los datos que conservemos, y la acción de habeas data (art. 37) si no te respondemos. El órgano de control es la Unidad Reguladora y de Control de Datos Personales, creada por el artículo 31 como órgano desconcentrado de AGESIC.
Qué se agregó
Un endpoint nuevo, público, sin autenticación y sin límite de llamadas declarado. No maneja ni devuelve datos de personas: sólo precios que las casas de cambio publican.
https://api.cambio-uruguay.com/intradayDevuelve una serie por cotización (casa + moneda + tipo) del día pedido, más el resumen del día. Cachea 30 s mientras el día corre y 1 hora una vez cerrado.
Parámetros
| Parámetro | Valor por defecto | Qué hace |
|---|---|---|
code | USD | Moneda, en ISO de 2 a 5 letras. Las disponibles del día vuelven en availableCurrencies. |
date | hoy | Día calendario YYYY-MM-DD en zona America/Montevideo. No se aceptan fechas futuras. |
origins | todas | Lista de casas separadas por coma. Los ids válidos están en /parameters/origins. |
type | todos | Tipo exacto de cotización. type= (vacío) es la simple; hoy existen además EBROU y TRANSFERENCIA. |
Campos de cada serie
| Campo | Qué es |
|---|---|
openBuy / openSell | Precio con el que la casa arrancó el día. |
lastBuy / lastSell | Último precio conocido del día (el cierre, si el día ya terminó). |
highBuy / lowBuy | Máximo y mínimo de compra alcanzados dentro del día. |
highSell / lowSell | Máximo y mínimo de venta alcanzados dentro del día. |
buyChange / sellChange | Diferencia absoluta entre el último y la apertura, con signo. |
buyChangePct / sellChangePct | La misma diferencia en porcentaje sobre la apertura, con cuatro decimales. |
changes | Cuántas veces se movió el precio en el día. Cero es un dato válido. |
points[] | Cada movimiento: hora exacta, precios nuevos y qué lado cambió. |
openSource | Cómo se reconstruyó la apertura (ver más abajo). |
totals | Resumen del día: cotizaciones, cuántas se movieron, cuántos cambios y promedios. |
Ejemplos
# El dólar de hoy, todas las casas
curl "https://api.cambio-uruguay.com/intraday?code=USD"
# Un día cerrado, sólo BROU e Itaú
curl "https://api.cambio-uruguay.com/intraday?code=USD&date=2026-08-19&origins=brou,itau"
# Sólo la cotización simple, sin las variantes por canal
curl "https://api.cambio-uruguay.com/intraday?code=USD&type="const res = await fetch("https://api.cambio-uruguay.com/intraday?code=USD")
const day = await res.json()
for (const s of day.series) {
if (s.changes === 0) continue
console.log(
s.houseName,
"abrió en", s.openSell,
"cerró en", s.lastSell,
`(${s.sellChangePct > 0 ? "+" : ""}${s.sellChangePct}%)`,
"en", s.changes, "movimientos"
)
}import requests
day = requests.get(
"https://api.cambio-uruguay.com/intraday",
params={"code": "USD", "date": "2026-08-19"},
timeout=10,
).json()
movers = [s for s in day["series"] if s["changes"] > 0]
movers.sort(key=lambda s: abs(s["sellChange"]), reverse=True)
for s in movers[:5]:
print(f'{s["houseName"]:<24} {s["openSell"]:>7} -> {s["lastSell"]:>7} ({s["changes"]} cambios)'){
"asOf": "2026-08-20T18:42:11.004Z",
"date": "2026-08-20",
"dayStart": "2026-08-20T03:00:00.000Z",
"dayEnd": "2026-08-21T03:00:00.000Z",
"coverageEnd": "2026-08-20T18:42:11.004Z",
"isToday": true,
"code": "USD",
"totals": {
"quotes": 46, "quotesChanged": 12, "changes": 19,
"avgOpenSell": 41.28, "avgLastSell": 41.34, "avgSellChangePct": 0.1453
},
"availableCurrencies": ["ARS", "BRL", "EUR", "USD"],
"series": [
{
"origin": "brou", "houseName": "BROU", "code": "USD", "type": "",
"openBuy": 39.2, "openSell": 41.2, "openSource": "ledger-previous",
"lastBuy": 39.5, "lastSell": 41.5, "lastAt": "2026-08-20T13:05:02.000Z",
"highSell": 41.5, "lowSell": 41.2,
"sellChange": 0.3, "sellChangePct": 0.7282,
"changes": 1,
"points": [
{ "at": "2026-08-20T13:05:02.000Z", "buy": 39.5, "sell": 41.5,
"buyChanged": true, "sellChanged": true }
]
}
]
}El dólar de hoy, movimiento por movimiento
Esta tabla se arma con la misma llamada que acabás de ver, sin nada agregado. Si una casa no tocó el precio en todo el día, su fila muestra cero cambios: eso también es información.
Cotizaciones
47
Se movieron
20
Cambios en el día
40
Venta promedio
41,27
| Casa | Apertura (venta) | Último (venta) | Mín · Máx | Variación | Cambios |
|---|---|---|---|---|---|
| Santander | 42,05 | 42,03 | 41,98 · 42,05 | −0,02 | 11 |
| Prex | 40,63 | 40,43 | 40,35 · 40,63 | −0,20 | 4 |
| Scotiabank · TRANSFERENCIA | 41,30 | 41,30 | 41,26 · 41,31 | — | 4 |
| Alter Cambio | 41,60 | 41,45 | 41,35 · 41,60 | −0,15 | 3 |
| Fortex | 40,75 | 40,65 | 40,60 · 40,75 | −0,10 | 3 |
| Cambio Inglés | 41,60 | 41,35 | 41,35 · 41,60 | −0,25 | 1 |
| Cambio Pernas | 41,60 | 41,40 | 41,40 · 41,60 | −0,20 | 1 |
| Itau | 41,50 | 41,30 | 41,30 · 41,50 | −0,20 | 1 |
| Itau · TRANSFERENCIA | 41,20 | 41,00 | 41,00 · 41,20 | −0,20 | 1 |
| Aeromar | 41,35 | 41,25 | 41,25 · 41,35 | −0,10 | 1 |
| BROU | 41,35 | 41,25 | 41,25 · 41,35 | −0,10 | 1 |
| BROU · EBROU | 40,85 | 40,75 | 40,75 · 40,85 | −0,10 | 1 |
| Cambio Argentino | 41,35 | 41,25 | 41,25 · 41,35 | −0,10 | 1 |
| Cambio Federal | 41,35 | 41,25 | 41,25 · 41,35 | −0,10 | 1 |
| Cambio Gales | 41,45 | 41,35 | 41,35 · 41,45 | −0,10 | 1 |
| Cambio Matriz | 41,45 | 41,35 | 41,35 · 41,45 | −0,10 | 1 |
| Cambio Romántico | 41,35 | 41,25 | 41,25 · 41,35 | −0,10 | 1 |
| Cambio Varzy | 41,35 | 41,25 | 41,25 · 41,35 | −0,10 | 1 |
| Cambistar | 41,35 | 41,25 | 41,25 · 41,35 | −0,10 | 1 |
| Cambio Fenix | 41,20 | 41,20 | 41,20 · 41,20 | — | 1 |
Día 2026-08-20 (America/Montevideo) · leído 14:49 · 47 cotizaciones públicas de USD, de las que la tabla muestra las 20 de mayor movimiento y omite 27. La llamada devuelve todas. Excluye la referencia oficial del BCU y los tipos mayoristas, que no son precios a los que una persona pueda operar.
De dónde sale la apertura (y por qué cuesta)
La base guarda una fila por casa y por día, y cada sync la sobrescribe. Esa fila es entonces el cierre del día, nunca su apertura: leerla de ahí daría siempre variación cero. La apertura se reconstruye con el registro de cambios, que sí guarda el valor anterior de cada movimiento. El campo openSource te dice cuál de los cuatro caminos se usó, para que no tengas que confiar a ciegas:
| openSource | Significa |
|---|---|
ledger-previous | Exacto: es el valor anterior del primer cambio del día. Se usa siempre que el día tuvo al menos un movimiento. |
ledger-carried | El día no tuvo movimientos, así que arrastra el último estado registrado antes de que empezara. |
daily-previous | Tampoco había registro previo: se toma el cierre del día anterior de esa casa. |
flat | No hay nada anterior con qué comparar (por ejemplo, el primer día de una casa nueva): apertura y cierre coinciden y la variación es cero por construcción. |
El registro de cambios sólo escribe cuando compra o venta efectivamente se movieron: una verificación que devuelve el mismo precio no genera fila. Por eso changes: 0 significa «no se movió», no «no lo miramos».
Los otros endpoints del mismo tema
GET /changes
El registro crudo de cambios, sin agrupar por día ni por casa.
GET /market-change
Cuánto se movió el promedio del mercado contra exactamente N horas atrás.
GET /analytics/rates
Series por hora o por día para graficar un rango largo, con los huecos marcados.
GET /evolution/{origin}/{code}
La evolución histórica de una casa en meses, no en horas.
Preguntas frecuentes
- ¿Quién pidió este endpoint?
- Un estudiante de la Universidad Tecnológica (UTEC), por correo electrónico. No publicamos su nombre ni su dirección de correo: son datos personales en los términos del artículo 4 de la Ley 18.331 y no tenemos consentimiento para difundirlos, que es lo que exige el artículo 17 para comunicar datos a un tercero.
- ¿Necesito una clave o registrarme para usarlo?
- No. Es un endpoint público, sin autenticación, sobre HTTPS y con CORS abierto. Tampoco recolecta datos de quien lo llama más allá de los registros técnicos normales de un servidor web.
- ¿Cada cuánto se actualizan los datos?
- Las casas se consultan cada cinco minutos y sólo se escribe una fila cuando el precio efectivamente cambió. La respuesta se cachea 30 segundos mientras el día corre y una hora una vez que el día cerró, porque un día terminado ya no puede cambiar.
- ¿Hasta qué día para atrás puedo pedir?
- Podés pedir cualquier día pasado del que haya datos. Ahora bien, el registro de cambios intradía arrancó mucho después que el histórico diario: para los días anteriores a ese arranque vas a ver la apertura reconstruida con openSource daily-previous y cero puntos intradía, porque esos movimientos nunca se registraron.
- ¿Por qué no aparece la cotización del BCU?
- Porque no es un precio al que una persona pueda operar: es la referencia oficial del Banco Central. Por el mismo criterio quedan afuera los tipos mayoristas (INTERBANCARIO, CABLE, PROMED.FONDO). La cotización oficial está en el endpoint /bcu.
- Necesito otra cosa de la API, ¿cómo la pido?
- Escribinos desde la página de contacto o abrí un issue en el repositorio de GitHub. Un issue es público y no expone tu correo; si preferís escribir por mail, tu dirección no se publica.