Tous les guides sur la conversion de devises dans Power BI appartiennent à l'un de deux camps. Le camp DAX vous montre une mesure élégante en supposant discrètement qu'une table ExchangeRate existe déjà dans votre modèle. Le camp API vous montre un extrait de Power Query qui fonctionne à merveille dans Power BI Desktop et qui échoue dès la publication, parce que l'actualisation planifiée le refuse.
Ce guide couvre les deux moitiés, dans l'ordre : comment récupérer des taux de change en direct dans Power BI depuis une API REST d'une manière qui survit à la publication sur le service Power BI, comment modéliser ces taux pour que vos chiffres soient défendables, et comment convertir les montants à l'import ou à la requête selon ce dont votre rapport a réellement besoin.
Tous les exemples Power Query et DAX ci-dessous ont été écrits à partir des formats de réponse documentés de l'API Finexly.
Commencez par identifier votre problème de conversion
« Conversion de devises dans Power BI » désigne trois problèmes d'ingénierie différents sous un même nom, et se tromper de problème est l'erreur la plus coûteuse de tout cet article.
- Plusieurs devises sources, une devise de reporting. Votre table de ventes contient des lignes en EUR, GBP et JPY et le directeur financier veut un seul chiffre en USD. Convertissez à l'import. Le taux est une propriété de la transaction, pas du rapport.
- Une devise source, plusieurs devises de reporting. Tout est stocké en USD et l'utilisateur choisit la devise d'affichage dans un segment. Convertissez à la requête, en DAX. Précalculer toutes les devises n'est pas réaliste.
- Plusieurs devises sources, plusieurs devises de reporting. Normalisez vers une devise pivot unique à l'import, puis appliquez le cas 2 par-dessus. N'essayez pas de résoudre cela en une seule étape.
La règle que répètent les modélisateurs expérimentés, et qui mérite d'être répétée encore : appliquez la conversion aussi tôt que possible. Chaque conversion repoussée au moment de la requête vous coûte à chaque visuel, à chaque changement de filtre, à chaque clic de segment.
Récupérer les taux en direct dans Power Query, correctement
Commencez par la table des taux. L'appel le plus efficace est une requête multi-paires, qui vous rend toutes les devises utiles en une seule requête plutôt qu'une requête par devise.
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 }
}Voici l'équivalent en requête M Power Query. Créez une requête vide (Accueil → Nouvelle source → Requête vide → Éditeur avancé) et collez :
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
StampedNommez-la FxRates. Vous obtenez une table à quatre colonnes — BaseCurrency, Currency, Rate, RetrievedAt — immédiatement utilisable dans les relations et les mesures.
L'erreur Web.Contents qui casse l'actualisation planifiée
Remarquez ce que la requête ci-dessus ne fait pas : elle ne concatène jamais les paramètres dans la chaîne d'URL. C'est la raison numéro un pour laquelle un rapport de conversion de devises fonctionne sur le poste et meurt dans le cloud.
Si vous écrivez plutôt ceci :
// Do NOT do this
Source = Json.Document(
Web.Contents("https://api.finexly.com/v1/convert?q=" & Pairs)
)…Power BI Desktop l'actualisera sans broncher, et le service Power BI le refusera avec « Ce jeu de données inclut une source de données dynamique. L'actualisation n'est pas prise en charge. » Le service doit pouvoir résoudre une URL de base statique au moment de l'analyse pour y rattacher des informations d'identification. Passer les parties variables par RelativePath et Query lui donne exactement cela : la base reste https://api.finexly.com et tout le dynamique vit dans les options.
La même règle vaut pour toute requête qui construit une URL à partir d'un paramètre, d'une date ou d'une valeur venue d'une autre table. Si vous ne retenez qu'une chose de cet article, retenez RelativePath.
Gérer la clé d'API sans la coder en dur
ApiKey = "YOUR_API_KEY" en clair convient pour un test de cinq minutes et ne convient pas pour quoi que ce soit de partagé. Le code M d'un modèle sémantique est visible par toute personne disposant de l'autorisation de build.
Deux options viables :
- Un paramètre Power Query (Accueil → Gérer les paramètres), référencé par
ApiKey = KeyParam. Il reste stocké avec le modèle, mais il est centralisé, facile à faire tourner, et vous pouvez le surcharger par environnement avec les pipelines de déploiement. - Le type d'identifiants Web API. Dans le service Power BI, allez dans Paramètres du modèle sémantique → Informations d'identification de la source de données → Modifier les informations d'identification et choisissez API web, en y saisissant la clé. Le service injecte alors lui-même l'en-tête
Authorizationet vous supprimez complètement l'optionHeadersde votre M. Le secret sort ainsi de la définition du modèle.
Quel que soit votre choix, utilisez une clé limitée au reporting et renouvelez-la lorsqu'une personne quitte l'équipe. Si la clé finit dans la chaîne de requête de l'URL plutôt que dans un en-tête, elle peut apparaître dans les journaux d'accès des proxys et des serveurs — la documentation Finexly le signale, et cela vaut pour tous les fournisseurs.
Construire une dimension devise, pas seulement une liste de taux
Une table de taux seule vous donne des nombres. Elle ne vous donne pas les bons libellés, les bonnes décimales, ni un segment qui se trie correctement. Récupérez la liste des devises comme dimension à part entière :
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
TypedAjoutez ensuite les deux colonnes que Power BI ne peut pas déduire pour vous :
MinorUnits— le nombre de décimales réellement utilisé par la devise. Le JPY en a 0, le KWD 3, la plupart 2. Formater un total en yens avec deux décimales est une erreur visible dans un rapport financier, et arrondir à la mauvaise étape aggrave le problème. Le guide de l'arrondi des devises montre par où l'erreur s'introduit.FormatString— par exemple"\€#,0.00","\¥#,0". Vous en aurez besoin plus loin pour les chaînes de format dynamiques.
Les deux suivent la norme ISO 4217 et non une connaissance native de Power BI ; la référence ISO 4217 contient le tableau complet. Marquez cette table comme dimension, reliez-la à FxRates[Currency] en un-à-plusieurs, et utilisez-la — et non la table des taux — comme source de votre segment.
Voie A : convertir à l'import (rapide, ennuyeux, correct)
Pour le scénario 1 — plusieurs devises en entrée, une seule en sortie — faites le travail dans Power Query et laissez le modèle stocker un unique nombre propre.
- Chargez votre requête de transactions dans Power Query.
- Accueil → Fusionner des requêtes, en joignant
Transactions[Currency]àFxRates[Currency](externe gauche). - Développez la colonne fusionnée et conservez
Rate. - Ajouter une colonne → Colonne personnalisée :
= if [Currency] = "USD" then [Amount]
else if [Rate] = null then null
else [Amount] / [Rate]Notez la branche null. Une jointure externe gauche sur une table de taux à laquelle il manque une devise produit null, et null dans l'arithmétique Power Query produit silencieusement null au lieu d'une erreur — ce qui devient un blanc dans votre visuel et un total discrètement trop bas. Rendez le trou explicite pour pouvoir le filtrer et le voir.
Notez aussi la division. USD_EUR = 0.9215 signifie qu'un USD achète 0,9215 EUR : convertir un montant en EUR vers l'USD divise. Convertir un montant en USD vers l'EUR multiplie. Inverser les deux est le deuxième bug le plus fréquent dans les rapports multidevises et, avec des taux proches de 1,0, il est presque invisible — une erreur de 3 % sur un chiffre EUR/USD ressemble à un écart d'arrondi jusqu'à ce que quelqu'un rapproche les comptes.
Voie B : convertir à la requête avec DAX
Pour le scénario 2 — une devise stockée, une devise d'affichage choisie par l'utilisateur — la conversion doit se faire dans une mesure.
La version naïve fait un LOOKUPVALUE par ligne et devient lente au-delà de quelques centaines de milliers de lignes. Agrégez d'abord, convertissez une fois :
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 )Deux détails qui comptent plus qu'il n'y paraît :
SELECTEDVALUEavec valeur par défaut. Sans le repli"USD", la mesure renvoie un blanc dès qu'aucune devise n'est sélectionnée — c'est-à-dire l'état dans lequel votre rapport s'ouvre.- La garde explicite sur le blanc. Si une devise n'a pas de taux, renvoyez un blanc délibérément plutôt que de laisser
[Sales Amount] * BLANK()renvoyer zéro. Un zéro sur une carte de chiffre d'affaires est un mensonge ; un blanc est un trou visible.
Quand le taux varie dans le temps
La mesure ci-dessus utilise un taux courant unique pour tout le jeu de données. C'est correct pour « combien vaudrait aujourd'hui le chiffre d'affaires de l'an dernier » et faux pour presque tout le reste. Si votre table de taux comporte une ligne par devise et par jour, regroupez par date avant de convertir :
Sales (Historical Rates) =
SUMX (
VALUES ( 'Date'[Date] ),
VAR DayRate =
CALCULATE (
SELECTEDVALUE ( FxRates[Rate] ),
FxRates[Currency] = SELECTEDVALUE ( Currency[Currency], "USD" )
)
RETURN
[Sales Amount] * DayRate
)Itérer sur VALUES('Date'[Date]) plutôt que sur la table de faits garde l'itérateur petit — des jours, pas des transactions.
Chaînes de format dynamiques
Un nombre converti avec un préfixe $ figé est pire que pas de symbole du tout. Dans Power BI, réglez le Format de la mesure sur Dynamique et fournissez une expression :
SELECTEDVALUE ( Currency[FormatString], "#,0.00" )La carte qui affiche ¥ affiche désormais ¥, sans décimales, sans deuxième mesure. Cela nécessitait autrefois des groupes de calcul dans Analysis Services ; les chaînes de format dynamiques pour les mesures l'ont amené dans Power BI même.
Quel taux devriez-vous réellement utiliser ?
C'est la question qui sépare un tableau de bord d'un rapport que la direction financière signera, et aucune API ne peut y répondre à votre place.
- Taux spot à la date de transaction — pour enregistrer les transactions individuelles. Fidélité maximale, table de taux la plus volumineuse.
- Moyenne mensuelle ou de période — la norme pour les postes du compte de résultat, sous IAS 21 comme sous ASC 830. Elle lisse la volatilité intra-mensuelle et c'est ce qu'utilisent la plupart des consolidations.
- Taux de clôture de période — pour les postes du bilan : trésorerie, créances, dettes.
- Taux budgétaire ou de plan — un taux fixe maintenu toute l'année pour que l'analyse des écarts isole la performance opérationnelle du mouvement de change.
Un modèle sérieux a souvent besoin de deux ou trois de ces taux côte à côte, en colonnes distinctes de la même table (SpotRate, AverageRate, ClosingRate) plutôt qu'en tables séparées. Si votre rapport alimente quelque chose qui finit dans une déclaration, le guide taux de change et déclaration fiscale traite de la source et de l'horodatage que vous devez pouvoir défendre, et le guide des taux historiques couvre la récupération de taux datés plutôt que de taux en direct.
Le trou du week-end qui casse silencieusement les totaux
Les marchés des changes ferment. Une table de taux quotidienne construite depuis une API en direct n'a ni samedi, ni dimanche, ni 25 décembre. Joignez une transaction datée d'un samedi à cette table et vous obtenez null, null devient un blanc, et le blanc devient un total trop bas d'exactement les ventes du week-end.
Corrigez cela dans la table des taux, pas dans la mesure. Générez une liste de dates complète et remplissez vers le bas :
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
FilledTable.FillDown propage le taux du vendredi sur le week-end, ce qui est le traitement conventionnel et, surtout, un traitement déclaré plutôt qu'accidentel. Triez par devise et par date avant de remplir, faute de quoi vous propagerez le taux d'une autre devise à travers le trou.
Si votre offre n'inclut pas les points de terminaison historiques, vous pouvez construire l'historique vers l'avant : ajoutez les taux du jour à une table stockée à chaque actualisation — un dataflow ou une table Fabric Lakehouse conviennent bien — et au bout d'un trimestre vous disposez d'une vraie série temporelle. Ce n'est pas rétroactif, mais cela coûte un appel d'API par jour.
Planification des actualisations et arithmétique des quotas
Power BI Pro autorise 8 actualisations planifiées par jour sur un modèle sémantique ; les capacités Premium et Fabric en autorisent 48. C'est le nombre que votre quota d'API doit couvrir, et le calcul est plus clément que ce que l'on imagine.
La table de taux ci-dessus représente deux appels par actualisation : un vers /v1/currencies, un vers /v1/convert. Donc :
| Cadence d'actualisation | Actualisations/mois | Appels API/mois | Offre Finexly |
|---|---|---|---|
| 8/jour (maximum Pro) | ~240 | ~480 | Gratuite (1 000/mois) |
| 48/jour (Fabric, toutes les 30 min) | ~1 440 | ~2 880 | Starter |
| 48/jour + historique horaire en dataflow | ~2 160 | ~4 320 | Growth |
Le plafond de 10 par minute est celui à surveiller. Si vous construisez une requête qui appelle /v1/rate une fois par devise dans un Table.AddColumn, vingt devises signifient vingt appels en quelques secondes et une rafale de réponses 429 en pleine actualisation. C'est précisément pour cela que l'appel multi-paires /v1/convert existe. Regroupez et mettez en cache : le guide sur le cache et la gestion des erreurs présente des schémas de nouvelle tentative avec backoff qui s'appliquent tout autant à une actualisation planifiée.
Passerelles, Excel et Fabric
Quelques remarques d'environnement qui font gagner un après-midi chacune :
- Aucune passerelle nécessaire. Une API REST dans le cloud n'est pas une source locale : vous n'avez pas besoin d'une passerelle de données locale pour cela. Si l'actualisation échoue et que quelqu'un propose d'en installer une, c'est presque toujours l'erreur de source dynamique déguisée.
- Excel utilise le même moteur. Power Query dans Excel accepte exactement le M ci-dessus. Si votre public vit dans des classeurs plutôt que dans des tableaux de bord, le guide des taux en direct dans Excel couvre
WEBSERVICE,LAMBDAet la matrice des versions ; il existe aussi un équivalent pour Google Sheets. - Dataflow Gen2 de Fabric est un meilleur foyer pour la table des taux dès que plus d'un rapport en a besoin. Chargez les taux une fois, laissez tous les modèles sémantiques lire la même table, et votre consommation d'API cesse de croître avec le nombre de rapports.
- Vérifiez face à un chiffre connu avant de publier. Prenez une paire dans le convertisseur de devises et comparez-la à ce que votre modèle affiche pour la même paire au même instant. En cas d'écart, vous avez un problème de sens ou d'arrondi, et mieux vaut le trouver maintenant qu'en conseil d'administration.
Questions fréquentes
Power BI peut-il convertir des devises sans API ? Oui, si vous fournissez les taux vous-même : une table tenue à la main, un export du système financier ou une vue de base de données. Power BI n'a pas de source de taux intégrée. L'API devient utile lorsque vous voulez des taux qui se mettent à jour sans que quelqu'un pense à les mettre à jour.
Pourquoi mon rapport de conversion s'actualise-t-il dans Power BI Desktop mais échoue-t-il dans le service ?
Presque toujours à cause de l'erreur de source de données dynamique. Votre appel Web.Contents construit son URL par concaténation. Déplacez les parties variables vers les options RelativePath et Query pour que l'URL de base soit statique, republiez et ressaisissez les informations d'identification.
Faut-il convertir dans Power Query ou dans DAX ? Power Query lorsque le rapport n'a qu'une devise de reporting : c'est plus rapide et plus simple. DAX lorsque l'utilisateur choisit la devise à l'exécution. Si vous avez besoin des deux, normalisez vers une devise pivot dans Power Query et posez la mesure DAX par-dessus.
Combien de requêtes API consomme une actualisation Power BI ?
Deux par actualisation si vous regroupez toutes les paires dans un seul appel /v1/convert. Au maximum de 8 actualisations quotidiennes de Power BI Pro, cela fait environ 480 requêtes par mois, dans les limites d'une offre gratuite. Cela ne devient coûteux que si vous appelez l'API une fois par devise ou une fois par ligne.
Comment gérer les week-ends et jours fériés dans une table de taux quotidienne ? Générez une table de dates continue, joignez les taux à gauche, triez par devise et par date, puis remplissez vers le bas. Le taux du vendredi se propage sur le week-end. L'essentiel est que le traitement soit délibéré et documenté, et non que des lignes disparaissent en silence.
Quel taux de change utiliser pour le reporting financier ? Taux moyens de période pour les postes du compte de résultat, taux de clôture pour les postes du bilan, sous IAS 21 comme sous ASC 830. Stockez-les en colonnes distinctes d'une même table de taux pour qu'un rapport puisse basculer de l'un à l'autre sans modifier le modèle.
Prêt à placer des taux en direct derrière vos tableaux de bord ? Obtenez votre clé d'API Finexly gratuite — sans carte bancaire. Commencez avec 1 000 requêtes par mois, de quoi actualiser un espace de travail Power BI Pro à sa cadence maximale, et montez en gamme lorsque vous aurez besoin de données historiques ou d'une fréquence supérieure. Si vous comparez encore les fournisseurs, la page de comparaison les met côte à côte.
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 →