Retour au blog

Comment obtenir des taux de change en direct dans Power BI : Power Query, DAX et le piège de l'actualisation

V
Vlado Grigirov
September 01, 2026
Currency API Exchange Rates Power BI Power Query DAX Tutorial Finexly

Récupérer des taux de change en direct dans Power BI fait partie de ces tâches qui semblent terminées bien avant de l'être. Vous collez une URL d'API dans Obtenir les données → Web, une table de taux apparaît, votre mesure de chiffre d'affaires converti s'allume, et vous publiez. Deux jours plus tard, l'actualisation du jeu de données échoue dans le service Power BI avec un message évoquant une source de données dynamique — ou bien elle réussit et convertit discrètement chaque transaction historique au taux du jour.

Ce guide couvre le parcours complet : une requête M Power Query qui renvoie une table de taux propre, le motif RelativePath qui la garde actualisable dans le service, une fonction de taux historiques pour convertir à la date de transaction, une mesure DAX pour une devise de reporting dynamique, et le calcul du nombre de requêtes qui détermine si votre planification tient dans un forfait d'API.

Toutes les requêtes ci-dessous sont écrites d'après les formats de réponse documentés de l'API Finexly. Si vous avez déjà lu notre guide sur les taux de change en direct dans Excel, le code M vous paraîtra familier — mais Power BI ajoute une couche d'actualisation côté service qu'Excel n'a pas, et c'est précisément là que la plupart de ces projets se cassent.

Les trois façons d'amener des taux dans Power BI

ApprocheS'actualise dans le service ?Clé d'API protégée ?Taux historiquesIdéal pour
Connecteur Web, URL collée dans la boîte de dialogueSouvent non — l'URL devient généralement une source dynamique❌ La clé se retrouve dans l'URLNonUne preuve de concept en cinq minutes
Requête vide avec Web.Contents + RelativePath✅ Oui✅ Via en-tête✅ OuiPresque tous les modèles réels
Dataflow (ou pipeline Fabric) alimentant une table de taux✅ Oui, et découplé de l'actualisation du rapport✅ Via en-tête✅ OuiPlusieurs rapports, gros volumes
La deuxième ligne est la réponse par défaut. La troisième est celle vers laquelle on évolue dès que plusieurs rapports ont besoin des mêmes taux. La première est celle que tous les tutoriels à base de captures d'écran enseignent, et c'est la raison pour laquelle tant de modèles de devises échouent dès leur première actualisation planifiée.

Méthode 1 : une table de taux en direct avec Power Query

Ouvrez Transformer les données → Nouvelle source → Requête vide → Éditeur avancé et collez ceci. Vous obtenez une ligne par paire de devises, avec un horodatage UTC de récupération.

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
    Typed

Le point de terminaison appelé ressemble à ceci en ligne de commande — il vaut la peine de l'exécuter une fois pour voir la structure que vous analysez :

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": "…" }
}

Pourquoi RelativePath et Query ne sont pas facultatifs

C'est le point le plus important de l'article, il mérite donc son propre titre.

Si vous construisez l'URL comme une seule chaîne concaténée — "https://api.finexly.com/v1/latest?base=" & Base — Power Query ne peut pas déterminer la destination avant l'exécution de la requête. Microsoft classe cela comme une source de données dynamique, et les sources dynamiques ne sont pas actualisées dans le service Power BI, pour des raisons de sécurité et de confidentialité. Le rapport s'actualisera parfaitement sur votre portable et échouera dès qu'il sera planifié.

Passer le chemin dans RelativePath et les paramètres dans Query est l'exception documentée. Power BI peut alors résoudre une URL de base statique unique — https://api.finexly.com — pour les contrôles d'identifiants et de confidentialité, tandis que les parties variables restent variables. Trois règles en découlent :

  1. L'URL de base doit être une chaîne littérale. Aucun paramètre, aucune concaténation, aucun &.
  2. RelativePath doit être le chemin fixe du point de terminaison. "v1/latest", pas "v1/latest?base=USD".
  3. Ne concaténez jamais à l'intérieur de Query. Passez un enregistrement de paires nom/valeur et laissez Power Query gérer l'encodage d'URL. Il échappera aussi les caractères qui casseraient la requête.

Configurer les identifiants

Au premier lancement, Power BI demande comment s'authentifier auprès de https://api.finexly.com. Choisissez Anonyme. Cela semble faux, mais c'est correct : la clé d'API voyage dans l'en-tête Authorization que vous avez fourni dans le code M, pas via le magasin d'identifiants de Power BI. Choisir API Web ou Basique amènera Power BI à ajouter son propre en-tête et la requête sera rejetée.

Réglez le niveau de confidentialité sur Public ou Organisationnel de manière cohérente sur toutes vos sources. Des niveaux incompatibles sont la deuxième cause la plus fréquente d'une actualisation qui fonctionne dans Desktop et échoue dans le service — Power BI bloque la requête plutôt que de risquer de faire fuir les données d'une source dans la requête d'une autre.

Une réserve honnête : la clé est désormais stockée en clair dans la requête. Quiconque ouvre le .pbix peut la lire. Pour tout ce qui est partagé au-delà de votre propre machine, promouvez la clé en paramètre Power Query et conservez la version renseignée dans un dataflow détenu par un compte de service, afin que les auteurs de rapports consomment la table de taux sans jamais voir l'identifiant.

Méthode 2 : taux historiques pour une conversion à la date de transaction

Une table de taux en direct répond à « combien vaut EUR/USD maintenant ». Elle ne répond pas à « quel était notre chiffre d'affaires de janvier en USD », et l'utiliser pour cela est l'erreur la plus coûteuse du sujet — retraiter les chiffres du trimestre précédent parce que le modèle les a reconvertis à un nouveau taux est exactement ce que cherchent les auditeurs. Si votre rapport alimente quelque chose que vous devrez défendre plus tard, lisez aussi notre guide sur les taux de change et la déclaration fiscale.

Il vous faut une table de taux indexée par date. Cette fonction M encapsule le point de terminaison de séries temporelles et renvoie une ligne par date et par devise :

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
    FxHistory

Deux détails y font un vrai travail :

  • Culture = "en-US" sur Date.ToText comme sur Date.FromText. Sans cela, une machine configurée en allemand ou en français envoie 01.09.2026 comme start_date et l'API refuse — ou pire, l'actualisation d'un collègue produit une table de forme différente de la vôtre. La culture locale est la variable invisible de tout projet Power Query qui dépasse une seule frontière.
  • Table.UnpivotOtherColumns. L'API renvoie les dates comme clés d'enregistrement, avec un enregistrement imbriqué de devises. Dépivoter vers un format long RateDate / Base / Quote / Rate donne une table qui se joint proprement à une dimension de dates et qu'il n'est pas nécessaire de remodeler à chaque nouvelle devise.

Appelez-la une fois par chargement : FxHistory( "USD", "EUR,GBP,JPY", #date(2026,1,1), Date.From( DateTime.LocalNow() ) ).

Comme les taux d'une date passée ne changent jamais, cette table est le cas d'école de l'actualisation incrémentielle : partitionnez sur RateDate, actualisez les 7 derniers jours, archivez le reste. Votre durée d'actualisation cesse de croître avec l'historique — et votre volume de requêtes aussi.

N'appelez pas l'API une fois par ligne

Le motif qui tue ces modèles consiste à invoquer une fonction de taux comme colonne personnalisée sur la table de faits. Dix mille transactions, c'est dix mille requêtes HTTP par actualisation, une actualisation de quarante minutes, et une facture de quota qui arrive avant midi.

Faites le calcul avant de construire. Power BI Pro autorise 8 actualisations planifiées par jeu de données et par jour ; Premium Per User et la capacité Fabric en autorisent 48. Un unique appel /v1/latest couvrant toutes vos devises, actualisé au maximum Pro, coûte 8 requêtes par jour — environ 240 par mois, largement dans un palier gratuit de 1 000. La même planification avec une fonction par ligne n'a aucune borne. Même au maximum de 48 actualisations en PPU, un appel consolidé se situe autour de 1 440 requêtes par mois, soit un petit forfait payant plutôt qu'une catastrophe ligne par ligne. Nos forfaits détaillent les seuils si vous devez dimensionner précisément.

Dès que plusieurs rapports ont besoin des taux, déplacez la requête dans un dataflow. Le dataflow appelle l'API selon sa propre planification et matérialise le résultat ; chaque jeu de données en aval lit le stockage au lieu de retaper sur l'API. Cinq rapports sur le même dataflow font un seul lot de requêtes, pas cinq.

Convertir les montants : fusion Power Query ou mesure DAX

Il existe deux endroits légitimes pour appliquer la conversion, et ils répondent à des questions différentes.

Option A : fusionner dans Power Query

Si votre devise de reporting est fixe — tout est reporté en USD, point — faites la jointure au chargement.

  1. Chargez la table de faits dans Power Query.
  2. Fusionner les requêtes avec la table de taux, en faisant correspondre le code devise et la date. Maintenez Ctrl et sélectionnez les colonnes dans le même ordre dans les deux tables.
  3. Développez uniquement la colonne Rate et définissez-la en Nombre décimal fixe.
  4. Sélectionnez Amount et Rate, puis Ajouter une colonne → Standard → Multiplier.
  5. Désactivez le chargement de la table de taux si rien d'autre ne la référence.

C'est rapide, cela se matérialise une fois, et aucun segment ne peut le fausser. Ce dernier point est le compromis.

Option B : une mesure DAX avec devise de reporting sélectionnable

Si les utilisateurs doivent basculer tout le rapport entre USD, EUR et GBP, la conversion doit avoir lieu à l'exécution de la requête. Ajoutez une table déconnectée Reporting Currency avec une colonne de codes ISO, mettez-la dans un segment, et écrivez :

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
)

Trois points à remarquer :

  • Les lignes VAR sont porteuses. Capturer TxCurrency et TxDate dans des variables avant CALCULATE les fige sur la ligne SUMX courante. Référencer les colonnes directement dans les arguments de filtre invite un bug de transition de contexte qui produit des totaux plausibles mais faux.
  • REMOVEFILTERS( 'FX Rates' ) empêche tout filtre entrant sur la table de taux de restreindre la recherche.
  • Un taux manquant renvoie BLANK(), et Amount * BLANK() vaut BLANK(). C'est le comportement souhaité. Une ligne qui retomberait silencieusement sur le montant non converti est une ligne qui gonfle votre total de la valeur du taux de change.

Le piège des relations

Ne créez pas de relation physique entre la table de faits et la table de taux sur le seul code devise. Le code devise n'est pas unique dans une table indexée par date, Power BI propose donc une relation plusieurs-à-plusieurs — et plusieurs-à-plusieurs avec filtrage bidirectionnel démultipliera joyeusement vos lignes et votre chiffre d'affaires. Utilisez la fusion composite dans Power Query, ou gardez la table de taux déconnectée et cherchez les taux en DAX comme ci-dessus.

Cinq erreurs qui produisent de faux chiffres sans lever d'erreur

  1. URLs concaténées. S'actualise dans Desktop, échoue dans le service avec « Ce jeu de données inclut une source de données dynamique ». Corrigé par RelativePath et Query.
  2. Paires inversées. base=USD&symbols=EUR renvoie USD→EUR. Si votre table de faits stocke des montants en EUR, il vous faut l'inverse. Vérifiez une paire connue à la main avant de faire confiance au moindre total. Notre référence ISO 4217 aide en cas de doute sur le code à placer de chaque côté.
  3. Le taux du jour sur des lignes historiques. Le chiffre change à chaque actualisation et personne ne le remarque jusqu'à ce que quelqu'un compare deux exports du même rapport.
  4. Dates et décimales au format régional. Une virgule décimale transforme 1,0842 en texte, Table.TransformColumnTypes renvoie une erreur que le modèle traite comme vide, et les lignes concernées disparaissent de vos totaux.
  5. Arrondir à la mauvaise étape. Arrondissez une seule fois, à la présentation, après la multiplication. Arrondir le taux à quatre décimales avant de multiplier un montant à sept chiffres crée un écart visible face au système comptable.

Foire aux questions

Power BI peut-il actualiser les taux de change automatiquement ?

Oui. Un jeu de données publié peut être planifié jusqu'à 8 actualisations par jour sur Power BI Pro et 48 par jour sur Premium Per User ou capacité Fabric. Le point de terminaison XMLA de Premium permet à des outils externes de déclencher des actualisations hors de ces limites. La requête de taux n'exige rien de particulier, sinon d'être construite avec Web.Contents + RelativePath pour que le service l'accepte.

Comment transmettre une clé d'API en toute sécurité dans Power BI ?

Placez-la dans l'enregistrement Headers de Web.Contents sous la forme Authorization = "Bearer " & ApiKey et choisissez l'authentification Anonyme lorsque Power BI le demande. Cela garde la clé hors de l'URL, hors des journaux de proxy et hors de l'historique du navigateur. Cela ne chiffre pas la clé dans le .pbix — pour les modèles partagés, conservez la clé dans un dataflow détenu par un compte de service et laissez les rapports lire la table matérialisée.

Pourquoi mon jeu de données indique-t-il « Ce jeu de données inclut une source de données dynamique » ?

Parce que l'URL est assemblée dans le code : Power BI ne peut pas vérifier la destination avant l'exécution de la requête et refuse donc de l'actualiser dans le service. Reconstruisez l'appel avec une URL de base statique plus les options RelativePath et Query, republiez, puis ressaisissez l'identifiant pour l'URL de base.

Comment convertir des montants au taux de change de la date de transaction ?

Chargez une table de taux indexée par date depuis un point de terminaison de séries temporelles, puis faites correspondre à la fois la devise et la date — soit par une fusion composite dans Power Query, soit avec une recherche CALCULATE à l'intérieur de SUMX comme montré plus haut. Ne joignez jamais sur la seule devise : vous prendriez en silence la ligne de taux qui se trouve en tête du tri.

Existe-t-il une API de devises gratuite compatible avec Power BI ?

Oui. Le palier gratuit de l'API de devises gratuite de Finexly inclut 1 000 requêtes par mois sans carte bancaire, ce qui couvre largement un jeu de données sous licence Pro actualisé huit fois par jour. Si vous comparez des fournisseurs sur les limites d'actualisation, la profondeur d'historique ou la couverture de devises, comparez les API de devises avant d'en câbler une dans un modèle que vous maintiendrez pendant des années.


Démarrer avec Finexly

Prêt à intégrer des taux de change en direct à vos rapports Power BI ? Obtenez votre clé d'API Finexly gratuite — sans carte bancaire. Commencez avec 1 000 requêtes gratuites par mois et montez en puissance selon vos besoins. Taux en temps réel et historiques pour plus de 170 devises, depuis une seule API REST qui se comporte de la même façon que vous l'appeliez depuis Power Query, Python ou un service de paiement.

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 →

Partager cet article