ブログに戻る

Power BI でリアルタイム為替レートを取得する方法:Power Query、DAX、そして更新の落とし穴

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

Power BI でリアルタイム為替レートを取得する作業は、実際に完了するずっと前から「もう終わった」ように見えるタイプのタスクです。データを取得 → Web に API の URL を貼り付けると、レートのテーブルが現れ、換算後の売上メジャーが点灯し、そのまま発行してしまう。二日後、Power BI サービスでのデータセット更新が「動的データソース」を告げるメッセージとともに失敗する。あるいは成功はするものの、すべての過去取引を今日のレートで静かに換算してしまっている。

本ガイドは全行程を扱います。きれいなレートテーブルを返す Power Query の M クエリ、サービス上でも更新可能な状態を保つ RelativePath パターン、取引日で換算するための履歴レート関数、レポート通貨を動的に切り替える DAX メジャー、そして更新スケジュールが API プランに収まるかを決めるリクエスト数の計算です。

以下のクエリはすべて、Finexly API のドキュメント化されたレスポンス構造に基づいて書かれています。Excel でリアルタイム為替レートを取得するガイドを読まれた方には見覚えのある M でしょう。ただし Power BI には Excel にはないサービス側の更新レイヤーがあり、多くのプロジェクトはまさにそこで壊れます。

Power BI にレートを取り込む三つの方法

方法サービスで更新できる?API キーは安全?履歴レート向いている用途
Web コネクタ、ダイアログに URL を貼るたいてい不可 — その URL は動的データソースになりがち❌ キーが URL に露出不可5 分間の概念実証
空のクエリ + Web.Contents + RelativePath✅ 可能✅ ヘッダー経由✅ 可能ほぼすべての実運用モデル
データフロー(または Fabric パイプライン)でレートテーブルを供給✅ 可能。レポート更新とも疎結合✅ ヘッダー経由✅ 可能複数レポート、大量行
二行目が標準解です。同じレートを複数のレポートが必要とし始めたら三行目へ移行します。一行目はスクリーンショット主体のチュートリアルが必ず教える方法であり、多くの通貨モデルが最初のスケジュール更新で失敗する原因でもあります。

方法 1:Power Query でリアルタイムのレートテーブルを作る

データの変換 → 新しいソース → 空のクエリ → 詳細エディター を開き、次を貼り付けます。通貨ペアごとに 1 行、UTC の取得タイムスタンプ付きで返します。

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

呼び出しているエンドポイントはコマンドラインではこう見えます。解析対象の構造を確認するために一度実行しておく価値があります。

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

RelativePath と Query が任意ではない理由

本記事で最も重要な点なので、独立した見出しにしています。

URL を一本の連結文字列として組み立てると — "https://api.finexly.com/v1/latest?base=" & Base — Power Query はクエリが実行されるまで接続先を判定できません。Microsoft はこれを動的データソースに分類し、セキュリティとプライバシーの理由から、動的データソースは Power BI サービスで更新されません。ノート PC 上では完璧に更新できるのに、スケジュールに載せた瞬間に失敗します。

パスを RelativePath に、パラメーターを Query に渡すのが、ドキュメントに記載された例外です。こうすれば Power BI は資格情報とプライバシーの検査のために単一の静的ベース URL — https://api.finexly.com — を解決でき、可変部分は可変のまま残ります。ここから三つの規則が導かれます。

  1. ベース URL はリテラル文字列であること。 パラメーターなし、連結なし、& も一切なし。
  2. RelativePath は固定のエンドポイントパスにすること。 "v1/latest" であり、"v1/latest?base=USD" ではありません。
  3. Query の内部で連結しないこと。 名前と値のペアのレコードを渡し、URL エンコードは Power Query に任せます。リクエストを壊しかねない文字のエスケープも行ってくれます。

資格情報の設定

クエリを初めて実行すると、Power BI は https://api.finexly.com への認証方法を尋ねます。匿名を選んでください。違和感がありますが、これが正解です。API キーは M コードで指定した Authorization ヘッダーで送られており、Power BI の資格情報ストアを経由しないからです。ここで Web API基本を選ぶと、Power BI が独自のヘッダーを追加し、リクエストは拒否されます。

プライバシーレベルは全ソースで一貫してパブリックまたは組織に設定します。プライバシーレベルの不一致は、「Desktop では動くのにサービスでは失敗する」の二番目に多い原因です。Power BI は、あるソースのデータが別のソースへのリクエストに漏れる危険を冒すくらいなら、クエリ自体をブロックします。

正直な注意点として、キーはクエリ内に平文で保存されます。.pbix を開いた人は誰でも読めます。自分のマシンを越えて共有するものであれば、キーを Power Query パラメーターに昇格させ、値を入れた版はサービスアカウント所有のデータフローに置いてください。レポート作成者は資格情報を一切見ずにレートテーブルを利用できます。

方法 2:取引日換算のための履歴レート

リアルタイムのレートテーブルが答えられるのは「いま EUR/USD はいくらか」だけです。「1 月の売上は USD でいくらだったか」には答えられません。そこにこれを使うのが、この話題で最も高くつく間違いです。モデルが新しいレートで再換算したせいで前四半期の数字を修正再表示する — 監査人がまさに探している事態です。後で説明責任を負う数字にレポートを使うなら、為替レートと税務報告のガイドも併せてお読みください。

必要なのは日付をキーにしたレートテーブルです。次の M 関数は時系列エンドポイントをラップし、日付 × 通貨ごとに 1 行を返します。

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

ここで実際に効いている細部が二つあります。

  • Date.ToTextDate.FromText 双方の Culture = "en-US" これがないと、ドイツ語やフランス語ロケールの端末は start_date として 01.09.2026 を送り、API に拒否されます。さらに悪ければ、同僚の更新があなたとは違う形のテーブルを生みます。ロケールは、複数国にまたがるあらゆる Power Query プロジェクトにおける見えない変数です。
  • Table.UnpivotOtherColumns API は日付をレコードキーとし、値に通貨のネストされたレコードを返します。RateDate / Base / Quote / Rate の縦持ち形式にアンピボットしておくと、日付ディメンションときれいに結合でき、通貨を追加するたびに形を作り直す必要もなくなります。

読み込みごとに一度呼び出します:FxHistory( "USD", "EUR,GBP,JPY", #date(2026,1,1), Date.From( DateTime.LocalNow() ) )

過去日のレートは決して変わらないため、このテーブルは増分更新の教科書的な事例です。RateDate でパーティション分割し、直近 7 日だけ更新して、それ以前はアーカイブします。更新時間が履歴とともに伸び続けることはなくなり、リクエスト数も同様です。

行ごとに API を呼ばないこと

このようなモデルを潰す典型が、ファクトテーブルのカスタム列としてレート関数を呼び出す実装です。1 万件の取引は、更新ごとに 1 万回の HTTP リクエスト、40 分かかる更新、そして昼食前に届くクォータ超過を意味します。

作る前に計算しましょう。Power BI Pro はデータセットあたり 1 日 8 回のスケジュール更新、Premium Per User と Fabric 容量は 48 回を許可します。必要な全通貨を 1 回の /v1/latest で取得し、Pro の上限で更新した場合のコストは1 日 8 リクエスト — 月およそ 240 回で、1,000 回の無料枠に余裕で収まります。同じスケジュールでも行ごとの関数なら上限はありません。PPU の 48 回上限でも、集約した 1 回の呼び出しなら月およそ 1,440 リクエストにとどまり、これは行単位の惨事ではなく小さな有料プランの話です。厳密に見積もる必要があれば、料金プランに各しきい値を掲載しています。

複数のレポートがレートを必要とするなら、クエリをデータフローへ移します。データフローは自身のスケジュールで API を呼び、結果をマテリアライズします。下流の各データセットは API を再度叩かずストレージから読むだけです。同じデータフローに乗る 5 つのレポートが生むリクエストは 5 セットではなく 1 セットです。

金額の換算:Power Query のマージ か DAX メジャー か

換算を適用する妥当な場所は二つあり、それぞれ別の問いに答えます。

選択肢 A:Power Query でマージする

レポート通貨が固定なら — すべて USD で報告する、以上 — 読み込み時に結合してしまいます。

  1. ファクトテーブルを Power Query に読み込む。
  2. レートテーブルに対してクエリのマージを行い、通貨コード日付で突き合わせる。Ctrl を押しながら、両テーブルで同じ順序に列を選択する。
  3. Rate 列だけを展開し、固定小数点数に設定する。
  4. AmountRate を選択し、列の追加 → 標準 → 乗算
  5. 他から参照されないなら、レートテーブルの読み込みを無効化する。

高速で、一度だけマテリアライズされ、スライサーに左右されません。最後の点がトレードオフです。

選択肢 B:レポート通貨を選べる DAX メジャー

レポート全体を USD・EUR・GBP で切り替えたいなら、換算はクエリ時に行う必要があります。ISO コードの列だけを持つ非接続テーブル Reporting Currency を追加し、スライサーに置いて、次のように書きます。

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
)

注目すべき点が三つあります。

  • VAR の行は構造上の要です。 CALCULATE の前に TxCurrencyTxDate を変数へ取り込むことで、現在の SUMX 行に固定されます。フィルター引数の中で列を直接参照すると、コンテキスト遷移のバグを招き、もっともらしく見えて誤った合計を生みます。
  • REMOVEFILTERS( 'FX Rates' ) は、レートテーブルへの入力フィルターが検索範囲を狭めるのを防ぎます。
  • レートが見つからなければ BLANK() が返り、Amount * BLANK()BLANK() です。 これが望ましい挙動です。未換算の金額に静かにフォールバックする行は、為替レートの分だけ合計を水増しする行です。

リレーションシップの落とし穴

通貨コードだけでファクトテーブルとレートテーブルの物理リレーションシップを作らないでください。日付キー付きのレートテーブルでは通貨コードは一意ではないため、Power BI は多対多を提案します。多対多に双方向フィルターが組み合わさると、行数は容赦なく膨らみ、売上は何倍にもなります。Power Query で複合キーのマージを使うか、レートテーブルを非接続のままにして上記のように DAX で参照してください。

エラーを出さずに数字を狂わせる五つのミス

  1. 連結した URL。 Desktop では更新でき、サービスでは「このデータセットには動的データソースが含まれています」で失敗します。RelativePathQuery で修正できます。
  2. ペアの向きの取り違え。 base=USD&symbols=EUR は USD→EUR を返します。ファクトテーブルが EUR 建てなら必要なのは逆数です。どの合計を信じる前にも、既知のペアを一つ手計算で確認しましょう。どちらの側にどのコードを置くか迷ったら、ISO 4217 リファレンスが役立ちます。
  3. 履歴行に今日のレート。 数字は更新のたびに変わり、誰かが同じレポートの二つのエクスポートを比べるまで、誰も気づきません。
  4. ロケール書式の日付と小数。 小数点のカンマは 1,0842 をテキストに変え、Table.TransformColumnTypes はモデルが空白として扱うエラーを返し、該当行は合計から消えます。
  5. 丸める工程の間違い。 丸めるのは一度だけ、乗算のあと、表示の段階で。7 桁の金額に掛ける前にレートを小数 4 桁へ丸めると、会計システムとの差異が目に見える形で生じます。

よくある質問

Power BI は為替レートを自動更新できますか?

できます。発行済みデータセットは Power BI Pro で 1 日最大 8 回Premium Per User または Fabric 容量で 1 日 48 回のスケジュール更新を設定できます。Premium の XMLA エンドポイントを使えば、外部ツールからこの制限外で更新を起動できます。レートのクエリ自体に特別な処理は不要で、サービスに受け入れられるよう Web.Contents + RelativePath で組んであれば十分です。

Power BI で API キーを安全に渡すには?

Web.ContentsHeaders レコードに Authorization = "Bearer " & ApiKey として入れ、Power BI が尋ねてきたら匿名認証を選びます。これでキーは URL にもプロキシログにもブラウザー履歴にも残りません。ただし .pbix 内で暗号化されるわけではないため、共有モデルではサービスアカウント所有のデータフローにキーを保持し、レポートにはマテリアライズ済みテーブルだけを読ませてください。

「このデータセットには動的データソースが含まれています」と表示されるのはなぜ?

URL がコードで組み立てられており、Power BI がクエリ実行前に接続先を検証できないため、サービスでの更新を拒否するからです。静的なベース URL に RelativePathQuery オプションを添えて呼び出しを組み直し、再発行のうえ、ベース URL の資格情報を入力し直してください。

取引日のレートで金額を換算するには?

時系列エンドポイントから日付キーのレートテーブルを読み込み、通貨日付の両方で突き合わせます。Power Query の複合キーマージでも、上記のように SUMX 内の CALCULATE 参照でも構いません。通貨だけで結合してはいけません。並べ替えでたまたま先頭に来たレート行を、気づかないまま拾ってしまいます。

Power BI で使える無料の通貨 API はありますか?

あります。Finexly の無料通貨 API 枠はクレジットカード不要で月 1,000 リクエストを含み、Pro ライセンスのデータセットを 1 日 8 回更新しても十分な余裕があります。更新回数の上限、履歴の深さ、通貨カバレッジで比較検討中なら、何年も保守することになるモデルに組み込む前に通貨 API を比較してみてください。


Finexly を始める

Power BI レポートにリアルタイム為替レートを組み込む準備はできましたか?無料の Finexly API キーを取得しましょう — クレジットカードは不要です。月 1,000 リクエストの無料枠から始め、成長に合わせてアップグレードできます。170 を超える通貨のリアルタイムおよび履歴レートを、Power Query から呼んでも Python から呼んでも決済サービスから呼んでも同じように振る舞う一つの REST API で。

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 →

この記事を共有する