業務システム

Power BIのAPI連携:Web APIの取り込みとREST APIで更新・発行を自動化する手順

Power BIのAPI連携:Web APIの取り込みとREST APIで更新・発行を自動化する手順

Power BIのAPI連携には向きの違う2つの作業があります。1つは社外SaaSや自社システムのWeb APIからデータを取り込む連携で、Power QueryのWeb.Contentsで記述する方式です。もう1つはPower BI自体をREST APIで動かす連携で、セマンティックモデルの更新の起動、PBIXの発行、DAXでの集計値の取り出しを外部のプログラムから行います。この記事では、Serviceで定時更新が止まらないM言語の書き方、サービスプリンシパルの準備、更新を起動して完了を待つPythonのコード、発行の自動化、各APIの回数と行数の上限までを作業の順に示します。仕様の根拠は2026年11月時点のMicrosoft Learnの記述です。

まとめ:Power BIのAPI連携はデータを入れるPower Queryと操作するREST APIの2系統

外部のAPIからデータを取るなら、Power QueryのWeb.Contentsで基準URLを固定し、可変部分をRelativePathとQueryに分けて書きます。この形にしないと、Desktopでは動いてもServiceの定時更新が設定できません。APIキーはコードに書かず、資格情報の画面で入れます。

Power BIを外から動かすなら、Microsoft Entraのサービスプリンシパルを用意し、ワークスペースにメンバーとして入れてからREST APIを呼びます。共有容量(Pro)ではAPI経由の更新も1日8回の枠に数えられるため、データ元のバッチ完了に合わせて回数を絞って起動する設計が必要です。認証がOAuthでトークンの取り直しが要るAPIや、件数が数十万を超えるAPIは、Power BIから直接たたかず、データベースに一度ためてから読むほうが運用は安定します。

Power BIのAPI連携の2つの方向と、目的別に使う機能の対応表

「API連携」で検索した人が探しているものは、データの取り込みとPower BIの操作のどちらかに分かれます。最初にどちらを作るのかを決めると、要るライセンスと権限が絞れます。

外部のWeb APIから取り込む連携と、Power BI自体をAPIで動かす連携の違い

取り込みの連携は、Power BIがクライアントになって外部のAPIを呼びます。コードはPower BI Desktopの中のM言語で書き、発行後はServiceが定時更新のたびに同じコードを実行します。用意するのは取り込み先APIの認証情報だけで、Azure側の設定は要りません。

操作の連携は向きが逆です。自社のバッチやワークフローがクライアントになり、api.powerbi.comのREST APIを呼びます。呼び出し元の認証にMicrosoft Entra IDのトークンが要るため、アプリ登録とテナント設定の許可という管理者による事前作業が必要です。製品全体の位置づけはPower BIのできることと料金・他ツールとの違いで整理しています。

データ取得・更新の起動・発行・DAXクエリ・行の書き込みの目的別API早見表

目的ごとに使う機能と、最初に当たりやすい制約を並べます。

目的 使う機能 先に確かめる制約
外部APIのデータを取り込む Power QueryのWeb.Contents 基準URLの固定・POSTは匿名のみ
モデルの更新を外から起動する Refresh Dataset In Group 共有容量は1日8回
PBIXを自動で発行する Post Import In Group 上書きはモデルの所有者のみ
集計値を他システムへ渡す Execute Queries In Group 1回10万行・1分120回
行を直接書き込む Push Datasets の PostRows プッシュデータセット専用

実務で頻度が高いのは上の2行です。発行とDAXクエリは、レポートの本数が増えて手作業が回らなくなった段階で足せば間に合います。

Web.ContentsでREST APIのJSONをPower Queryに取り込む手順

Desktopの[データを取得]>[Web]でURLを貼るだけでもJSONは読めます。ただし貼ったURLをそのままコードにすると、日付や件数で変わる部分を足した途端にServiceで更新できなくなります。[詳細エディター]で書き直す形は次のとおりです。

RelativePathとQueryで基準URLを固定する定時更新の書き方

Power BIでのデータの更新の説明では、Power Queryが実行されるまで接続先が決まらない「動的データソース」は、ほとんどの場合Serviceで更新できません。例外として挙げられているのが、Web.ContentsのRelativePathとQueryのオプションを使う書き方です。次の例は、直近30日分の受注を返すAPIを読み込みます。

let
    // 基準URLは文字列のまま固定し、可変部分だけをRelativePathとQueryに渡す
    FromDate = Date.ToText(Date.AddDays(Date.From(DateTime.LocalNow()), -30), [Format = "yyyy-MM-dd"]),
    Source = Json.Document(
        Web.Contents(
            "https://api.example.com",
            [
                RelativePath = "v1/orders",
                Query = [from = FromDate],
                ApiKeyName = "api_key"
            ]
        )
    ),
    Orders = Table.FromRecords(Source[items])
in
    Orders

第1引数に"https://api.example.com/v1/orders?from=" & FromDateのような連結を書くと動的データソースになります。判定は、Power Queryエディターの[データ ソース設定]で[現在のファイル内のデータ ソース]を開き、警告が出ていないかで確かめます。

ページ分割されたAPIをList.Generateで最後のページまで取り切るコード例

多くのREST APIは1回に100件などで区切って返す仕組みです。1回目の応答だけを読むと、先頭ページの件数で止まったレポートができあがり、誰も気付かないまま数字が欠けます。ページ番号を増やしながら、空のページが返るまで呼び続けます。

let
    // 1ページ分を取得する関数。Queryの値はテキストで渡す
    GetPage = (page as number) =>
        Json.Document(
            Web.Contents(
                "https://api.example.com",
                [
                    RelativePath = "v1/orders",
                    Query = [page = Text.From(page), per_page = "100"],
                    ApiKeyName = "api_key"
                ]
            )
        ),
    // 空のページが返るまで次のページを呼ぶ
    Pages = List.Generate(
        () => [n = 1, res = GetPage(1)],
        each List.Count([res][items]) > 0,
        each [n = [n] + 1, res = GetPage([n] + 1)],
        each [res][items]
    ),
    Result = Table.FromRecords(List.Combine(Pages))
in
    Result

終了条件はAPIの仕様に合わせて変えます。次ページのURLを応答に入れて返すAPIなら、そのパス部分だけをRelativePathに渡します。基準URLまで応答から組み立てると、再び動的データソースとして扱われる形です。

ApiKeyNameでAPIキーを資格情報に分ける方法とPOSTの匿名制約

Web.Contentsのリファレンスには、ApiKeyNameにはキーの名前だけを書き、値はWeb APIの資格情報として入れる方法が載っています。上のコードでは、Desktopの資格情報の画面で[Web API]を選んでキーを入れると、?api_key=...が付いた要求になります。PBIXを共有してもキーは中に入りません。

同じページに、Contentを指定した要求はPOSTになり、POSTは匿名でしか送れないとも書かれています。トークン発行のエンドポイントへクライアントシークレットをPOSTしてアクセストークンを取るOAuthのAPIは、シークレットをMのコードに直接書かない限り呼べません。ヘッダーに期限付きのトークンを要するAPIも、Headersに固定値を書くことになり、期限切れのたびに再発行が要ります。この2種類はPower Queryで取り込む対象から外します。

サービスプリンシパルでPower BI REST APIを呼ぶための準備とテナント設定

ここから先は、Power BIを外から動かす連携です。個人のアカウントでも呼べますが、退職や多要素認証の設定変更で止まるため、業務のバッチではサービスプリンシパル(アプリケーションの資格情報)を使います。

Entraアプリ登録とセキュリティグループ、APIのアクセス許可を足さない理由

サービスプリンシパルを使った埋め込みの手順書が、REST APIを呼ぶ準備にもそのまま使えます。順番は次のとおりです。

  1. Microsoft Entra IDでアプリを登録し、アプリケーション(クライアント)IDとシークレットを控える
  2. セキュリティグループを作り、そのアプリのサービスプリンシパルをメンバーに入れる
  3. Power BI(Fabric)の管理者に、テナント設定でそのグループへのAPI利用の許可を依頼する
  4. 対象ワークスペースに、サービスプリンシパルかグループをメンバーまたは管理者で追加する

手順書は、Azure portalでPower BIのAPIのアクセス許可(委任・アプリケーションのどちらも)を追加しないよう勧めています。サービスプリンシパルでは使われず、原因の分かりにくいエラーを招くからです。Power BI側の権限はワークスペースのロールだけで決まると割り切ります。本番ではシークレットより証明書での認証が推奨されています。

テナント設定の許可とワークスペースへのメンバー追加、マイワークスペース不可の制約

開発者向けのテナント設定には、2026年11月時点で「サービス プリンシパルはFabricのパブリックAPIを呼び出すことができます」という項目があり、新規の顧客では既定で有効です。ワークスペースや接続をAPIで新しく作らせたい場合は、既定で無効の別項目「サービス プリンシパルは、ワークスペース、接続、デプロイ パイプラインを作成できます」も要ります。どちらも組織全体ではなく、手順2のセキュリティグループに限定して有効にします。

サービスプリンシパルは個人の[マイワークスペース]にあるモデルを扱えません。個人が作って発行したレポートを自動更新したいときは、先に共有ワークスペースへ移します。ポータルにサインインする用途にも使えないため、Serviceの画面で動作を確かめる作業は人のアカウントで行います。

同じサービスプリンシパルで顧客向けにレポートを表示する場合は、GenerateToken APIで埋め込みトークンを発行する工程が加わります。手順とPythonのコードはPower BI Embeddedの実装手順:埋め込みトークンの発行とRLS・A SKU容量の選び方で解説しています。

REST APIでセマンティックモデルの更新を起動し、完了まで追うPythonの実装

定時更新の時刻を決め打ちにすると、データ元のバッチが遅れた日に古いデータで更新が走ります。バッチの最後で更新を起動すれば、この時間差は消えます。

MSAL認証でRefresh Dataset In Groupの完了を待つ実装例

トークンの取得にはMSAL for Pythonを使います(pip install msal)。IDとシークレットは環境変数で渡します。

export PBI_TENANT_ID=テナントID
export PBI_CLIENT_ID=アプリケーションID
export PBI_CLIENT_SECRET=シークレットの値
export WS_ID=ワークスペースID
export DS_ID=セマンティックモデルID
python3 refresh_and_wait.py

次のコードは更新を1回起動し、終わるまで1分おきに履歴を見て、失敗なら異常終了します。

import json, os, time, urllib.request, urllib.error
import msal

app = msal.ConfidentialClientApplication(
    os.environ["PBI_CLIENT_ID"],
    authority="https://login.microsoftonline.com/" + os.environ["PBI_TENANT_ID"],
    client_credential=os.environ["PBI_CLIENT_SECRET"],
)
tok = app.acquire_token_for_client(scopes=["https://analysis.windows.net/powerbi/api/.default"])
if "access_token" not in tok:
    raise SystemExit(tok.get("error_description"))

HDR = {"Authorization": "Bearer " + tok["access_token"], "Content-Type": "application/json"}
URL = ("https://api.powerbi.com/v1.0/myorg/groups/" + os.environ["WS_ID"]
       + "/datasets/" + os.environ["DS_ID"] + "/refreshes")

# 更新の起動(受け付けられると202が返る)
body = json.dumps({"notifyOption": "NoNotification"}).encode()
try:
    with urllib.request.urlopen(urllib.request.Request(URL, data=body, headers=HDR, method="POST"), timeout=60) as r:
        print("受付", r.status, r.headers.get("x-ms-request-id"))
except urllib.error.HTTPError as e:
    raise SystemExit(f"起動失敗 HTTP {e.code}: {e.read().decode()}")

# 直近1件の履歴を見て、Unknown(実行中)が終わるまで待つ
while True:
    time.sleep(60)
    with urllib.request.urlopen(urllib.request.Request(URL + "?$top=1", headers=HDR), timeout=60) as r:
        last = json.load(r)["value"][0]
    print(last["status"], last.get("startTime"), last.get("endTime"))
    if last["status"] != "Unknown":
        break
if last["status"] != "Completed":
    raise SystemExit(last.get("serviceExceptionJson"))

起動はRefresh Dataset In Group、待機はGet Refresh History In Groupで、スコープはDataset.ReadWrite.Allです。notifyOptionのメール通知はサービスプリンシパルの操作には適用されないため、失敗の検知は終了コードで呼び出し元のジョブ管理に任せます。ワークスペース全体の更新設定と失敗をまとめて点検するコードは、Power BI DesktopとServiceの違いと更新・ゲートウェイの設定で紹介しています。

共有容量の1日8回と、Premium・PPUで使える強化された更新のパラメーター

共有容量のワークスペースでは、APIからの更新は定時更新と合わせて1日8要求までで、要求本文に書けるのはnotifyOptionだけです。Premium・PPU・Fabric容量では固定の回数制限はなく、容量のリソースで調整されます。こちらではnotifyOption以外の項目を書くと強化された更新になり、次の指定ができます。

パラメーター 既定値 使いどころ
objects モデル全体 変わったテーブル・パーティションだけ更新
commitMode transactional partialBatch は失敗時に一部だけ残る
retryCount 0 一時的な失敗の再試行回数
timeout 05:00:00 再試行を含めて合計24時間まで
maxParallelism 10 データ元の負荷に合わせて下げる

強化された更新では、Locationヘッダーの末尾にあるrequestIdでGET /refreshes/{requestId}を呼ぶと、テーブル単位の進み具合まで取れます。日中に売上テーブルだけを1時間ごとに更新し、マスター系は夜間に回す、という分け方ができるのはこの方式だけです。

同時実行の400と更新の取り消し、データ元のバッチ完了から起動する組み方

1つのモデルが受け付ける更新は同時に1つだけで、実行中に次を送ると400 Bad Requestが返ります。短い間に要求を重ねた場合、保持される対象は実行中と次の1件だけです。起動の前に?$top=1で状態がUnknownでないことを確かめれば、二重起動で枠を減らす事故を防げます。

強化された更新なら、実行中の更新をDELETE /refreshes/{requestId}で止められます。[今すぐ更新]や定時更新で始まったものは止められません。データ元のETLの最後のステップに上のスクリプトを置き、Power BI側の定時更新は切るか、保険として深夜1回だけ残します。

PBIXの発行をImports APIで自動化する手順と上書き時の注意点

同じレポートを開発・検証・本番のワークスペースへ配る作業や、顧客ごとのワークスペースに同じPBIXを置く作業は、Desktopの[発行]を繰り返すと取り違えが起きます。発行もAPIで行えます。

Post Import In GroupでPBIXを送信する手順と上書き指定

Post Import In Groupには、PBIXをmultipart/form-dataで送ります。表示名のクエリパラメーターには拡張子まで含めたファイル名を渡し、同名モデルの扱いはnameConflictで選びます。

# 前のコードの続きとして実行する(tok・os・json・urllib を再利用)
import uuid

name = "sales.pbix"
boundary = uuid.uuid4().hex
with open(name, "rb") as f:
    pbix = f.read()
body = (f'--{boundary}\r\nContent-Disposition: form-data; name="file"; filename="{name}"\r\n'
        "Content-Type: application/octet-stream\r\n\r\n").encode() + pbix + f"\r\n--{boundary}--\r\n".encode()

url = ("https://api.powerbi.com/v1.0/myorg/groups/" + os.environ["WS_ID"]
       + "/imports?datasetDisplayName=" + name + "&nameConflict=CreateOrOverwrite")
hdr = {"Authorization": "Bearer " + tok["access_token"],
       "Content-Type": "multipart/form-data; boundary=" + boundary}
with urllib.request.urlopen(urllib.request.Request(url, data=body, headers=hdr, method="POST"), timeout=300) as r:
    print("インポートID", json.load(r)["id"])   # 状態は Publishing → Succeeded または Failed

nameConflictの既定値はIgnoreで、同じ名前のモデルがあっても新しく作るため、同名のモデルが増えていきます。自動化では、無ければ作り有れば置き換えるCreateOrOverwriteを指定します。Overwriteは既存が無いと失敗するので、2回目以降の更新専用として使う指定です。

サービスプリンシパルが上書きできるモデルの条件と、1GB超・秘密度ラベルの制約

リファレンスの制限事項で運用に響くのは3点です。サービスプリンシパルが既存のモデルを上書きできるのは、そのモデルの所有者である場合だけです。人が手で発行したモデルをAPIで置き換えようとしても通らないため、最初の発行からAPIで行い、所有者をサービスプリンシパルにそろえます。

1GBから10GBのPBIXは、一時アップロード場所を作ってURLを渡す別の方式になり、Premium容量のワークスペースが必要です。秘密度ラベルが「保護」のファイルはサービスプリンシパルではインポートできません。発行後のモデルにはデータソースの資格情報が入っていないため、初回だけはServiceで資格情報とゲートウェイを設定してから更新を起動します。

Execute Queries APIでモデルの集計値を他システムへ渡すときの上限

Power BIで作った売上や在庫の集計を、基幹システムや社内ポータルに数字として出したい場面があります。Execute Queries In GroupはモデルにDAXクエリを投げ、結果をJSONで返します。

DAXクエリ1本で返せる10万行・100万値と、1分120回の要求上限

リファレンスに載っている上限は次のとおりです。

  • 1回の呼び出しで1クエリ・1テーブルまで
  • 返せるのは10万行か100万値の少ない方まで(20列なら5万行)、データ量は15MBまで
  • ユーザーごとに1分120要求まで
  • テナント設定の「データセット実行クエリ REST API」の有効化と、モデルの読み取り・ビルド権限が必要
  • 行レベルセキュリティやSSOを設定したモデルは、サービスプリンシパルから呼べない

行数の上限を超えても応答は200で返り、データが途中で切れたうえでエラーの文言が入ります。HTTPの状態だけで判定すると、欠けた数字を連携先へ渡すことになるため、応答のerrorを必ず見ます。明細の抜き出しには向かず、数十行の集計を取る用途にとどめる使い方です。CopilotのDAXクエリビューで生成したDAXの結果を検算する手順はPower BI Copilotの使い方:必要な容量・設定手順とDAX生成・レポート作成の実装で解説しています。送信するDAXクエリそのもの(DEFINE MEASUREやCALCULATE)の書き方はPower BIのDAX入門:メジャーと計算列の違い・CALCULATE・前年比のコード例にまとめています。

Power BIのAPI連携を自社で組むか、データ基盤側で受けるかの判断基準

ここまでの機能で、たいていの連携は組めます。ただし、組めることと続けて運用できることは別です。

Power Queryの直接取り込みで済ませてよい条件と、見送るべきAPIの特徴

Power Queryで直接取ってよいのは、APIキーをクエリ文字列で受け付けるか匿名で読めること、全件が数十ページで収まること、更新が1日数回で足りること、の3条件がそろうAPIです。社外の為替や祝日のAPI、SaaSの月次集計など、マスターに近い小さなデータが当てはまります。

次のどれかに当たるなら、Power BIから直接たたくのは見送ります。OAuthでトークンの取り直しが要るAPI、1回の取り込みが数百ページを超えるAPI、呼び出し回数に課金や上限があるAPIです。Power Queryは更新のたびに全ページを取り直すため、件数が増えるほど更新時間と呼び出し回数が膨らみます。こうしたデータは、ETLのジョブで差分だけを取ってデータベースにため、Power BI側はデータベースを読む構成にする方針です。ためる先の考え方はDWHの仕組みとDBとの違いで整理しています。

更新・発行の自動化を外部の開発会社に任せる作業範囲と判断の目安

モデルが数本で、更新の起動をETLの最後に1行足すだけなら社内で十分に組めます。外部に任せる目安は、取り込むAPIが3種類を超えてOAuthを含むとき、顧客や拠点ごとのワークスペースへの発行を自動化したいとき、サービスプリンシパルの権限設計と監視まで含めて引き継げる担当がいないときです。

APIからのデータ取得、データベースへの格納、Power BIの更新起動までを1本の連携として作り直す段階なら、API開発・システム連携で、現在の取り込み方法と更新の流れを確認したうえで、Power Queryに残す範囲と連携基盤に移す範囲を切り分けられます。

Power BIのAPI連携で更新や認証の実装時によくある質問

API連携の実装で寄せられやすい質問に、Microsoft Learnの記述を根拠に答えます。

Power AutomateからPower BIのデータ更新を起動できますか?

できます。Microsoft Learnのデータ更新のページでも、Serviceでデータを更新する方法の1つとしてPower Automateからの更新が挙げられています。コードを書かずにフローの最後で起動できるのが利点です。フローから起動した更新は失敗してもメールが届かないため、結果の確認もフローの中に組み込みます。

APIで起動した更新が失敗してもメールが届かないのはなぜですか?

仕様です。オンデマンド更新と、Power Automateなどプログラムから起動した更新では、失敗通知のメールは送られません。Refresh Dataset In GroupのnotifyOptionも、サービスプリンシパルの操作には適用されません。起動した側で更新履歴のstatusを確かめ、Failedなら自社のジョブ監視で通知する仕組みを用意します。

DirectQueryのモデルもREST APIで更新を起動する必要がありますか?

要りません。データの更新が必要なのは、データをモデルに取り込むインポートモードだけです。DirectQueryはレポートを操作するたびに元のデータソースへ問い合わせるため、最新の値がそのまま表示されます。ただしダッシュボードにピン留めしたタイルは約1時間ごとの更新なので、タイルの数字には時間差が出ます。

外部システムからPower BIへデータを直接書き込むことはできますか?

プッシュデータセットを作れば、Push DatasetsのPostRowsで行を追加できます。この操作はプッシュデータセット専用で、Desktopで作って発行した通常のモデルには書き込めません。管理者がテナント設定で「ResourceKey 認証をブロックする」を有効にすると、リソースキーでのプッシュは使えなくなる点にも注意します。

Web.Contentsで書いたクエリがServiceで更新できるか事前に確かめる方法は?

Power Queryエディターの[データ ソース設定]で[現在のファイル内のデータ ソース]を選び、動的データソースの警告が出ないかを見ます。警告が出たら、第1引数のURLを文字列の定数にして、変わる部分をRelativePathとQueryに移します。手で書いたクエリは一覧に出ないことがあるため、発行後に一度[今すぐ更新]を試すのが確実です。

関連記事

お気に入りに入れた記事の一覧

一創の公式 SNS

X では毎朝の新着記事と IT ニュースの速報を、YouTube・TikTok・Instagram・LinkedIn・Facebook では記事の解説動画を配信しています。

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.11.03 テックブログ ネットマイル不正アクセスで最大549万人:対象確認と退会会員データを消す実装
  2. 2026.11.06 テックブログ ドコモ不正アクセス:最大29.5万件の流出項目と見舞金、My docomo停止の経過と偽連絡の見分け方
  3. 2026.11.06 テックブログ あいおいニッセイ同和の不正アクセス:44.4万件の漏えい項目と窓口、止まった手続きの代わり
  4. 2026.11.06 テックブログ アイリスオーヤマ情報漏洩:アイリスプラザ770万人分の対象項目と本物の連絡の見分け方
  5. 2026.07.09 テックブログ Muse Imageとは?Metaの画像生成AIの使い方・API料金(1枚0.01ドル)と日本での提供状況【2026年10月】

RELATED POSTS 関連記事

目次