Obtener tipos de cambio en vivo en Power BI es una de esas tareas que parecen terminadas mucho antes de estarlo. Pegas una URL de API en Obtener datos → Web, aparece una tabla de tasas, tu medida de ingresos convertidos se enciende y publicas. Dos días después, la actualización del conjunto de datos falla en el servicio de Power BI con un mensaje sobre un origen de datos dinámico, o bien funciona y convierte silenciosamente cada transacción histórica al tipo de cambio de hoy.
Esta guía cubre el camino completo: una consulta M de Power Query que devuelve una tabla de tasas limpia, el patrón RelativePath que la mantiene actualizable en el servicio, una función de tasas históricas para convertir a la fecha de la transacción, una medida DAX para una moneda de reporte dinámica y las cuentas de peticiones que deciden si tu programación de actualización cabe dentro de un plan de API.
Todas las consultas siguientes están escritas contra las formas de respuesta documentadas de la API de Finexly. Si ya leíste nuestra guía sobre tipos de cambio en vivo en Excel, el código M te resultará familiar, pero Power BI añade una capa de actualización del lado del servicio que Excel no tiene, y es justo ahí donde se rompen la mayoría de estos proyectos.
Las tres formas de llevar tasas a Power BI
| Enfoque | ¿Se actualiza en el servicio? | ¿Clave de API segura? | Tasas históricas | Ideal para |
|---|---|---|---|---|
| Conector Web, URL pegada en el cuadro de diálogo | A menudo no: la URL suele convertirse en un origen de datos dinámico | ❌ La clave queda en la URL | No | Una prueba de concepto de cinco minutos |
Consulta en blanco con Web.Contents + RelativePath | ✅ Sí | ✅ Mediante cabecera | ✅ Sí | Casi cualquier modelo real |
| Dataflow (o canalización de Fabric) que alimenta una tabla de tasas | ✅ Sí, y desacoplado de la actualización del informe | ✅ Mediante cabecera | ✅ Sí | Varios informes, muchas filas |
Método 1: una tabla de tasas en vivo con Power Query
Abre Transformar datos → Nuevo origen → Consulta en blanco → Editor avanzado y pega esto. Devuelve una fila por par de divisas con una marca de tiempo UTC de recuperación.
let
ApiKey = "YOUR_API_KEY",
Base = "USD",
Symbols = "EUR,GBP,JPY,CHF,AUD,CAD,SEK,NZD",
Source = Json.Document(
Web.Contents(
"https://api.finexly.com",
[
RelativePath = "v1/latest",
Query = [ base = Base, symbols = Symbols ],
Headers = [ #"Authorization" = "Bearer " & ApiKey ]
]
)
),
Rates = Record.ToTable( Source[rates] ),
Renamed = Table.RenameColumns( Rates, {{"Name", "Quote"}, {"Value", "Rate"}} ),
AddBase = Table.AddColumn( Renamed, "Base", each Base, type text ),
AddStamp = Table.AddColumn( AddBase, "RetrievedUTC", each DateTimeZone.UtcNow(), type datetimezone ),
Typed = Table.TransformColumnTypes(
AddStamp,
{{"Quote", type text}, {"Rate", type number}}
)
in
TypedEl endpoint al que llama se ve así desde la línea de comandos, y conviene ejecutarlo una vez para ver la estructura que estás analizando:
curl "https://api.finexly.com/v1/latest?base=USD&symbols=EUR,GBP,JPY" \
-H "Authorization: Bearer YOUR_API_KEY"{
"success": true,
"base": "USD",
"date": "2026-09-01",
"rates": { "EUR": "…", "GBP": "…", "JPY": "…" }
}Por qué RelativePath y Query no son opcionales
Esta es la línea más importante del artículo, así que se lleva su propio encabezado.
Si construyes la URL como una sola cadena concatenada — "https://api.finexly.com/v1/latest?base=" & Base — Power Query no puede determinar el destino hasta que la consulta se ejecuta. Microsoft clasifica eso como un origen de datos dinámico, y los orígenes dinámicos no se actualizan en el servicio de Power BI, por razones de seguridad y privacidad. El informe se actualizará perfectamente en tu portátil y fallará en cuanto se programe.
Pasar la ruta en RelativePath y los parámetros en Query es la excepción documentada. Power BI puede entonces resolver una única URL base estática — https://api.finexly.com — para las comprobaciones de credenciales y privacidad, mientras que las partes variables siguen siendo variables. De ahí se derivan tres reglas:
- La URL base debe ser una cadena literal. Sin parámetros, sin concatenación, sin ningún
&. RelativePathdebe ser la ruta fija del endpoint."v1/latest", no"v1/latest?base=USD".- Nunca concatenes dentro de
Query. Pasa un registro de pares nombre/valor y deja que Power Query haga la codificación de URL. También escapará caracteres que de otro modo romperían la petición.
Configurar las credenciales
La primera vez que ejecutes la consulta, Power BI preguntará cómo autenticarse en https://api.finexly.com. Elige Anónimo. Suena mal, pero es lo correcto: la clave de API viaja en la cabecera Authorization que proporcionaste en el código M, no a través del almacén de credenciales de Power BI. Si eliges API web o Básica, Power BI añadirá su propia cabecera y la petición será rechazada.
Configura el nivel de privacidad como Público u Organizativo de forma coherente en todos tus orígenes. Los niveles de privacidad incompatibles son la segunda causa más común de una actualización que funciona en Desktop y falla en el servicio: Power BI bloquea la consulta antes que arriesgarse a filtrar datos de un origen en la petición de otro.
Una advertencia honesta: la clave queda almacenada como texto plano dentro de la consulta. Cualquiera que abra el .pbix puede leerla. Para cualquier cosa compartida más allá de tu propia máquina, convierte la clave en un parámetro de Power Query y mantén la versión rellenada en un dataflow propiedad de una cuenta de servicio, para que los autores de informes consuman la tabla de tasas sin ver nunca la credencial.
Método 2: tasas históricas para convertir a la fecha de la transacción
Una tabla de tasas en vivo responde a "¿cuánto vale EUR/USD ahora mismo?". No puede responder a "¿cuáles fueron nuestros ingresos de enero en USD?", y usarla para eso es el error más caro de todo este tema: reexpresar las cifras del trimestre pasado porque el modelo las reconvirtió a una tasa nueva es exactamente el resultado que buscan los auditores. Si tu informe alimenta algo que tendrás que defender más adelante, lee también nuestra guía sobre tipos de cambio y reporte fiscal.
Necesitas una tabla de tasas indexada por fecha. Esta función M envuelve el endpoint de series temporales y devuelve una fila por fecha y divisa:
let
FxHistory = (base as text, symbols as text, startDate as date, endDate as date) as table =>
let
ApiKey = "YOUR_API_KEY",
Source = Json.Document(
Web.Contents(
"https://api.finexly.com",
[
RelativePath = "v1/timeseries",
Query = [
base = base,
symbols = symbols,
start_date = Date.ToText( startDate, [Format = "yyyy-MM-dd", Culture = "en-US"] ),
end_date = Date.ToText( endDate, [Format = "yyyy-MM-dd", Culture = "en-US"] )
],
Headers = [ #"Authorization" = "Bearer " & ApiKey ]
]
)
),
Days = Table.RenameColumns( Record.ToTable( Source[rates] ), {{"Name", "RateDate"}} ),
Expanded = Table.ExpandRecordColumn( Days, "Value", Record.FieldNames( Days{0}[Value] ) ),
Unpivoted = Table.UnpivotOtherColumns( Expanded, {"RateDate"}, "Quote", "Rate" ),
AsDate = Table.TransformColumns(
Unpivoted,
{{"RateDate", each Date.FromText( _, [Format = "yyyy-MM-dd", Culture = "en-US"] ), type date}}
),
AddBase = Table.AddColumn( AsDate, "Base", each base, type text ),
Typed = Table.TransformColumnTypes( AddBase, {{"Quote", type text}, {"Rate", type number}} )
in
Typed
in
FxHistoryDos detalles ahí hacen trabajo real:
Culture = "en-US"tanto enDate.ToTextcomo enDate.FromText. Sin eso, una máquina configurada en alemán o francés envía01.09.2026comostart_datey la API lo rechaza; o peor, la actualización de un compañero produce una tabla con otra forma que la tuya. La configuración regional es la variable invisible de todo proyecto de Power Query que abarque más de un país.Table.UnpivotOtherColumns. La API devuelve las fechas como claves de registro con un registro anidado de divisas. Despivotar a una forma largaRateDate / Base / Quote / Ratete da una tabla que se une limpiamente a una dimensión de fecha y que no hay que rehacer cada vez que añades una divisa.
Invócala una vez por carga: FxHistory( "USD", "EUR,GBP,JPY", #date(2026,1,1), Date.From( DateTime.LocalNow() ) ).
Como las tasas de una fecha pasada nunca cambian, esta tabla es el caso de manual para la actualización incremental: particiona por RateDate, actualiza los últimos 7 días y archiva el resto. Tu tiempo de actualización deja de crecer con el histórico, y tu recuento de peticiones también.
No llames a la API una vez por fila
El patrón que mata estos modelos es invocar una función de tasa como columna personalizada sobre la tabla de hechos. Diez mil transacciones significan diez mil peticiones HTTP por actualización, una actualización de cuarenta minutos y una factura de cuota que llega antes de comer.
Haz las cuentas antes de construir. Power BI Pro permite 8 actualizaciones programadas por conjunto de datos y día; Premium Per User y la capacidad Fabric permiten 48. Una sola llamada a /v1/latest que cubra todas las divisas que necesitas, actualizada al máximo de Pro, cuesta 8 peticiones al día — unas 240 al mes, cómodamente dentro de un plan gratuito de 1.000. La misma programación con una función por fila no tiene límite. Incluso al máximo de 48 actualizaciones de PPU, una llamada consolidada se queda en unas 1.440 peticiones al mes, que es un plan de pago pequeño en lugar de una catástrofe por fila. Nuestros planes de precios detallan los umbrales si necesitas dimensionarlo con precisión.
Cuando más de un informe necesita las tasas, mueve la consulta a un dataflow. El dataflow llama a la API según su propia programación y materializa el resultado; cada conjunto de datos aguas abajo lee del almacenamiento en lugar de volver a golpear la API. Cinco informes sobre el mismo dataflow hacen un conjunto de peticiones, no cinco.
Convertir importes: combinación en Power Query o medida DAX
Hay dos lugares legítimos donde aplicar la conversión, y responden a preguntas distintas.
Opción A: combinar en Power Query
Si tu moneda de reporte es fija — todo se reporta en USD, punto — haz la unión en el momento de la carga.
- Carga la tabla de hechos en Power Query.
- Combinar consultas contra la tabla de tasas, emparejando por código de divisa y fecha. Mantén
Ctrly selecciona las columnas en el mismo orden en ambas tablas. - Expande solo la columna
Ratey ponla como Número decimal fijo. - Selecciona
AmountyRate, y luego Agregar columna → Estándar → Multiplicar. - Desactiva la carga de la tabla de tasas si nada más la referencia.
Es rápido, se materializa una vez y no puede ser alterado por una segmentación. Ese último punto es la contrapartida.
Opción B: una medida DAX con moneda de reporte seleccionable
Si los usuarios necesitan alternar todo el informe entre USD, EUR y GBP, la conversión tiene que ocurrir en tiempo de consulta. Añade una tabla desconectada Reporting Currency con una columna de códigos ISO, ponla en una segmentación y escribe:
Revenue (Reporting Currency) =
VAR ReportingCurrency = SELECTEDVALUE( 'Reporting Currency'[Code], "USD" )
RETURN
SUMX (
'Sales',
VAR TxCurrency = 'Sales'[CurrencyCode]
VAR TxDate = 'Sales'[OrderDate]
VAR Rate =
CALCULATE (
MAX ( 'FX Rates'[Rate] ),
REMOVEFILTERS ( 'FX Rates' ),
'FX Rates'[Base] = TxCurrency,
'FX Rates'[Quote] = ReportingCurrency,
'FX Rates'[RateDate] = TxDate
)
RETURN 'Sales'[Amount] * Rate
)Tres cosas a tener en cuenta:
- Las líneas
VARson estructurales. CapturarTxCurrencyyTxDateen variables antes deCALCULATElas fija a la fila actual deSUMX. Referenciar las columnas directamente dentro de los argumentos de filtro invita a un error de transición de contexto que produce totales verosímiles pero incorrectos. REMOVEFILTERS( 'FX Rates' )impide que cualquier filtro entrante sobre la tabla de tasas estreche la búsqueda.- Una tasa ausente devuelve
BLANK(), yAmount * BLANK()esBLANK(). Ese es el comportamiento que quieres. Una fila que cae silenciosamente al importe sin convertir es una fila que infla tu total en lo que valga el tipo de cambio.
La trampa de las relaciones
No crees una relación física entre la tabla de hechos y la de tasas solo por el código de divisa. El código de divisa no es único en una tabla indexada por fecha, así que Power BI propone una relación de muchos a muchos, y muchos a muchos con filtrado bidireccional multiplicará alegremente tus filas y tus ingresos. Usa la combinación compuesta en Power Query, o mantén la tabla de tasas desconectada y busca las tasas en DAX como arriba.
Cinco errores que producen números equivocados sin dar error
- URLs concatenadas. Se actualiza en Desktop, falla en el servicio con "Este conjunto de datos incluye un origen de datos dinámico". Se arregla con
RelativePathyQuery. - Pares invertidos.
base=USD&symbols=EURdevuelve USD→EUR. Si tu tabla de hechos guarda importes en EUR, necesitas el recíproco. Comprueba a mano un par conocido antes de fiarte de ningún total. Nuestra referencia ISO 4217 ayuda si dudas qué código va en cada lado. - Tasa de hoy sobre filas históricas. El número cambia en cada actualización y nadie se da cuenta hasta que alguien compara dos exportaciones del mismo informe.
- Fechas y decimales con formato regional. Una coma decimal convierte
1,0842en texto,Table.TransformColumnTypesdevuelve un error que el modelo trata como vacío, y las filas afectadas desaparecen de tus totales. - Redondear en el paso equivocado. Redondea una sola vez, en la presentación, después de multiplicar. Redondear la tasa a cuatro decimales antes de multiplicar un importe de siete cifras introduce una discrepancia visible frente al sistema contable.
Preguntas frecuentes
¿Puede Power BI actualizar los tipos de cambio automáticamente?
Sí. Un conjunto de datos publicado puede programarse hasta 8 actualizaciones al día en Power BI Pro y 48 al día en Premium Per User o capacidad Fabric. El endpoint XMLA de Premium permite a herramientas externas disparar actualizaciones fuera de esos límites. La consulta de tasas no necesita nada especial más allá de estar construida con Web.Contents + RelativePath para que el servicio la acepte.
¿Cómo paso una clave de API de forma segura en Power BI?
Ponla en el registro Headers de Web.Contents como Authorization = "Bearer " & ApiKey y elige autenticación Anónima cuando Power BI lo pida. Así la clave se queda fuera de la URL, de los registros de proxy y del historial del navegador. Eso no cifra la clave dentro del .pbix: para modelos compartidos, guarda la clave en un dataflow propiedad de una cuenta de servicio y deja que los informes lean la tabla materializada.
¿Por qué mi conjunto de datos dice "Este conjunto de datos incluye un origen de datos dinámico"?
Porque la URL se ensambla en código, así que Power BI no puede verificar el destino antes de ejecutar la consulta y se niega a actualizarla en el servicio. Reconstruye la llamada con una URL base estática más las opciones RelativePath y Query, vuelve a publicar y reintroduce la credencial para la URL base.
¿Cómo convierto importes usando el tipo de cambio de la fecha de la transacción?
Carga una tabla de tasas indexada por fecha desde un endpoint de series temporales y empareja por divisa y fecha: mediante una combinación compuesta en Power Query, o con una búsqueda CALCULATE dentro de SUMX como se muestra arriba. Nunca unas solo por divisa; acabarás tomando en silencio la fila de tasa que quede primero al ordenar.
¿Existe una API de divisas gratuita que funcione con Power BI?
Sí. El nivel gratuito de la API de divisas gratuita de Finexly incluye 1.000 peticiones al mes sin tarjeta de crédito, lo que cubre con holgura un conjunto de datos con licencia Pro que se actualice ocho veces al día. Si estás comparando proveedores por límites de actualización, profundidad histórica o cobertura de divisas, compara APIs de divisas antes de conectar una a un modelo que mantendrás durante años.
Empieza con Finexly
¿Listo para llevar tipos de cambio en vivo a tus informes de Power BI? Consigue tu clave gratuita de la API de Finexly — sin tarjeta de crédito. Empieza con 1.000 peticiones gratuitas al mes y amplía según crezcas. Tasas en tiempo real e históricas para más de 170 divisas, desde una única API REST que se comporta igual la llames desde Power Query, Python o un servicio de pagos.
Explore More
Vlado Grigirov
Senior Currency Markets Analyst & Financial Strategist
Vlado Grigirov is a senior currency markets analyst and financial strategist with over 14 years of experience in foreign exchange markets, cross-border finance, and currency risk management. He has wo...
View full profile →