Terug naar Blog

Valutaconversie in Power BI: live wisselkoersen via een API (gids 2026)

V
Vlado Grigirov
August 27, 2026
Currency API Exchange Rates Power BI Power Query DAX Tutorial Finexly

Elke handleiding over valutaconversie in Power BI valt in een van twee kampen. Het DAX-kamp toont je een elegante meting en gaat er stilzwijgend van uit dat er al een ExchangeRate-tabel in je model staat. Het API-kamp toont je een stukje Power Query dat prachtig werkt in Power BI Desktop en breekt op het moment dat je publiceert, omdat het geplande vernieuwen het weigert.

Deze gids behandelt beide helften, in die volgorde: hoe je live wisselkoersen uit een REST-API in Power BI haalt op een manier die publicatie naar de Power BI-service overleeft, hoe je die koersen modelleert zodat je cijfers verdedigbaar zijn, en hoe je bedragen omrekent bij het importeren of bij de query, afhankelijk van wat je rapport werkelijk nodig heeft.

Alle Power Query- en DAX-voorbeelden hieronder zijn geschreven op basis van de gedocumenteerde responsstructuren van de Finexly API.

Bepaal eerst welk conversieprobleem je hebt

"Valutaconversie in Power BI" is de naam van drie verschillende technische problemen, en het verkeerde kiezen is de duurste fout in dit hele artikel.

  1. Veel bronvaluta's, één rapportagevaluta. Je verkooptabel bevat rijen in EUR, GBP en JPY en de CFO wil één cijfer in USD. Reken om bij het importeren. De koers is een eigenschap van de transactie, niet van het rapport.
  2. Eén bronvaluta, veel rapportagevaluta's. Alles staat in USD en gebruikers kiezen de weergavevaluta via een slicer. Reken om bij de query, in DAX. Elke valuta vooraf berekenen is niet praktisch.
  3. Veel bronvaluta's, veel rapportagevaluta's. Normaliseer bij het importeren naar één spilvaluta en leg daar geval 2 bovenop. Probeer dit niet in één stap op te lossen.

De vuistregel die ervaren modelleurs herhalen, en die het waard is nog eens te herhalen: reken zo vroeg om als je kunt. Elke omrekening die je naar de query verschuift, kost je bij elk visueel element, elke filterwijziging en elke klik op een slicer.

Live koersen op de juiste manier in Power Query halen

Begin met de koerstabel. De efficiëntste aanroep is een query met meerdere paren: je krijgt alle valuta's die je nodig hebt in één verzoek in plaats van één verzoek per valuta.

curl -H "Authorization: Bearer YOUR_API_KEY" \
  "https://api.finexly.com/v1/convert?q=USD_EUR,USD_GBP,USD_JPY"
{
  "USD_EUR": { "rate": 0.9215 },
  "USD_GBP": { "rate": 0.7892 }
}

Hier is het equivalent als Power Query M-query. Maak een lege query (Start → Nieuwe bron → Lege query → Geavanceerde editor) en plak:

let
    ApiKey = "YOUR_API_KEY",
    Pairs  = "USD_EUR,USD_GBP,USD_JPY,USD_CAD,USD_AUD,USD_CHF",
    Source = Json.Document(
        Web.Contents(
            "https://api.finexly.com",
            [
                RelativePath = "v1/convert",
                Query        = [ q = Pairs ],
                Headers      = [ #"Authorization" = "Bearer " & ApiKey ]
            ]
        )
    ),
    ToTable  = Record.ToTable(Source),
    Expanded = Table.ExpandRecordColumn(ToTable, "Value", {"rate"}, {"Rate"}),
    Split    = Table.SplitColumn(
                   Expanded, "Name",
                   Splitter.SplitTextByDelimiter("_", QuoteStyle.None),
                   {"BaseCurrency", "Currency"}
               ),
    Typed    = Table.TransformColumnTypes(
                   Split,
                   {{"BaseCurrency", type text}, {"Currency", type text}, {"Rate", type number}}
               ),
    Stamped  = Table.AddColumn(Typed, "RetrievedAt", each DateTimeZone.UtcNow(), type datetimezone)
in
    Stamped

Noem hem FxRates. Je krijgt een tabel met vier kolommen — BaseCurrency, Currency, Rate, RetrievedAt — direct bruikbaar in relaties en metingen.

De Web.Contents-fout die geplande vernieuwing sloopt

Let op wat de query hierboven niet doet: hij plakt de parameters nooit in de URL-tekst. Dit is verreweg de vaakst voorkomende reden waarom een valutaconversierapport op de desktop werkt en in de cloud sterft.

Schrijf je in plaats daarvan dit:

// Do NOT do this
Source = Json.Document(
    Web.Contents("https://api.finexly.com/v1/convert?q=" & Pairs)
)

…dan vernieuwt Power BI Desktop het zonder morren, en weigert de Power BI-service het met "Deze semantische model bevat een dynamische gegevensbron. Vernieuwen wordt niet ondersteund." De service moet tijdens analyse een statische basis-URL kunnen bepalen om er referenties aan te koppelen. De variabele delen via RelativePath en Query doorgeven levert precies dat: de basis blijft https://api.finexly.com en alles wat dynamisch is, leeft in de opties.

Hetzelfde geldt voor elke query die een URL opbouwt uit een parameter, een datum of een waarde uit een andere tabel. Als je één ding uit dit artikel meeneemt, neem dan RelativePath mee.

De API-sleutel beheren zonder hem hard te coderen

ApiKey = "YOUR_API_KEY" inline is prima voor een test van vijf minuten en verkeerd voor alles wat gedeeld wordt. De M-code van een semantisch model is zichtbaar voor iedereen met build-rechten.

Twee werkbare opties:

  • Een Power Query-parameter (Start → Parameters beheren), aangeroepen als ApiKey = KeyParam. Hij wordt nog steeds met het model opgeslagen, maar staat centraal, is makkelijk te roteren en kun je per omgeving overschrijven met implementatiepijplijnen.
  • Het referentietype Web-API. Ga in de Power BI-service naar Instellingen semantisch model → Referenties gegevensbron → Referenties bewerken en kies Web-API, waar je de sleutel invult. De service injecteert dan zelf de Authorization-header en je verwijdert de optie Headers volledig uit je M. Zo blijft het geheim buiten de modeldefinitie.

Wat je ook kiest: gebruik een sleutel die alleen voor rapportage geldt, en roteer hem als iemand het team verlaat. Belandt de sleutel in de querystring van de URL in plaats van in een header, dan kan hij opduiken in toegangslogboeken van proxy's en servers — de Finexly-documentatie wijst daar expliciet op, en dat geldt voor elke aanbieder.

Bouw een valutadimensie, niet alleen een koerslijst

Een koerstabel alleen geeft je getallen. Geen juiste labels, geen juiste decimalen en geen slicer die logisch sorteert. Haal de valutalijst binnen als eigen dimensie:

let
    ApiKey = "YOUR_API_KEY",
    Source = Json.Document(
        Web.Contents(
            "https://api.finexly.com",
            [
                RelativePath = "v1/currencies",
                Headers      = [ #"Authorization" = "Bearer " & ApiKey ]
            ]
        )
    ),
    ToTable = Table.FromList(Source, Splitter.SplitByNothing(), {"Currency"}),
    Typed   = Table.TransformColumnTypes(ToTable, {{"Currency", type text}})
in
    Typed

Voeg daarna de twee kolommen toe die Power BI niet voor je kan afleiden:

  • MinorUnits — het aantal decimalen dat de valuta werkelijk gebruikt. JPY heeft er 0, KWD 3, de meeste 2. Een yentotaal met twee decimalen opmaken is een zichtbare fout in een financieel rapport, en afronden in de verkeerde stap versterkt het. De gids over valuta-afronding laat zien waar de fout binnensluipt.
  • FormatString — bijvoorbeeld "\€#,0.00", "\¥#,0". Dit heb je verderop nodig voor dynamische opmaakreeksen.

Beide volgen de ISO 4217-standaard en niet iets wat Power BI van huis uit kent; de ISO 4217-referentie bevat de volledige tabel. Markeer deze tabel als dimensie, koppel hem één-op-veel aan FxRates[Currency] en gebruik hem — niet de koerstabel — als bron van je slicer.

Route A: omrekenen bij het importeren (snel, saai, correct)

Voor scenario 1 — veel valuta's erin, één eruit — doe je het werk in Power Query en laat je het model één schoon getal opslaan.

  1. Laad je transactiequery in Power Query.
  2. Start → Query's samenvoegen, waarbij je Transactions[Currency] koppelt aan FxRates[Currency] (left outer).
  3. Vouw de samengevoegde kolom uit en behoud Rate.
  4. Kolom toevoegen → Aangepaste kolom:
= if [Currency] = "USD" then [Amount]
  else if [Rate] = null then null
  else [Amount] / [Rate]

Let op de null-tak. Een left outer join op een koerstabel waarin een valuta ontbreekt levert null op, en null in Power Query-rekenwerk levert stilletjes weer null op in plaats van een fout — dat wordt een leeg vlak in je visual en een totaal dat ongemerkt te laag is. Maak het gat expliciet zodat je erop kunt filteren en het kunt zien.

Let ook op de deling. USD_EUR = 0.9215 betekent dat één USD 0,9215 EUR koopt, dus een EUR-bedrag naar USD omrekenen is delen. Een USD-bedrag naar EUR omrekenen is vermenigvuldigen. Dit omdraaien is de op één na meest voorkomende bug in multivalutarapporten en bij koersen rond 1,0 nagenoeg onzichtbaar: een fout van 3% in een EUR/USD-cijfer lijkt op een afrondingsverschil totdat iemand gaat afstemmen.

Route B: omrekenen bij de query met DAX

Voor scenario 2 — één opslagvaluta en een door de gebruiker gekozen weergavevaluta — moet de omrekening in een meting gebeuren.

De naïeve versie doet een LOOKUPVALUE per rij en wordt traag voorbij een paar honderdduizend rijen. Aggregeer eerst, reken één keer om:

Sales (Reporting Currency) =
VAR SelectedCurrency = SELECTEDVALUE ( Currency[Currency], "USD" )
VAR Rate =
    CALCULATE (
        SELECTEDVALUE ( FxRates[Rate] ),
        FxRates[Currency] = SelectedCurrency
    )
VAR Result =
    IF (
        SelectedCurrency = "USD",
        [Sales Amount],
        [Sales Amount] * Rate
    )
RETURN
    IF ( ISBLANK ( Rate ) && SelectedCurrency <> "USD", BLANK (), Result )

Twee details die zwaarder wegen dan ze lijken:

  • SELECTEDVALUE met standaardwaarde. Zonder de terugval op "USD" geeft de meting leeg zodra er geen valuta is geselecteerd — precies de toestand waarin je rapport opent.
  • De expliciete lege-waardebeveiliging. Heeft een valuta geen koers, geef dan bewust leeg terug in plaats van [Sales Amount] * BLANK() nul te laten opleveren. Een nul in een omzetkaart is een leugen; leeg is een zichtbaar gat.

Wanneer de koers in de tijd varieert

De meting hierboven gebruikt één actuele koers voor de hele dataset. Dat klopt voor "wat zou de omzet van vorig jaar vandaag waard zijn" en is fout voor bijna al het andere. Heeft je koerstabel één rij per valuta per dag, groepeer dan op datum vóór het omrekenen:

Sales (Historical Rates) =
SUMX (
    VALUES ( 'Date'[Date] ),
    VAR DayRate =
        CALCULATE (
            SELECTEDVALUE ( FxRates[Rate] ),
            FxRates[Currency] = SELECTEDVALUE ( Currency[Currency], "USD" )
        )
    RETURN
        [Sales Amount] * DayRate
)

Itereren over VALUES('Date'[Date]) in plaats van over de feitentabel houdt de iterator klein — dagen, geen transacties.

Dynamische opmaakreeksen

Een omgerekend getal met een vast $-voorvoegsel is erger dan helemaal geen symbool. Zet in Power BI de Opmaak van de meting op Dynamisch en geef een expressie mee:

SELECTEDVALUE ( Currency[FormatString], "#,0.00" )

Nu toont de kaart met ¥ ook ¥, zonder decimalen en zonder tweede meting. Vroeger had je hiervoor berekeningsgroepen in Analysis Services nodig; dynamische opmaakreeksen voor metingen brachten het naar Power BI zelf.

Welke koers moet je eigenlijk gebruiken?

Dit is de vraag die een dashboard scheidt van een rapport waar finance zijn handtekening onder zet, en geen enkele API beantwoordt hem voor je.

  • Spotkoers op transactiedatum — voor het vastleggen van individuele transacties. Hoogste nauwkeurigheid, grootste koerstabel.
  • Maand- of periodegemiddelde — de standaard voor posten in de winst-en-verliesrekening, zowel onder IAS 21 als ASC 830. Het vlakt volatiliteit binnen de maand af en is wat de meeste consolidaties feitelijk gebruiken.
  • Slotkoers van de periode — voor balansposten: kas, vorderingen, schulden.
  • Budget- of plankoers — een het hele jaar vaste koers zodat verschillenanalyse operationele prestaties scheidt van valutabewegingen.

Een serieus model heeft er vaak twee of drie tegelijk nodig, als aparte kolommen op dezelfde koerstabel (SpotRate, AverageRate, ClosingRate) in plaats van als losse tabellen. Voedt je rapport iets dat in een aangifte belandt, dan behandelt de gids over wisselkoersen en fiscale rapportage welke bron en welk tijdstempel je moet kunnen verantwoorden, en de gids over historische wisselkoersen het ophalen van gedateerde in plaats van live koersen.

Het weekendgat dat totalen stilletjes sloopt

Valutamarkten sluiten. Een dagelijkse koerstabel uit een live API heeft geen zaterdag, geen zondag en geen 25 december. Koppel een transactie met zaterdagdatum aan die tabel en je krijgt null, null wordt leeg, en leeg wordt een totaal dat precies de weekendomzet te laag is.

Los dit op in de koerstabel, niet in de meting. Genereer een volledige datumlijst en vul naar beneden:

let
    Dates = List.Dates(#date(2026,1,1), Duration.Days(Date.From(DateTime.LocalNow()) - #date(2026,1,1)) + 1, #duration(1,0,0,0)),
    DateTable = Table.FromList(Dates, Splitter.SplitByNothing(), {"Date"}),
    Typed = Table.TransformColumnTypes(DateTable, {{"Date", type date}}),
    Joined = Table.NestedJoin(Typed, {"Date"}, RateHistory, {"Date"}, "r", JoinKind.LeftOuter),
    Expanded = Table.ExpandTableColumn(Joined, "r", {"Currency", "Rate"}),
    Filled = Table.FillDown(Expanded, {"Currency", "Rate"})
in
    Filled

Table.FillDown trekt de vrijdagkoers door het weekend heen, wat de gebruikelijke behandeling is en, belangrijker nog, een uitgesproken behandeling in plaats van een toevallige. Sorteer op valuta en datum vóór het vullen, anders trek je de koers van de verkeerde valuta over het gat.

Bevat je abonnement geen historische endpoints, dan kun je de historie vooruit opbouwen: voeg bij elke vernieuwing de koersen van vandaag toe aan een opgeslagen tabel — een dataflow of een Fabric Lakehouse-tabel werkt prima — en na een kwartaal heb je een echte tijdreeks. Niet met terugwerkende kracht, maar het kost één API-aanroep per dag.

Vernieuwingsschema en het quotarekensommetje

Power BI Pro staat 8 geplande vernieuwingen per dag toe op een semantisch model; Premium- en Fabric-capaciteiten staan er 48 toe. Dat is het getal dat je API-quotum moet dekken, en de rekensom is vriendelijker dan de meesten aannemen.

De koerstabel hierboven is twee aanroepen per vernieuwing: één naar /v1/currencies, één naar /v1/convert. Dus:

VernieuwingsfrequentieVernieuwingen/maandAPI-aanroepen/maandFinexly-abonnement
8/dag (Pro-maximum)~240~480Gratis (1.000/maand)
48/dag (Fabric, elke 30 min)~1.440~2.880Starter
48/dag + uurlijkse dataflow-historie~2.160~4.320Growth
Het gratis abonnement is 1.000 verzoeken per maand met een plafond van 10 per minuut, wat een Pro-werkruimte op maximale frequentie ruim dekt. Actuele limieten en de beschikbaarheid van historische data staan op de prijzenpagina.

Het plafond van 10 per minuut is het punt om op te letten. Bouw je een query die /v1/rate één keer per valuta aanroept binnen een Table.AddColumn, dan betekenen twintig valuta's twintig aanroepen in enkele seconden en een reeks 429-antwoorden midden in de vernieuwing. Precies daarvoor bestaat de aanroep /v1/convert met meerdere paren. Bundel en cache: de gids over caching en foutafhandeling beschrijft retry-met-backoff-patronen die net zo goed gelden voor een geplande vernieuwing.

Gateways, Excel en Fabric

Een paar omgevingsnotities die elk een middag besparen:

  • Geen gateway nodig. Een REST-API in de cloud is geen on-premises bron, dus je hebt hier geen on-premises gegevensgateway voor nodig. Mislukt de vernieuwing en stelt iemand voor er een te installeren, dan is dat vrijwel altijd de fout over de dynamische gegevensbron in vermomming.
  • Excel gebruikt dezelfde engine. Power Query in Excel accepteert exact de M hierboven. Leeft je publiek in werkmappen in plaats van dashboards, dan behandelt de gids over live koersen in Excel WEBSERVICE, LAMBDA en de versiematrix; er is ook een Google Spreadsheets-variant.
  • Fabric Dataflow Gen2 is de betere plek voor de koerstabel zodra meer dan één rapport hem nodig heeft. Land de koersen één keer, laat alle semantische modellen dezelfde tabel lezen, en je API-verbruik groeit niet langer mee met het aantal rapporten.
  • Toets tegen een bekend getal voordat je publiceert. Haal één paar uit de valutaomrekener en vergelijk het met wat je model voor hetzelfde paar op hetzelfde moment toont. Wijken ze af, dan heb je een richtings- of afrondingsprobleem, en dat vind je liever nu dan in een bestuursvergadering.

Veelgestelde vragen

Kan Power BI valuta omrekenen zonder API? Ja, als je de koersen zelf aanlevert: een handmatig bijgehouden tabel, een export uit het financiële systeem of een databaseweergave. Power BI heeft geen ingebouwde koersbron. Een API telt zodra je koersen nodig hebt die bijwerken zonder dat iemand eraan hoeft te denken.

Waarom vernieuwt mijn conversierapport wel in Power BI Desktop en niet in de service? Vrijwel altijd door de fout over de dynamische gegevensbron. Je Web.Contents-aanroep bouwt de URL met tekstsamenvoeging. Verplaats de variabele delen naar de opties RelativePath en Query zodat de basis-URL statisch is, publiceer opnieuw en voer de referenties opnieuw in.

Moet ik omrekenen in Power Query of in DAX? Power Query wanneer het rapport één rapportagevaluta heeft — sneller en eenvoudiger. DAX wanneer gebruikers de valuta tijdens gebruik kiezen. Heb je beide nodig, normaliseer dan in Power Query naar één spilvaluta en leg de DAX-meting erbovenop.

Hoeveel API-verzoeken kost één Power BI-vernieuwing? Twee per vernieuwing als je alle paren bundelt in één /v1/convert-aanroep. Bij het Power BI Pro-maximum van 8 vernieuwingen per dag is dat ongeveer 480 verzoeken per maand, binnen een gratis abonnement. Duur wordt het pas als je de API per valuta of per rij aanroept.

Hoe ga ik om met weekenden en feestdagen in een dagelijkse koerstabel? Genereer een aaneengesloten datumtabel, koppel de koersen met een left join, sorteer op valuta en datum en vul naar beneden. De vrijdagkoers loopt door over het weekend. Belangrijk is dat de behandeling bewust en gedocumenteerd is, en dat er geen rijen stilletjes verdwijnen.

Welke wisselkoers moet ik gebruiken voor financiële rapportage? Periodegemiddelden voor posten in de winst-en-verliesrekening en slotkoersen voor balansposten, zowel onder IAS 21 als ASC 830. Bewaar ze als aparte kolommen op één koerstabel, zodat een rapport ertussen kan wisselen zonder modelwijziging.


Klaar om live koersen achter je dashboards te zetten? Haal je gratis Finexly API-sleutel — geen creditcard nodig. Begin met 1.000 verzoeken per maand, genoeg om een Power BI Pro-werkruimte op maximale frequentie te vernieuwen, en stap over zodra je historische data of een hogere frequentie nodig hebt. Vergelijk je nog aanbieders, dan zet de vergelijkingspagina de opties naast elkaar.

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 →