何が起きていたか
REST APIからデータを取得するPower Queryを作成していました。PostmanでエンドポイントをテストするとHTTP 200 OKでデータが正常に返ってきます。しかしExcelは次のエラーを出し続けました:
DataSource.Error: Web.Contents failed to get contents from 'https://api.example.com/data' (403): Forbidden.
Expression.Error: Access to the resource is forbidden
エンドポイントはダウンしていません。APIキーも有効です。Postmanでは動作します。それでもPower Queryは拒否し続けました。
調べていくと、同じ403エラーを引き起こす3つの全く異なる根本原因が見つかりました。そして、それらを解消する順番が重要です。
根本原因(可能性の高い順)
- 認証ヘッダーが送信されていない — Power Queryは特定の構成でカスタムヘッダーを警告なしに削除することがある
- 数式ファイアウォールがクロスソースクエリをブロックしている — Power Queryのプライバシー設定が2つのデータソースの組み合わせをセキュリティ違反として扱う
- データソースレベルで認証情報が正しく保存されていない — Excelが誤った認証情報をキャッシュして送り続ける
- API側のIPまたはリファラー制限 — APIキーが特定の送信元にスコープされている場合にありえるが、頻度は低い
ステップ1 — ヘッダーが実際に送信されているか確認する
ほとんどの場合、これが原因です。Web.ContentsのheadersにAPIキーを渡しているのに、Power Queryが組み込みの認証情報システムにフォールバックする際にそれらを警告なしで無視してしまいます。
詳細エディターを開いてMコードを確認してください。次のパターンは正しく見えますが、実際には機能しません:
// 警告なしでヘッダーが削除される場合がある
let
Source = Web.Contents(
"https://api.example.com/data",
[
Headers = [
#"Authorization" = "Bearer YOUR_TOKEN",
#"Content-Type" = "application/json"
]
]
),
Result = Json.Document(Source)
in
Result
URLをベースとRelativePathに分割してください。これにより、Power QueryがURLに認証情報を埋め込んだままキャッシュすることを防ぎます — これが認証システムを混乱させる原因です:
let
BaseUrl = "https://api.example.com",
Source = Web.Contents(
BaseUrl,
[
RelativePath = "data",
Headers = [
#"Authorization" = "Bearer YOUR_TOKEN",
#"x-api-key" = "YOUR_API_KEY"
],
ManualStatusHandling = {403, 401}
]
),
ResponseCode = Value.Metadata(Source)[Response.Status],
Result = if ResponseCode = 200 then Json.Document(Source)
else error "HTTP " & Text.From(ResponseCode) & " — 認証ヘッダーを確認してください"
in
Result
ここで重要な追加点はManualStatusHandlingです。Power Queryが403を不透明なクラッシュに変換する代わりに、実際のHTTPステータスコードが返ってきます — 確認して対処できる情報です。
ステップ2 — データソースの認証情報をクリアしてリセットする
ExcelはデータソースのURLごとに認証情報をキャッシュします。一度匿名で接続したり、誤ったトークンを使ったりすると、Mコードを修正した後でも古い認証情報を毎回の更新で送り続けます。
- Excelでデータ → データの取得 → データソースの設定に移動する
- 一覧でURLを見つける(多くの場合ベースドメインのみ、例:
api.example.com) - アクセス許可のクリアをクリックする — 編集ではなく、完全にクリアする
- クエリを閉じて再度開く
- プロンプトが表示されたら、適切な認証情報の種類を選択する:MヘッダーでAPIキーを渡す場合は匿名、APIが標準のキーベース認証を使用する場合はWeb API
ここで多くの人が躓きます。Power Queryは2つの独立した認証レイヤーを実行しています:Mコード内のヘッダーと、認証情報ストアです。これらが競合すると、認証情報ストアが優先されます。カスタムヘッダーがサーバーに届かないこともあります。
ステップ3 — 数式ファイアウォール(プライバシーレベル)を修正する
Web APIをローカルのExcelテーブル、SharePointリスト、データベースなど他のデータソースと組み合わせると、数式ファイアウォールがトリガーされます。Power Queryはこの組み合わせをセキュリティリスクとして扱いブロックします。このとき全く同じ403エラーが発生することがあります。
代わりにこのより明示的なメッセージが表示されることもあります:
Formula.Firewall: Query 'Query1' (step 'Source') references other queries or steps,
so it may not directly access a data source. Please rebuild this data combination.
プライバシーレベルを明示的に設定して修正します:
- ファイル → オプション → セキュリティセンター → セキュリティセンターの設定 → プライバシーに移動する
- またはクエリエディター内:ファイル → オプションと設定 → クエリオプション → プライバシー
- Webソースのプライバシーレベルをパブリック(または社内ネットワークでは組織)に設定する
- ローカルファイルソースをプライベートまたは組織に設定する
デバッグ中はこれを完全にスキップできます:
- クエリオプション → プライバシー → プライバシーレベルの設定を常に無視する
機密性の高いソースを組み合わせている場合、本番環境でこれを有効にしたままにしないでください。ただし、プライバシーレベルの調整に時間をかける前に、ファイアウォールが実際に問題かどうかを確認する最速の方法です。
ステップ4 — URL文字列の連結の代わりにクエリパラメーターを使用する
URL文字列に直接埋め込まれたAPIキーを拒否するAPIがあります。キーを適切なクエリパラメーターとして期待しており、Power Queryはこれら2つの構成を内部でまったく異なる方法で処理します。Queryオプションを使用してください:
let
Source = Web.Contents(
"https://api.example.com",
[
RelativePath = "v1/data",
Query = [
api_key = "YOUR_API_KEY",
format = "json"
],
Headers = [
#"Accept" = "application/json"
]
]
),
Result = Json.Document(Source)
in
Result
おまけ:Power Queryはこれを動的なソースではなく静的なソースとして扱うため、スケジュール更新のたびに余分なセキュリティプロンプトが表示されなくなります。
ステップ5 — API側のIP制限を確認する
それでも403が発生しますか?問題はExcel側にない可能性があります。企業向けAPIはIPアドレスでアクセスを制限することが多くあります。Power BIサービスやSharePoint Online経由でPower Queryが更新する場合、リクエストはMicrosoftのAzureデータセンターから送信されます — あなたのラップトップからではありません。APIは未知のIPを検出してブロックします。
別のネットワークから同じクエリを実行してテストするか、APIプロバイダーにアクセスログを確認して拒否されたIPを調べてもらってください。これが原因であれば、MicrosoftのデータセンターIPレンジをホワイトリストに追加するか、許可されたIPから送信するオンプレミスデータゲートウェイ経由で更新をルーティングする必要があります。
確認方法
修正を適用したら、それが実際に有効かどうかを確認してください:
- 一時的に
ManualStatusHandling = {403}を追加してResponse.Statusを表示する — 403ではなく200が表示されるはずです - ステータスチェックを削除して完全な更新を実行する — エラーなし
- Power BIやSharePointのスケジュール更新の場合、サービスから手動更新をトリガーして更新履歴を確認する
- データソースの設定を再度開き、正しい認証情報の種類がURLに保存されていることを確認する
クイックリファレンス
- ヘッダーが警告なしで削除される →
RelativePath+ManualStatusHandlingを使用する - 誤った認証情報がキャッシュされている → データソースの設定 → アクセス許可のクリア
- 数式ファイアウォールによるブロック → プライバシーレベルを設定するか、一時的にファイアウォールを無効にする
- URL文字列にAPIキーが含まれている → 代わりに
Query = [...]オプションを使用する - IP制限 → MicrosoftデータセンターのIPをホワイトリストに追加するか、オンプレミスゲートウェイを使用する
学んだこと
このエラーが非常に厄介な理由はここにあります:APIは壊れていません。Postmanがそれを証明しています。403はPower Queryの認証処理、プライバシー設定、認証情報キャッシュの内部にあります — そのどれもMコード自体には見えません。
順番通りに進めること — まずヘッダー、次に認証情報キャッシュ、そして数式ファイアウォール — でほぼ毎回原因が見つかります。そしてManualStatusHandlingは本当に活用されていない機能です。「Access to the resource is forbidden」という不透明なメッセージと、診断できる実際のHTTPステータスコードの違いを生み出すものです。

