この記事では、見出しの項目ごとに Gmail API と Microsoftと 両APIを並べて比較調査をまとめてます

環境のセットアップやAPI使用するための準備など
Google Cloud の「プロジェクト」と Azure の「アプリ登録」の関係
Google Cloud Console と Azure Portal では、API利用のために登録する単位の名前が違う。
一見同じレイヤーに見えるが、厳密には対応関係がずれている。
Google Cloud の「プロジェクト」は、アプリそのものではない。
1つのプロジェクトの中に以下をまとめる上位の管理単位
- APIの有効化(Gmail API、Drive APIなど複数可)
- OAuthクライアントID(複数作成可能)
- 課金設定
- IAM(権限管理)
つまり、1プロジェクト=1アプリとは限らない。
Azure Portal の「アプリ登録」(Microsoft Entra ID)は、1つのアプリケーションに対してクライアントID・シークレット・API権限を定義する
Google側でいえば、プロジェクトではなくプロジェクト内で作る「OAuthクライアントID」に対応
対応関係
| レイヤー | Google Cloud | Azure |
|---|---|---|
| 管理の器(課金・権限をまとめる単位) | プロジェクト | サブスクリプション / リソースグループ |
| アプリの認証情報(ID・シークレット) | OAuthクライアントID | アプリ登録 |
Google側で「プロジェクト」と呼ばれている単位は Azure の「アプリ登録」とは1対1で対応しない。
ウェブアプリを1つ作るとき、実際に両方で対になるのは OAuthクライアントID(Google)とアプリ登録(Azure)のレイヤーになる。
Google API の使用で Google Workspace と 個人Googleアカウントの違い

| 項目 | Google Workspace | 個人Googleアカウント |
| 主な対象 | 企業、組織、学校 | 個人 |
| 料金 | 有料(1ユーザーごとの月額/年額) | 基本無料 |
| メールアドレス | 独自ドメイン(例)name@yourcompany.com | @gmail.com |
| アカウント管理 | 管理者が一括管理(作成、削除、権限設定) | 個人で管理 |
API利用における違い
OAuth同意画面を設定するとき、User Typeの選択肢が2つあります。
- 内部(Internal)
Google Workspaceの組織内ユーザーだけがアプリを使える。Workspace契約がないとこの選択肢はそもそも表示されない。 - 外部(External)
個人アカウント含め誰でも使える。個人アカウントで開発する場合はこれ一択。
個人Googleアカウントで開発環境を用意する
- Google Cloud Consoleでプロジェクトを作成する
- Gmail APIを有効化する
- 左メニュー「APIとサービス」→「ライブラリ」をクリック
- 検索バーに「Gmail API」と入力
- 「Gmail API」をクリック
- 「有効にする」をクリック
- OAuth同意画面を設定する
- 左メニュー「APIとサービス」→「OAuth同意画面」をクリック
- User Type「外部」を選択して「作成」
- アプリ名適当でOK(例)
gmail-test - ユーザーサポートメール 自分のGmailを選択
- デベロッパーの連絡先メールアドレス 自分のGmailを入力
- アプリ名適当でOK(例)
- 「保存して次へ」
- OAuthクライアントIDを作成する
- アプリケーションの種類「ウェブアプリケーション」を選択
- 名前適当でOK(例)
gmail-test-client - 「承認済みのリダイレクトURI」の「URIを追加」をクリック
http://localhost:3000/api/auth/google/callbackを入力- 「作成」をクリック
- 作成後、クライアントID と クライアントシークレット が表示されるので、メモ
- テストユーザー登録
- 「APIとサービス」→「OAuth同意画面」
Microsoft Graph API の使用する準備
Microsoft Graph APIではMicrosoftの様々なサービス(Teams、Outlook、Excelなど)のデータや機能を、外部のプログラムから操作ができます

そもそもMicrosoft Graph APIを使うには認証が必須
APIを使用するためには「どのアプリが、どの権限で、誰のデータにアクセスするのか」をMicrosoftに申告する必要がある
そのための場所が Azure Portal(https://portal.azure.com) です。
個人アカウント、組織アカウントの違い
| 項目 | 組織アカウント(Microsoft 365) | 個人アカウント(outlook.com等) |
|---|---|---|
| エンドポイント | /users/{userId}/messages | /me/messages |
| 認証方式 | アプリケーション権限(Client Credentials)が使える | 委任権限(Authorization Code)のみ |
| ユーザー操作 | 不要(サーバー間で完結) | ブラウザでログイン・許可が必要 |
| 管理者同意 | 必要(Azure AD管理者が許可) | 不要(自分で許可するだけ) |
| 他人のメールボックス | アクセス可能(権限があれば) | 自分のメールボックスのみ |
| 使えるAPI | ほぼ全て | 一部制限あり(共有メールボックス、メールフォルダの一部操作など) |
| テナントID | 組織固有の値 | consumers という固定値を使う |
| MSALの設定 | authorityに組織のテナントIDを指定 | authorityに https://login.microsoftonline.com/consumers を指定 |
個人で開発環境を用意する
M365開発者プログラムに参加する方法もある
Microsoft 365の機能を使ったサービスを開発するときのテスト環境として利用ができる
https://developer.microsoft.com/en-us/microsoft-365/dev-program にアクセス無料で90日間のMicrosoft 365開発用テナント(組織アカウント)がもらえるGraph APIの全機能を試せる
開発者プログラムへの参加自体はできましたが、E5サブスクリプション(組織テナント)はもらえない状態でサンドボックスは取得できません。
Azure無料アカウント作成
https://signup.azure.com/signup
https://azure.microsoft.com/ja-jp

検索バーで「Microsoft Entra ID」と検索
Entra IDにアプリを登録 → 「アプリの認証・認可を管理」
アプリ登録画面で
- 名前
graph-mail-test(任意) - サポートされているアカウントの種類「任意の組織ディレクトリ内のアカウントと、個人用のMicrosoftアカウント」に変更してください。「シングルテナントのみ」のままだと個人アカウントでのログインができません
- リダイレクトURI プラットフォームを「Web」にして、
http://localhost:3000/auth/callbackと入力
登録完了したら、概要画面に以下の情報が表示
- アプリケーション(クライアント)ID
- ディレクトリ(テナント)ID
APIのアクセス許可を設定する
- アプリ登録の概要画面の左メニューから「APIのアクセス許可」をクリック
- 「アクセス許可の追加」をクリック
- 「Microsoft Graph」を選択
- 「委任されたアクセス許可」を選択
- 検索バーで以下を検索してチェックを入れる
Mail.ReadMail.ReadWriteMail.Send
- 「アクセス許可の追加」をクリック
クライアントシークレットを作成する
- 左メニューから「証明書とシークレット」をクリック
- 「新しいクライアントシークレット」をクリック
- 説明(例
test-secret) - 有効期限「6か月」でOK(学習用なので短くて問題ない)
- 「追加」をクリック
リソースについて
Graph APIのリソース階層
Graph APIにおける「リソース」とは、操作対象となるデータのこと。URL上で /users/, /messages/, /events/ のように表現される。
リソース
ユーザーの URL には、要求で操作するリソースが含まれます。たとえば、me、user、group、drive、site などです。 多くの場合、最上位のリソースには リレーションシップも含まれます。me/messagesまたはme/driveのように、追加のリソースにアクセスするのに使用できます。
https://learn.microsoft.com/ja-jp/graph/use-the-api?source=recommendations#resource
エンティティ(Entity)と複合型(Complex Type)の違い
どちらもリソースの定義方法だが、決定的な違いは id プロパティの有無。
- エンティティ(Entity) —
idを持つ。単独で存在し、直接アドレス指定できる。 - 複合型(Complex Type) —
idを持たない。エンティティの一部としてのみ存在し、単独でアドレス指定できない。
メール機能での具体例
| 種類 | 例 | id | 単独で取得可能か |
|---|---|---|---|
| エンティティ | message, user, mailFolder | あり | /me/messages/{id} で可能 |
| 複合型 | emailAddress, itemBody, recipient | なし | 不可。親エンティティに含まれる |
Microsoft Graph を使用すると、他のユーザーやグループ、グループ メンバーシップ、メール、予定表、ファイル、管理ロールなど、アクセスするリソースなど、ユーザーとその他のオブジェクトとの関係に基づいて魅力的なアプリ エクスペリエンスを構築できます。
https://learn.microsoft.com/ja-jp/graph/api/resources/users?view=graph-rest-1.0
メールはトップレベルリソースではなく、あくまでユーザーリソース
graph.microsoft.com/v1.0
└── /me または /users/{id|UPN} ← ユーザーリソース
├── /mailFolders ← メールフォルダ
│ └── /messages ← フォルダ内のメッセージ
├── /messages ← 全メッセージ横断
├── /calendar ← カレンダー
├── /contacts ← 連絡先
├── /drive ← OneDrive
├── /teams ← Teams
├── /onenote ← OneNote
└── ...Graph APIのメールはフォルダ階層構造で管理されている
mailFolders を一覧表示する
https://learn.microsoft.com/ja-jp/graph/api/user-list-mailfolders?view=graph-rest-1.0&tabs=http
/me/mailFolders/inbox/messages — 受信トレイ
/me/mailFolders/drafts/messages — 下書き
/me/mailFolders/sentitems/messages — 送信済み
/me/mailFolders/deleteditems/messages — ゴミ箱(削除済みアイテム)
/me/mailFolders/junkemail/messages — 迷惑メール
/me/mailFolders/archive/messages — アーカイブ
/me/mailFolders/outbox/messages — 送信トレイGraph APIの「フォルダー」という概念、階層構造になっていて直感的でわかりやすいですよね。
一方、GoogleのGmail APIでは、フォルダーではなく「ラベル(Label)」を採用しています。
システムラベルの対応表
Graph APIの主要なフォルダーは、Gmail APIでは「システムラベル」として定義されています。
| 概念 | Gmail API (ラベルID) | Graph API (フォルダー名) |
|---|---|---|
| 受信トレイ | INBOX | Inbox |
| 送信済み | SENT | SentItems |
| 下書き | DRAFT | Drafts |
| ゴミ箱 | TRASH | DeletedItems |
| 迷惑メール | SPAM | JunkEmail |
| 未読 | UNREAD | (プロパティで管理) |
| 重要/スター | STARRED, IMPORTANT | (プロパティで管理) |
自分(ログインユーザー)以外のユーザーとメール共有する
こちら「Googleグループ」と「Outlook で共有メールボックス」を使用して可能です。
クセのある仕様が多いので別記事に切り出しまいしたので、そちら参照

ページネーション Google の nextPageToken vs Microsoft Graph の @odata.nextLink
| 観点 | Google API | Microsoft Graph |
|---|---|---|
| 位置づけ | レスポンス本体のフィールド | OData プロトコルのアノテーション(@ 付きメタ情報) |
| 中身 | トークン文字列のみ | 完全な URL |
| 使い方 | 元のクエリに pageToken= を付与して再送 | URL をそのまま GET |
Google の nextPageToken はトークン文字列だけなので、元のリクエストの q、labelIds、maxResults などのパラメータはクライアント側が自分で保持して毎回付与し直す必要があります。pageToken だけ渡すとフィルタなしの全件走査になります。
Graph の @odata.nextLink は $filter、$select、$top などのクエリパラメータをすべて含んだ完全な URL が返ってくるので、クライアントはそれをそのまま GET するだけで済みます。
呼び出し側がフィルタ条件を保持・再構成する必要がありません。
https://learn.microsoft.com/ja-jp/graph/paging?tabs=http#server-side-paging
@odata.nextLinkプロパティの URL 全体を使用して、結果の次のページを取得します。 クエリが実行される API に応じて、@odata.nextLinkURL 値には、$skiptokenまたは$skipクエリ パラメーターが含まれます。
メール一覧取得リクエストで使用できる検索やフィルターの設定など
gmail api は q パラメータによる検索フィルタ
Gmail APIのqパラメータはGmailのWeb UIの検索バーと同じ構文が使える。メールのフィルタリングに強力な機能だ。よく使うパターンをいくつか紹介する。
// 送信者で絞り込み
const query1 = "from:notifications@example.com";
// 日付範囲で絞り込み
const query2 = "after:2025/01/01 before:2025/04/01";
// 特定ドメインからのメールを除外
const query3 = "-from:@spam-domain.com";
// 複数ドメインを除外
const query4 = "-from:@marketing.example.com -from:@newsletter.example.com";
// ラベルと未読を組み合わせ
const query5 = "label:work is:unread";
// 件名に特定のキーワードを含む
const query6 = "subject:請求書";
// 添付ファイル付き + 特定サイズ以上
const query7 = "has:attachment larger:5M";
// 組み合わせた実践的な例:
// 今年の未読メールで、社内ドメインを除外し、添付ファイル付き
const practicalQuery =
"is:unread has:attachment after:2025/01/01 -from:@mycompany.com";
https://developers.google.com/workspace/gmail/api/guides/filtering?hl=ja
ドメイン除外は-from:@domain.comの形式で書く。ハイフン(-)がNOT演算子として機能する。複数ドメインを除外したい場合はスペース区切りで-from:を並べればよい。
このパラメータは、Gmail ウェブ インターフェースと同じ高度な検索構文のほとんどをサポートしています。
https://developers.google.com/workspace/gmail/api/reference/rest/v1/users.messages/list?hl=ja
Graph API は $filter $search で
$search詳細については、こちらの記事をご覧ください。

googleapis
googleapisはGoogleが公式提供しているNode.js向けクライアントライブラリで、google.gmail()はその中のGmail APIクライアント生成関数。
主要なメソッド階層
google.gmail()はクライアントインスタンスを返す関数で、引数にversionとauthを渡す。versionは現状"v1"一択。authは認証済みのOAuth2Clientや、サービスアカウントの認証情報など。
返ってくるgmailオブジェクトに、Gmail APIの全エンドポイントがメソッドチェーンで生えている。
REST APIのパス構造(users.messages.listなど)がそのままメソッド名として表現されている。
userIdは必須で、"me"を渡すと認証中のユーザー自身を指す。
gmail.users.messages.list()はGmailのメッセージ一覧を取得するメソッド
パラメータ
| パラメータ | 型 | 説明 |
|---|---|---|
userId | string(必須) | ユーザーのメールアドレス。"me"で認証中ユーザー |
q | string | Gmail検索構文によるフィルタクエリ |
maxResults | number | 1ページあたりの最大件数(デフォルト100、最大500) |
pageToken | string | 次ページ取得用のトークン |
labelIds | string[] | ラベルIDで絞り込み(複数指定はAND) |
includeSpamTrash | boolean | SPAMとTRASHを含めるか(デフォルトfalse) |
Gmail はスレッド(threadId)Graph API は 会話(conversationId)でメールをグルーピング
Gmail APIがスレッドを組み立てる仕組み
Gmail APIが複数のメッセージを同一の threadId にまとめる基準は、メールのRFCヘッダーに依存している。関係するヘッダーは3つある。
- Message-ID … 各メールに付与される一意のID
- In-Reply-To … 返信先メールの
Message-ID - References … 会話中の
Message-ID履歴
新規メールがグループに投稿されると、各メンバーのGmailに配送された時点で新しい threadId が生成される。そのメールに対して誰かが返信すると、返信メールの In-Reply-To と References に元のメールの Message-ID がセットされる。Gmailはこれを解析して、既存の threadId に返信メールをマージする。
Gmailは返信の認識をどのようにしている?
正直に言うと、公式ドキュメントで「この3つを設定すれば返信として認識される」と明記した1ページは存在しないと思います。根拠は複数のソースにまたがっています
threadId→ Gmail API 公式ドキュメントの Threads に「drafts.createでthreadIdを指定するとそのスレッドに追加される」と記載があるIn-Reply-To/References→ これは Gmail 固有ではなく RFC 2822(メールの標準仕様) のヘッダー。メーラーが返信ツリーを構築するための標準的な仕組み
- Gmail のスレッドに入れる →
threadId(Gmail API の仕様) - メーラー上で返信として表示させる →
In-Reply-To/References(RFC 2822 の仕様)

レートリミット対策
Gmail API を使用する場合、1 つのメール メッセージあたりの受信者の上限は 500 人です。
https://developers.google.com/workspace/gmail/api/reference/quota?hl=ja
Gmail API には使用量上限があり、API のメソッドを呼び出すことができる頻度が制限されます。上限は割り当て ユニットで定義されます。割り当てユニットは、 Gmail リソースの使用量を表す抽象的な測定単位です。使用量上限には、プロジェクトごとのレート制限とユーザーごとのレート制限の 2 種類があり、これらが同時に適用されます。
受信者の最大合計数は 500 です。
https://learn.microsoft.com/ja-jp/graph/api/resources/message?view=graph-rest-1.0
Exchange Online メールボックスから送信される 1 つの電子メール メッセージのための toRecipients、ccRecipients、および bccRecipients プロパティに含まれる受信者の最大合計数は 500 です。 詳細については、「送信の制限」を参照してください。

From、Toでメールアドレスは取れるけど名前部分が取れない場合
Gmail API
Gmail APIはメールの中身をRFC 2822準拠の生ヘッダー文字列で返す。From や To ヘッダーの形式は送信者のメールクライアントに依存し、"表示名" <address> と address のみの両方がありえる。アドレスだけで送信された場合、名前部分は存在しない。Gmail API自体には名前を補完する仕組みがない。
- Gmail APIで下書きを作成した場合、Fromヘッダーにアドレスだけが含まれ表示名が含まれないケースがある GMass
- Gmail API
messages.getリファレンス: https://developers.google.com/workspace/gmail/api/reference/rest/v1/users.messages- From/Toの詳しい挙動解説: https://www.gmass.co/blog/gmail-api-from-addresses-names/
Microsoft Graph API
理由: Graph APIは from.emailAddress.name と from.emailAddress.address を構造化データとして返す。name は String 型で「The display name of the person or entity」と定義されている Microsoft Learnが、required/optionalの明示がなく、実際に空文字やアドレスと同値で返ってくるケースがある。特に組織外の送信者でAzure AD/Exchangeのディレクトリに登録がない場合に起きる。
ドキュメント/エビデンス
emailAddressリソース型の定義: https://learn.microsoft.com/en-us/graph/api/resources/emailaddress?view=graph-rest-1.0messageリソース型の定義: https://learn.microsoft.com/en-us/graph/api/resources/message?view=graph-rest-1.0
Graph APIのドキュメントには「name が空になる場合がある」とは明記されていない。ただし型定義上 name は nullable な String であり、必須とも書かれていないので、空やアドレスと同値になる可能性は排除されていない。実際に実運用で確認できているなら、それ自体がエビデンスになる。

受信メール / 送信メールの判断
- Gmail APIはラベル(INBOX, SENT)
- Graph APIはフォルダ(inboxs, sentItems)
- IMFではこのメールは受信/送信という属性は持たない、メール自体は方向性を持たない
メール作成時にGoogle API は MIME形式にする必要がある、Graph API は MIME化はサーバーに任せれる
Gmail 側 は base64url エンコードした MIME を受け取り、ほぼそのまま格納・送信
Google が「メールの標準形式はすでに RFC で定義済みなので、それをそのまま使え」という設計で、組み立て責任をクライアントに寄せています。
Graph 側 ストレージが MAPI プロパティ
一方 Exchange は、メールを MIME としてではなく MAPI プロパティの集合(subject、body、toRecipients…といった構造化データ)として保存しています。
MIME への変換は SMTP で外部に送信する瞬間にトランスポート層が行うだけで、内部的には最後まで構造化データです。
Microsoft Graph は OData ベースの「リソース指向 API」で、message もユーザーやカレンダーと同じ一つのリソースです。
内部モデルがもともと構造化されているので、JSON の PATCH でドラフトの subject だけ更新する、といった操作が自然にできます。MIME の組み立ては送信時にサーバーが引き受けます。
| Gmail API | Graph API | |
|---|---|---|
| 内部保存形式 | 生の RFC 822 / MIME | MAPI プロパティ(構造化) |
| MIME 組み立ての責任 | クライアント | サーバー(送信時) |
| 設計思想 | 既存標準の薄いラッパー | Graph 全体で統一された OData リソース |
トレードオフとしては、Gmail 方式はクライアント実装が面倒な代わりに MIME レベルの完全な制御ができ、Graph 方式は楽な代わりにスキーマが公開しているプロパティの範囲でしか操作できません(カスタムヘッダーは internetMessageHeaders 経由で一部可能、という程度)。
ちなみに Graph にも sendMail に base64 の MIME を直接渡すモードが一応ありますが、ドラフト更新(PATCH)は JSON のみです。
Gmail API Graph API 比較
Gmail API vs Microsoft Graph API ― メッセージ構造比較
============================================================
■ エビデンス
├── Gmail : https://developers.google.com/gmail/api/reference/rest/v1/users.messages
├── Gmail : https://developers.google.com/workspace/gmail/api/guides/threads
├── MS Graph : https://learn.microsoft.com/en-us/graph/api/resources/message?view=graph-rest-1.0
└── MS Graph : https://learn.microsoft.com/en-us/graph/api/message-get?view=graph-rest-1.0
■ メッセージ全体構造
│
├── Gmail API (users.messages)
│ ├── id : string (16進, 例: "18e5a3b...")
│ ├── threadId : string (16進, スレッドの先頭メッセージIDと同値)
│ ├── labelIds : string[]
│ ├── snippet : string
│ ├── historyId : string
│ ├── internalDate : string (epoch ms, 例: "1704067200000")
│ ├── sizeEstimate : number
│ ├── raw : string (RFC2822 base64url, format=RAW時のみ)
│ └── payload : MessagePart (後述)
│
└── Microsoft Graph API (message resource)
├── id : string (Base64風, 例: "AAMkAG...")
├── conversationId : string (Base64風, 例: "AAQkAD...")
├── conversationIndex : string (binary)
├── subject : string
├── bodyPreview : string
├── body : { contentType: string, content: string }
├── from : { emailAddress: { name: string, address: string } }
├── toRecipients : { emailAddress: { name, address } }[]
├── ccRecipients : { emailAddress: { name, address } }[]
├── bccRecipients : { emailAddress: { name, address } }[]
├── receivedDateTime : string (ISO 8601, 例: "2026-01-01T00:00:00Z")
├── sentDateTime : string (ISO 8601)
├── createdDateTime : string (ISO 8601)
├── lastModifiedDateTime : string (ISO 8601)
├── isDraft : boolean
├── isRead : boolean
├── hasAttachments : boolean
├── importance : string ("low" | "normal" | "high")
├── categories : string[]
├── webLink : string
├── parentFolderId : string
└── flag : { flagStatus: string }
■ スレッドID
│
├── Gmail
│ ├── フィールド名 : threadId
│ ├── 型 : string (16進)
│ ├── 意味 : 同一会話のメッセージをグルーピング
│ └── 特徴 : スレッド先頭メッセージのidと同一値
│ ユーザーごとに固有(送信者と受信者で異なる)
│
└── MS Graph
├── フィールド名 : conversationId
├── 型 : string (Base64風)
├── 意味 : 同一会話のメッセージをグルーピング
└── 特徴 : 自動生成・不変
ユーザーごとに固有
→ 役割は同一。共通型では threadId: string に統一可能
■ 宛先情報の取得方法
│
├── Gmail
│ └── payload.headers[] から文字列パース
│ ├── { name: "From", value: "John Doe <john@example.com>" }
│ ├── { name: "To", value: "alice@example.com, Bob <bob@example.com>" }
│ └── { name: "Cc", value: "..." }
│ → RFC 2822 形式の文字列をMIMEパーサーで分解する必要あり
│
└── MS Graph
└── トップレベルにオブジェクト構造で提供
├── from: { emailAddress: { name: "John Doe", address: "john@example.com" } }
├── toRecipients: [{ emailAddress: { name, address } }, ...]
└── ccRecipients: [{ emailAddress: { name, address } }, ...]
→ パース不要、そのまま使える
■ 本文の取得方法
│
├── Gmail
│ └── payload (MessagePart) の再帰構造
│ ├── mimeType : string (例: "multipart/alternative", "text/plain", "text/html")
│ ├── body : { data: string(base64url), size: number }
│ ├── parts : MessagePart[] (再帰)
│ └── 取得手順:
│ 1. payload.parts を再帰的に走査
│ 2. mimeType が "text/plain" or "text/html" の part を探す
│ 3. body.data を base64url デコード
│
└── MS Graph
└── トップレベルにフラット構造で提供
├── body.contentType : string ("text" | "html")
├── body.content : string (デコード済み本文)
└── bodyPreview : string (プレビュー)
→ デコード不要、そのまま使える
■ 日時
│
├── Gmail
│ └── internalDate : string (epoch ms)
│ 例: "1704067200000"
│ → new Date(Number(internalDate)).toISOString() で変換
│
└── MS Graph
└── receivedDateTime : string (ISO 8601)
例: "2026-01-01T00:00:00Z"
→ そのまま使える
■ 既読・下書き状態
│
├── Gmail
│ └── labelIds で判定
│ ├── "UNREAD" が含まれる → 未読
│ ├── "DRAFT" が含まれる → 下書き
│ └── 専用フィールドなし
│
└── MS Graph
└── 専用booleanフィールド
├── isRead : boolean
└── isDraft : boolean
■ 添付ファイル
│
├── Gmail
│ └── payload.parts[] 内で判定
│ ├── filename が空でない part → 添付ファイル
│ └── body.attachmentId → messages.attachments.get で取得
│
└── MS Graph
└── 専用フィールド + 別エンドポイント
├── hasAttachments : boolean
└── /messages/{id}/attachments で一覧取得
■ ページネーション
│
├── Gmail
│ ├── レスポンス : nextPageToken: string
│ └── リクエスト : pageToken パラメータに渡す
│
└── MS Graph
├── レスポンス : @odata.nextLink: string (完全URL)
└── リクエスト : そのURLをそのままGETするAPI制限
Microsoft Graph API(メール)
メールボックスあたりの制限
https://learn.microsoft.com/ja-jp/graph/throttling-limits#outlook-service-limits
「ユーザー」はここではアクセス先のメールボックスの所有者を指す。ログインしているユーザー(操作者)ではない。
つまり、アプリAがメールボックスBにアクセスする場合、10分10,000リクエストの制限はメールボックスBに対してカウントされる。同じアプリAがメールボックスCにもアクセスしていれば、Cには別枠で10,000リクエストが使える。
Gmail API (2026年5月以降の新仕様)
Gmail API の割り当て
https://developers.google.com/workspace/gmail/api/reference/quota?hl=ja
2026年5月以降の新規プロジェクトは新仕様
以前は下記記事の通り毎秒単位の制限
http://xn--unipile-jv0lp571c.com/read-email-api/
レート制限とバックオフ
Gmailは、ユーザー1人あたり1秒あたり250クォータ単位(リスト表示に5単位、データ取得に5単位)の制限を設けています。Microsoft Graphは、テナント1つあたりアプリ1つにつき10分間に10,000リクエストに制限されています。どちらも、ジッターを伴う指数バックオフを必要とする429エラーを返します。リンクされたアカウントが1,000個になると、レート制限の管理は本格的なエンジニアリング問題となります。
https://stackoverflow.com/questions/52542789/check-specific-user-account-quota-usage-for-gmail-api
ユーザーごとのレート制限:1ユーザーあたり1秒あたり250クォータユニット(移動平均、短時間のバーストは許容)
messages.sendこのメソッドは100クォータユニットを消費します
messages.getこのメソッドは5クォータ単位を消費します
messages.listこのメソッドは5クォータ単位を消費します
messages.attachments.getこのメソッドは5クォータ単位を消費します
| 比較内容 | 以前 | 2026年5月以降 |
|---|---|---|
| ユーザーごとの制限 | 250 units / 秒 | 6,000 units / 分 |
| プロジェクトごとの制限 | 1日あたり 1,000,000,000(10億) units | 1,200,000 units / 分 80,000,000(8,000万) units / 日 |
messages.send | 100 | 100 |
messages.get | 5 | 20 |
messages.list | 5 | 5 |
messages.attachments.get | 5 | 20 |
たとえば1回のAPI呼び出しでリストを取得し、そのリストに含まれるメールをすべて get で取得する場合
以前(旧仕様)「1秒間に最大49件」
まず list を1回実行で残り 245 units(250 – 5)残り枠で get(1件5 units)を実行で 245 ÷ 5 = 49 件
2026年5月以降(新仕様)「1分間に最大299件」
まず list を1回実行で残り 5,995 units(6,000 – 5)残り枠で get(1件20 units)を実行で 5,995 ÷ 20 = 299.75 件