メールAPI 完全ガイド — Microsoft Graph API & Gmail API 2026年5月仕様変更あり

この記事では、見出しの項目ごとに Gmail APIMicrosoftと 両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 CloudAzure
管理の器(課金・権限をまとめる単位)プロジェクトサブスクリプション / リソースグループ
アプリの認証情報(ID・シークレット)OAuthクライアントIDアプリ登録
Google Cloud プロジェクト (管理の器) API有効化(Gmail, Driveなど) 課金設定 OAuth同意画面 OAuthクライアントID クライアントID / シークレット ※ 複数作成可能 Azure サブスクリプション (管理の器) リソースグループ 課金・リソース管理 アプリ登録(Entra ID) クライアントID / シークレット APIアクセス許可

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を入力
    • 「保存して次へ」
  • 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) です。

個人アカウント、組織アカウントの違い

Graph API 組織アカウント Microsoft 365 /users/{id}/messages 他人のメール 共有メールボックス Client Credentials アプリケーション権限 管理者同意が必要 テナントID = 組織固有 ほぼ全API利用可 個人アカウント outlook.com等 /me/messages 自分のメールのみ Authorization Code 委任権限のみ 管理者同意は不要 テナントID = consumers 一部API制限あり ブラウザログイン必須
項目組織アカウント(Microsoft 365)個人アカウント(outlook.com等)
エンドポイント/users/{userId}/messages/me/messages
認証方式アプリケーション権限(Client Credentials)が使える委任権限(Authorization Code)のみ
ユーザー操作不要(サーバー間で完結)ブラウザでログイン・許可が必要
管理者同意必要(Azure AD管理者が許可)不要(自分で許可するだけ)
他人のメールボックスアクセス可能(権限があれば)自分のメールボックスのみ
使えるAPIほぼ全て一部制限あり(共有メールボックス、メールフォルダの一部操作など)
テナントID組織固有の値consumers という固定値を使う
MSALの設定authorityに組織のテナントIDを指定authorityhttps://login.microsoftonline.com/consumers を指定

個人で開発環境を用意する

M365開発者プログラムに参加する方法もある

Microsoft 365の機能を使ったサービスを開発するときのテスト環境として利用ができる

開発者プログラムへの参加自体はできましたが、E5サブスクリプション(組織テナント)はもらえない状態でサンドボックスは取得できません。


Azure無料アカウント作成
https://signup.azure.com/signup

Azure無料アカウント作成 azure.microsoft.com/ja-jp/free 今ここ Azure Portalにログイン portal.azure.com アプリ登録(Entra ID) クライアントID・シークレットを取得 認証(トークン取得) ブラウザでログイン → アクセストークン Graph API(/me/messages)呼び出し メールの取得・送信・下書き作成など

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のアクセス許可を設定する

  1. アプリ登録の概要画面の左メニューから「APIのアクセス許可」をクリック
  2. アクセス許可の追加」をクリック
  3. Microsoft Graph」を選択
  4. 委任されたアクセス許可」を選択
  5. 検索バーで以下を検索してチェックを入れる
    • Mail.Read
    • Mail.ReadWrite
    • Mail.Send
  6. アクセス許可の追加」をクリック

クライアントシークレットを作成する

  1. 左メニューから「証明書とシークレット」をクリック
  2. 新しいクライアントシークレット」をクリック
  3. 説明(例test-secret
  4. 有効期限「6か月」でOK(学習用なので短くて問題ない)
  5. 追加」をクリック

リソースについて

Graph APIのリソース階層

Graph APIにおける「リソース」とは、操作対象となるデータのこと。URL上で /users/, /messages/, /events/ のように表現される。

リソース
ユーザーの URL には、要求で操作するリソースが含まれます。たとえば、meusergroupdrivesite などです。 多くの場合、最上位のリソースには リレーションシップも含まれます。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
        └── ...

/me or /users/{id} /mailFolders /messages /calendar /contacts /drive
/me or /users/{id} /mailFolders /messages 全フォルダ横断 inbox drafts sentItems /messages /messages/{id} /createReply /send /attachments PATCH / DEL

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 (フォルダー名)
受信トレイINBOXInbox
送信済みSENTSentItems
下書きDRAFTDrafts
ゴミ箱TRASHDeletedItems
迷惑メールSPAMJunkEmail
未読UNREAD(プロパティで管理)
重要/スターSTARRED, IMPORTANT(プロパティで管理)

自分(ログインユーザー)以外のユーザーとメール共有する

こちら「Googleグループ」と「Outlook で共有メールボックス」を使用して可能です。

クセのある仕様が多いので別記事に切り出しまいしたので、そちら参照

あわせて読みたい
Googleグループ・Outlook で共有メールボックスでメールを共有 Google・Microsoftそれぞれ「Googleグループ」と「Outlook で共有メールボックス」を使用して可能です。 それぞれの仕様について公式ガイド・リファレンスを参考にまと...

ページネーション Google の nextPageToken vs Microsoft Graph の @odata.nextLink

観点Google APIMicrosoft Graph
位置づけレスポンス本体のフィールドOData プロトコルのアノテーション(@ 付きメタ情報)
中身トークン文字列のみ完全な URL
使い方元のクエリに pageToken= を付与して再送URL をそのまま GET

Google の nextPageToken はトークン文字列だけなので、元のリクエストの qlabelIdsmaxResults などのパラメータはクライアント側が自分で保持して毎回付与し直す必要があります。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.nextLink URL 値には、 $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詳細については、こちらの記事をご覧ください。

あわせて読みたい
【Microsoft Graph API】Microsoft Graph API クエリ パラメーターの$searchの挙動について調査 Microsoft Graph REST API v1.0 エンドポイント リファレンスhttps://learn.microsoft.com/ja-jp/graph/api/overview?view=graph-rest-1.0 リファレンスを参照して動作...

googleapis

googleapisはGoogleが公式提供しているNode.js向けクライアントライブラリで、google.gmail()はその中のGmail APIクライアント生成関数。

主要なメソッド階層

google.gmail()はクライアントインスタンスを返す関数で、引数にversionauthを渡す。versionは現状"v1"一択。authは認証済みのOAuth2Clientや、サービスアカウントの認証情報など。

返ってくるgmailオブジェクトに、Gmail APIの全エンドポイントがメソッドチェーンで生えている。

REST APIのパス構造(users.messages.listなど)がそのままメソッド名として表現されている。

userIdは必須で、"me"を渡すと認証中のユーザー自身を指す。

gmail.users.messages.list()はGmailのメッセージ一覧を取得するメソッド

パラメータ

パラメータ説明
userIdstring(必須)ユーザーのメールアドレス。"me"で認証中ユーザー
qstringGmail検索構文によるフィルタクエリ
maxResultsnumber1ページあたりの最大件数(デフォルト100、最大500)
pageTokenstring次ページ取得用のトークン
labelIdsstring[]ラベルIDで絞り込み(複数指定はAND)
includeSpamTrashbooleanSPAMと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-ToReferences に元のメールの Message-ID がセットされる。Gmailはこれを解析して、既存の threadId に返信メールをマージする。

スレッド構成の流れ 1. Aさんが新規投稿 Message-ID: <msg-001@a.com> → Gmail側で threadId: t-100 が生成 2. グループが全メンバーに配送 各メンバーのGmailに届く 各自のメールボックスで threadId 付与 3. Bさんが返信 In-Reply-To: <msg-001@a.com> References: <msg-001@a.com> → 既存の threadId: t-100 にマージ Gmail API のレスポンス threads.get(id=”t-100″) messages[0]: Aさんの投稿 Message-ID: <msg-001@a.com> messages[1]: Bさんの返信 In-Reply-To: <msg-001@a.com> ヘッダーの参照関係により、別々のメールが1つのスレッドに束ねられる

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 つの電子メール メッセージのための toRecipientsccRecipients、および bccRecipients プロパティに含まれる受信者の最大合計数は 500 です。 詳細については、「送信の制限」を参照してください。

From、Toでメールアドレスは取れるけど名前部分が取れない場合

Gmail API

Gmail APIはメールの中身をRFC 2822準拠の生ヘッダー文字列で返す。FromTo ヘッダーの形式は送信者のメールクライアントに依存し、"表示名" <address>address のみの両方がありえる。アドレスだけで送信された場合、名前部分は存在しない。Gmail API自体には名前を補完する仕組みがない。

Microsoft Graph API

理由: Graph APIは from.emailAddress.namefrom.emailAddress.address を構造化データとして返す。nameString 型で「The display name of the person or entity」と定義されている Microsoft Learnが、required/optionalの明示がなく、実際に空文字やアドレスと同値で返ってくるケースがある。特に組織外の送信者でAzure AD/Exchangeのディレクトリに登録がない場合に起きる。

ドキュメント/エビデンス

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 で定義済みなので、それをそのまま使え」という設計で、組み立て責任をクライアントに寄せています。

Gmail API:クライアントが MIME を組み立てて渡す クライアント MIME を自前で組み立て Subject: =?UTF-8?B?…?= MIME-Version: 1.0 Content-Type: multipart/… –boundary123… raw (base64url) Gmail API 薄いラッパー ストレージ RFC 822 の まま保存 件名のエンコードや boundary 生成はすべてクライアント側の責任

Graph 側 ストレージが MAPI プロパティ

一方 Exchange は、メールを MIME としてではなく MAPI プロパティの集合(subject、body、toRecipients…といった構造化データ)として保存しています。

MIME への変換は SMTP で外部に送信する瞬間にトランスポート層が行うだけで、内部的には最後まで構造化データです。

Microsoft Graph は OData ベースの「リソース指向 API」で、message もユーザーやカレンダーと同じ一つのリソースです。

内部モデルがもともと構造化されているので、JSON の PATCH でドラフトの subject だけ更新する、といった操作が自然にできます。MIME の組み立ては送信時にサーバーが引き受けます。

Microsoft Graph:JSON を渡し、MIME 化はサーバー任せ クライアント JSON を送るだけ { “subject”: “会議の件”, “toRecipients”: […], “body”: {…} } JSON Microsoft Graph (OData リソース) Exchange MAPI プロパティ で保存 送信の瞬間にサーバーが MIME に変換して SMTP へ クライアントは MIME を一切意識しない
Gmail APIGraph API
内部保存形式生の RFC 822 / MIMEMAPI プロパティ(構造化)
MIME 組み立ての責任クライアントサーバー(送信時)
設計思想既存標準の薄いラッパーGraph 全体で統一された OData リソース

トレードオフとしては、Gmail 方式はクライアント実装が面倒な代わりに MIME レベルの完全な制御ができ、Graph 方式は楽な代わりにスキーマが公開しているプロパティの範囲でしか操作できません(カスタムヘッダーは internetMessageHeaders 経由で一部可能、という程度)。

ちなみに Graph にも sendMail に base64 の MIME を直接渡すモードが一応ありますが、ドラフト更新(PATCH)は JSON のみです。

MIME 組み立ての責任分界点 アプリ API ストレージ 送信 (SMTP) Gmail MIME 組み立て 素通し RFC 822 保存 そのまま送出 Graph JSON 作成 JSON 受領 MAPI 保存 MIME 組み立て 同じ「MIME 化」がどこで起きるかだけが違う

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億) units1,200,000 units / 分
80,000,000(8,000万) units / 日
messages.send100100
messages.get520
messages.list55
messages.attachments.get520

たとえば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 件

目次