Microsoft Graph APIは、graph.microsoft.com という1つの窓口からMicrosoft 365とMicrosoft Entra IDのデータを読み書きするREST APIです。ユーザー、メール、予定表、OneDriveとSharePointのファイル、Teamsのチャネルまで、同じ形式のURLとアクセストークンで扱えます。この記事では、できることの範囲を整理したうえで、委任アクセスとアプリ専用アクセスの違い、管理者の同意とSites.Selectedによる権限の絞り方、curlとPython SDKでユーザー一覧を取得する手順を解説します。後半は、429のスロットリングと $batch・デルタクエリ・変更通知の使い分けです。仕様と数値は2026年10月時点の公式ドキュメントに基づきます。
まとめ:Microsoft Graph APIで最初に決めるアクセス方式と権限範囲と同期方法
Microsoft Graph APIの実装で最初に決めるのは、誰の権限で呼ぶかです。画面でサインインした利用者の代わりに操作するなら委任アクセス、夜間バッチや常駐サービスのように利用者がいない処理ならアプリ専用アクセスを選びます。アプリ専用アクセスで使うアプリケーション許可は、必ず管理者の同意が必要です。
次に決めるのは、連携アプリに許可する権限の範囲を、対象のサイトやメールボックスに絞る方法です。Sites.Read.All のようなテナント全体の許可は、同意した瞬間にすべてのサイトへ届きます。特定のサイトだけを触らせたいならSites.Selected、特定のメールボックスだけならExchange OnlineのRBAC for Applicationsを使い、Entra ID側の広い許可は外しておきます。
最後はデータの取り方です。全件を定期的に読み直す作りは429のスロットリングを招きます。差分はデルタクエリで取り、変化のきっかけは変更通知で受け取る、という組み合わせを最初から設計に入れてください。
Microsoft Graph APIの統一窓口とMicrosoft 365のデータ範囲
Microsoft Graph APIを理解する近道は、URLの形を先に覚えることです。どのサービスのデータでも同じ規則で書けるため、メールを扱う処理とファイルを扱う処理で書き方を変える必要がありません。
graph.microsoft.comのユーザー・メール・ファイル・Teamsの集約範囲
Microsoft Graphの概要では、Microsoft GraphをMicrosoft 365、Windows、Enterprise Mobility + Securityのデータへの入口と位置付けています。かつてはExchange、SharePoint、Azure ADがそれぞれ別のAPIを持っていました。今はそれらが1つのエンドポイントの下にまとまっています。
扱えるデータは、ユーザーとグループ、Outlookのメールと予定表、OneDriveとSharePointのファイルとリスト、Teamsのチームとチャネル、Plannerのタスクなど広範囲に及びます。ディレクトリの情報を管理する Microsoft Entra ID(旧Azure AD) の操作も、今はこの窓口から行います。業務システムから見ると、社員マスタと文書とコミュニケーションの3つを1種類のトークンで読み書きできる点が大きな違いです。
リクエストURLの4要素とv1.0とbetaを本番で使い分ける基準
Microsoft Graph APIを使用するによると、リクエストは {HTTP method} https://graph.microsoft.com/{version}/{resource}?{query-parameters} の形です。HTTPメソッドはGET・POST・PATCH・PUT・DELETEの5つで、PATCHは存在しなければ作成する動きにも使われます。書き込み要求の本文は4MBまでで、超えるとHTTP 413が返ります。
バージョンは v1.0 と beta の2つです。公式ドキュメントは、v1.0を一般提供のAPIとして本番アプリに使い、betaは開発中のテストに限るよう求めています。バージョン管理とサポートの方針のとおり、betaは予告なく互換性のない変更が入りえます。betaにしか無い機能に頼る設計は、本番では採用しない判断が妥当です。
業務システムからの連携でよく使うリソースを、用途ごとに並べました。URLの先頭はすべて https://graph.microsoft.com/v1.0 です。
| やりたいこと | 対象のサービス | 主なリソース |
|---|---|---|
| 社員一覧の同期 | Entra ID | users と groups |
| 通知メールの送信 | Exchange Online | sendMail |
| 会議室と予定の取得 | Outlookの予定表 | events と calendarView |
| 帳票ファイルの保存 | SharePointとOneDrive | drives と driveItem |
| 申請データの読み書き | SharePointのリスト | lists と listItem |
| チャネルへの投稿 | Teams | channels と chatMessage |
実務でまず押さえるのは、社員一覧の同期とファイルの保存の2つです。多くの連携はこの2つから始まり、承認や通知はその後に足されます。SharePointの中を全文で探す処理は別のAPIが担っており、Microsoft 365の検索APIでSharePointを探す実装で扱っています。
委任アクセスとアプリ専用アクセスの違いとアクセス許可を最小に絞る設計
Microsoft Graph APIで起きる事故の多くは、コードではなく権限の選び方から始まります。アクセスの方式、許可の種類、範囲の絞り方の順に見ていきます。
サインインした利用者の代理か常駐処理かで決まる2つのアクセス方式
認証と承認の基本方法は、アクセスを2種類に分けています。委任アクセスはサインインした利用者の代わりにアプリが操作する方式で、アプリと利用者の両方に権限が要ります。アプリは利用者本人の権限を超えられません。利用者本人を指す /me も、委任アクセスでしか使えません。
アプリ専用アクセスは、アプリが自分のIDで動く方式です。夜間の同期処理やバックアップのように、利用者がサインインしない処理に使います。こちらは利用者の権限による歯止めが無く、許可した範囲のテナント全体に届きます。判断の基準は単純で、画面の操作に伴う処理なら委任、裏で動く処理ならアプリ専用です。迷ったら委任を選んでください。
管理者の同意が要るアプリケーション許可と用途別に最小権限を選ぶ手順
許可には、委任アクセスで使う委任されたアクセス許可(スコープ)と、アプリ専用アクセスで使うアプリケーションのアクセス許可(アプリロール)があります。ユーザーなしでアクセスを取得には、アプリケーション許可は常に管理者の同意が必要で、許可を変えたら同意をやり直すまで反映されないと書かれています。
どの許可を付けるかは、APIごとのリファレンスにある表で決めます。ユーザーを一覧表示するの場合、アプリ専用で最も弱い許可は User.Read.All です。同じ表に並ぶ Directory.ReadWrite.All でも動きますが、書き込みまで許すことになります。全体の一覧はアクセス許可のリファレンスにあり、ここで使うメソッドから逆算して1つずつ選びます。
Sites.SelectedとExchangeのRBACでのサイト・メールボックスの限定
SharePointの特定サイトだけを読ませたいときは、選択したアクセス許可を使います。Sites.Selectedは同意しただけでは何も読めず、サイトごとに権限を付ける操作が別に要る仕組みです。権限は read・write・owner・fullcontrol の4段階で、付与は次の要求で行います。
POST https://graph.microsoft.com/v1.0/sites/{site-id}/permissions
Content-Type: application/json
{
"roles": ["write"],
"grantedToIdentities": [{
"application": {
"id": "{連携アプリのクライアントID}",
"displayName": "invoice-sync"
}
}]
}
この付与を行う側には、サイト単位なら Sites.FullControl.All が要ります。強い許可なので、付与用のアプリと業務処理用のアプリを分けておくと安全です。
メールボックスを絞るときは、RBAC for Applications in Exchange Onlineを使います。公式ドキュメントは、これが従来のApplication Access Policiesを置き換えると明記しています。落とし穴は、Entra ID側で付けた許可と和集合になる点です。Entra IDに Mail.Read を残したままExchange側で範囲を絞っても、全メールボックスが読める状態は変わりません。設定の反映には30分から2時間のキャッシュが挟まります。
Microsoft Graph APIの最小実装:curlとPython SDKの起動手順
ここからは実際に動かします。最初にcurlでトークンの取得と1回の呼び出しを確かめ、次にPython SDKで同じ処理を書くと、権限の問題とコードの問題を切り分けられます。
Entra管理センターでのアプリ登録から管理者の同意までの4つの作業
アプリ専用アクセスで社員一覧を取得する場合、Microsoft Entra管理センターで次の順に設定します。
- アプリの登録でアプリを作り、アプリケーション(クライアント)IDとディレクトリ(テナント)IDを控える
- 証明書とシークレットでクライアントシークレットを発行し、値をその場で控える
- APIのアクセス許可で、Microsoft Graphのアプリケーションの許可から
User.Read.Allを追加する - 管理者の同意を与え、状態が許可済みになったことを確かめる
資格情報はシークレットのほかに証明書とフェデレーションIDの資格情報も選べます。シークレットには有効期限があり、その期限が切れた時点で連携が止まる仕組みです。本番では証明書か、Azure上で動かすならフェデレーションIDの資格情報を選ぶと、期限切れの事故を減らせます。
クライアント資格情報フローでトークンを取得しユーザー一覧を読むcurl
トークンは login.microsoftonline.com のトークンエンドポイントへ要求します。scopeに https://graph.microsoft.com/.default を指定すると、管理者が同意した許可がまとめてトークンに入ります。トークンを取得する認証の仕組みの背景については、OAuth 2.0とはをあわせて確認してください。
TENANT_ID="your-tenant-id"
CLIENT_ID="your-client-id"
CLIENT_SECRET="your-client-secret"
TOKEN=$(curl -s -X POST "https://login.microsoftonline.com/$TENANT_ID/oauth2/v2.0/token" \
-d "client_id=$CLIENT_ID" \
--data-urlencode "scope=https://graph.microsoft.com/.default" \
--data-urlencode "client_secret=$CLIENT_SECRET" \
-d "grant_type=client_credentials" | jq -r .access_token)
curl -s "https://graph.microsoft.com/v1.0/users?\$select=id,displayName,department&\$top=50" \
-H "Authorization: Bearer $TOKEN"
公式ドキュメントの応答例では expires_in が3599秒で、トークンはおよそ1時間で切れます。一覧は既定で100件、$top の上限は999件で、続きがあると @odata.nextLink が返ります。403が返った場合に確認するのは、必要な許可の追加や管理者による同意の漏れです。401ならトークンの取り違えを疑ってください。
msgraph-sdkとazure-identityで同じ処理をPythonから書く方法
本番のコードでは、トークンの取得とキャッシュを自前で書かずにライブラリへ任せます。Microsoft Graph SDK for Pythonは非同期のAPIを標準とし、PyPIの msgraph-sdk は2026年10月22日時点で1.64系が最新、対応するPythonは3.10以上です。pip install msgraph-sdk azure-identity で導入します。
import asyncio
import os
from azure.identity.aio import ClientSecretCredential
from kiota_abstractions.base_request_configuration import RequestConfiguration
from msgraph import GraphServiceClient
from msgraph.generated.users.users_request_builder import UsersRequestBuilder
async def main():
credential = ClientSecretCredential(
os.environ["TENANT_ID"], os.environ["CLIENT_ID"], os.environ["CLIENT_SECRET"]
)
client = GraphServiceClient(credentials=credential,
scopes=["https://graph.microsoft.com/.default"])
query = UsersRequestBuilder.UsersRequestBuilderGetQueryParameters(
select=["id", "displayName", "department"], top=50
)
page = await client.users.get(
request_configuration=RequestConfiguration(query_parameters=query)
)
while page:
for user in page.value or []:
print(user.id, user.display_name, user.department)
if not page.odata_next_link:
break
page = await client.users.with_url(page.odata_next_link).get()
await credential.close()
asyncio.run(main())
ページングは odata_next_link が無くなるまで with_url で次のページを読む書き方で、READMEの例と同じ形です。SDKは429を受けたときの再試行も内蔵しています。失敗した呼び出しは APIError の例外になるので、同期処理では件数と例外の内容をログに残しておくと原因を追えます。
大量処理で詰まる429のスロットリングと$batch・デルタクエリ・変更通知の使い分け
数十人規模の検証では動いたのに、全社員や全サイトを対象にした途端に失敗する。Microsoft Graph APIの連携でよく起きる経過です。原因はほぼスロットリングで、設計で避けられます。
429とRetry-Afterを前提にした再試行と$batch内で失敗した要求の扱い
調整ガイドによると、上限を超えるとHTTP 429が返り、待つべき秒数が Retry-After ヘッダに入ります。待っている間も使用量は数えられ続けるため、すぐに再送するのは逆効果です。指示された秒数だけ待ってから再送します。テナントから大量のデータを抜き出す処理は通常のAPIではなく、Microsoft Graph Data Connectを使うよう公式ドキュメントが案内しています。
往復の回数を減らすにはJSONバッチ処理を使います。1回にまとめられる要求は20件までです。
POST https://graph.microsoft.com/v1.0/$batch
Content-Type: application/json
{
"requests": [
{ "id": "1", "method": "GET", "url": "/users?$select=id,displayName" },
{ "id": "2", "method": "GET", "url": "/groups?$select=id,displayName" },
{ "id": "3", "dependsOn": ["2"], "method": "GET", "url": "/sites/root" }
]
}
注意したいのは、バッチ全体が200で返っても中の要求が成功したとは限らない点です。各要求は個別にスロットリングの判定を受け、超えたものだけが429になります。応答の順序も要求の順と一致しないため、id で突き合わせます。SDKはバッチの外の要求なら自動で再試行しますが、バッチ内で429になった要求は再試行しません。dependsOn で順序を付けた要求は、前の要求が失敗すると424になります。
定期ポーリングをデルタクエリと変更通知の組み合わせに置き換える設計
社員一覧を毎時すべて読み直す作りは、件数が増えるほど429に近づきます。デルタクエリは、/users/delta のように呼ぶと全件と @odata.deltaLink を返し、次からはそのURLで前回以降の差分だけを返す仕組みです。ユーザーやグループのようなディレクトリのデータでは、このトークンの有効期間は7日です。410 Goneが返ったら全件の同期からやり直します。
差分を取りに行くきっかけには変更通知を使います。届け先はWebhook、Azure Event Hubs、Azure Event Gridの3つです。サブスクリプションには期限があり、ユーザーとグループは41,760分(29日弱)、メールと予定は10,080分(7日弱)、Teamsのメッセージは4,320分(3日)が上限なので、更新処理を必ず組み込みます。通知は変化があったことだけを知らせ、中身はデルタクエリで取りに行く組み合わせが基本です。Webhookで受ける側の冪等性はWebhookの作り方と実装方法の考え方がそのまま使えます。
Microsoft Graph APIで自作すべき連携と見送るべき連携の判断基準
作れることと、作るべきことは別です。旧APIの廃止と課金の前提を確認してから、自作に進む線を引きます。
Azure AD Graphの完全廃止と従量課金APIを踏まえた2026年時点の前提
Azure AD Graphからの移行によると、旧来のAzure AD Graphは2025年8月31日で延長アクセスも終わり、完全に廃止されました。graph.windows.net を呼んでいる古い連携が社内に残っていれば、Microsoft Graphへの書き換えが前提になります。
料金については、従量制課金APIの概要が、大半を占める標準のAPIは利用者のライセンスの範囲で追加費用なく使えると説明しています。大量のエクスポート用や高度な機能の一部は従量課金で、使うにはアプリをAzureのサブスクリプションに関連付ける必要があります。Teamsのメッセージを一括で書き出すような要件は、この課金の対象かどうかを先に確かめてください。
Power Automateで足りる連携とGraph APIの自作に進む条件の線引き
自作を見送ってよいのは、処理の起点も終点もMicrosoft 365の中で閉じる場合です。SharePointのリストに項目が増えたらTeamsへ知らせる、といった流れはPower AutomateのSharePoint連携やPower AutomateのTeams連携で組めます。サーバーを運用してまでGraph APIを呼ぶ理由はありません。
反対に、人事システムの社員マスタからEntra IDのアカウントを作る、基幹システムの帳票をSharePointへ毎日数千件保存する、承認の結果を社内DBへ書き戻す、といった要件が1つでもあれば自作の領域です。人事側の正本をSmartHRに置く場合の社員データの取り出し方は、SmartHR APIとは?従業員データの取得・更新とWebhook差分同期の実装手順で手順を示しています。アプリ専用アクセスの権限設計、429を前提にした再試行、サブスクリプションの更新まで含めて設計できる体制が社内に無い場合は、Microsoft 365・Copilot導入支援で連携部分だけを切り出して任せる方法もあります。
よくある質問
Microsoft Graph APIを使い始めるときに出やすい疑問を、公式ドキュメントの記述に沿ってまとめます。
Microsoft Graph APIは無料で使えますか?
大半のAPIは追加費用なしで使えます。公式ドキュメントでは、ユーザー・メール・ファイルなどを読み書きする標準のAPIは、利用者のMicrosoft 365ライセンスの範囲に含まれると説明されています。一方で、大量の書き出しを行うAPIや一部の高度な機能は従量課金で、アプリをAzureのサブスクリプションに関連付けないと呼べません。費用が発生するかどうかは、使うAPIが従量課金の一覧に載っているかで判断します。
Graph Explorerでは何を試せますか?
Graph エクスプローラーは、ブラウザからMicrosoft Graph APIを呼べる公式のツールです。サインインしなければデモ用のテナントのデータで、サインインすれば自分のテナントのデータで要求を試せます。応答を表示するための操作は、メソッドとバージョンを選び、URLを入力して実行する手順です。コードを書く前に、必要な許可と応答の形を確かめる用途に向いています。
アプリ専用アクセスで特定のメールボックスだけに絞れますか?
絞れます。Exchange OnlineのRBAC for Applicationsで、管理スコープや管理単位を使って対象のメールボックスを指定します。従来のApplication Access Policiesは、この仕組みに置き換えられました。ただしEntra ID側に組織全体の Mail.Read などが残っていると、和集合で全メールボックスが読める状態のままです。Entra ID側の許可を外してから絞ってください。
新しく作る連携は、Microsoft Graph APIを基本にしてください。SharePointのサイト・リスト・ファイルの操作はGraphで一通り行え、同じトークンでTeamsやEntra IDのデータも扱えます。SharePoint REST APIを選ぶのは、必要な操作がGraphにまだ用意されていない場合に限られます。2つのAPIを混ぜて構成する場合は、権限を管理する対象も二重になるため、その管理の負担にも注意が必要です。
アクセストークンの有効期限はどれくらいですか?
公式ドキュメントのクライアント資格情報フローの応答例では、expires_in が3599秒です。つまり、アクセストークンはおよそ1時間で有効期限が切れる前提で実装する設計です。MSALやazure-identityはトークンをキャッシュし、期限が近づくと自動で取り直します。自前でHTTPを組む場合は、期限の少し前に取り直す処理と、401が返ったら1回だけ取り直して再送する処理を入れておきます。
関連記事
- JWTとは?構造・署名検証の仕組みとセッション・OAuth/OIDCとの違いを実装視点で解説:Graphのアクセストークンの中身を読むときの基礎
- Microsoft Agent 365とは|SDK・CLIで自社エージェントを登録する手順と料金:Microsoft 365へ自社のAIエージェントを載せる場合の構成
- Microsoft 365 Copilotの情報漏洩対策|過剰共有の可視化とRCD・DLPの設定手順:テナント全体の許可が招く過剰共有の点検方法
- Microsoft 365とは|Office単体との違い・含まれるサービスと導入で詰まる点:Graphの背後にあるサービス群の全体像
- SharePointとは?できること・OneDriveやTeamsとの違いと導入判断の基準:ファイルやリストを連携先にする前の導入判断