This chapter covers Microsoft Graph API integration, a critical skill for the AZ-204 exam as it appears in approximately 15-20% of questions in the 'Security' domain (Objective 3.1). You will learn how to authenticate, query, and manipulate Microsoft 365 data (users, groups, mail, calendar, files) using a unified RESTful endpoint. The chapter dives deep into OAuth 2.0 flows, permission scopes, pagination, batching, and delta queries—all essential for building applications that interact with Microsoft 365 services.
Jump to a section
Imagine a government that consolidates all its departments—tax, health, education, permits—into a single, unified digital portal. Instead of citizens visiting different buildings, filling out separate forms, and dealing with different clerks, they log into one portal with a single ID. The portal has a standardized request format: you submit a form with your ID, the department you want, and the action you need. The portal authenticates you once, then routes your request to the appropriate department, translates it into that department's internal system, and returns the response in a standard format. If you need to update your address, you submit one request to the portal, and it propagates the change to all relevant departments automatically. This portal also enforces security: you can only access departments you're authorized for, and your ID token expires after a set time. Microsoft Graph API works exactly like this—it's a unified REST endpoint (https://graph.microsoft.com/v1.0) that aggregates Microsoft 365 services (Azure AD, Outlook, Teams, SharePoint, etc.) behind a single API, using OAuth 2.0 tokens for authentication and providing a consistent JSON response format. Just as the government portal reduces complexity for citizens, Graph API reduces the need to learn and interact with multiple separate Microsoft service APIs.
What is Microsoft Graph API and Why It Exists
Microsoft Graph API is a RESTful web API that provides a single endpoint to access data and intelligence across Microsoft 365, Azure Active Directory, and other Microsoft cloud services. Before Graph, developers had to use separate APIs for Azure AD (Azure AD Graph), Outlook (Exchange Web Services), SharePoint (CSOM), and others—each with different authentication, request/response formats, and endpoint URLs. Graph unifies these under https://graph.microsoft.com/v1.0 (or /beta for preview features).
How It Works Internally
All requests to Microsoft Graph require authentication via OAuth 2.0. The flow typically involves:
- App Registration: In Azure AD, you register your application (app registration), which generates a Client ID (Application ID) and optionally a Client Secret or certificate.
- Permissions Scopes: You configure required permissions (e.g., User.Read, Mail.Send). These can be delegated (on behalf of a signed-in user) or application-only (daemon/service).
- Token Acquisition: Your app obtains an access token from the Azure AD token endpoint (https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token). For delegated permissions, use the authorization code flow or implicit flow; for app-only, use client credentials flow.
- API Call: The access token is included in the Authorization: Bearer header of requests to https://graph.microsoft.com/v1.0/{resource}.
- Response: Graph returns JSON responses. For collections, the response includes @odata.nextLink for pagination.
Key Components, Values, Defaults, and Timers
Base URL: https://graph.microsoft.com/v1.0 (stable) or /beta (preview).
Authentication Endpoint: https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token where {tenant} can be common (multi-tenant), organizations, consumers, or a specific tenant ID.
Token Lifetime: Default access token lifetime is 60-90 minutes (configurable in Azure AD). Refresh tokens last up to 90 days if not revoked.
Pagination: Default page size for most resources is 100 items. Use $top (max 999) and $skip or @odata.nextLink.
Batching: Up to 20 requests in one batch call to $batch endpoint. Requests are executed sequentially by default (order-dependent).
Delta Query: Tracks changes over time via delta function. Returns a delta token (@odata.deltaLink) that can be used to get subsequent changes.
Throttling: Graph API has per-app and per-tenant throttling limits. For example, read requests for users: 10,000 requests per 10 minutes per app per tenant.
Configuration and Verification Commands
To register an app and get tokens using Azure CLI:
# Register app
az ad app create --display-name "MyGraphApp" --sign-in-audience AzureADMyOrg
# Get Client ID and Tenant ID
az ad app list --display-name "MyGraphApp" --query "[].appId"
# Create client secret
az ad app credential reset --id <appId> --append --years 2
# Get access token using client credentials flow (app-only)
az account get-access-token --resource https://graph.microsoft.com --query accessToken -o tsvFor delegated flows, use MSAL libraries (e.g., MSAL.NET, MSAL.js). Example token acquisition in C#:
using Azure.Identity;
using Microsoft.Graph;
var scopes = new[] { "https://graph.microsoft.com/.default" };
var clientSecretCredential = new ClientSecretCredential(tenantId, clientId, clientSecret);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
var user = await graphClient.Users["user@domain.com"].Request().GetAsync();Interaction with Related Technologies
Graph API integrates with: - Azure AD: User, group, and device management. - Outlook/Exchange: Mail, calendar, contacts. - SharePoint/OneDrive: Files, lists, sites. - Teams: Channels, messages, meetings. - Planner: Tasks and plans. - Security: Identity protection alerts, risk events.
Graph also supports webhooks (subscriptions) to receive change notifications, with a validation handshake: Azure AD sends a POST to your notification URL with a validationToken query parameter; your endpoint must respond with that token in the body within 10 seconds (HTTP 200).
Permissions and Consent
Permissions are categorized as: - Delegated: App acts on behalf of signed-in user. Requires user consent or admin consent. - Application: App runs as a background service without a signed-in user. Requires admin consent.
Common permission scopes:
- User.Read.All: Read all users' full profiles.
- Mail.Send: Send mail as any user.
- Files.ReadWrite.All: Read and write all files that the signed-in user can access.
- Group.ReadWrite.All: Read and write all groups.
Admin consent can be granted via Azure portal or programmatically using the adminconsent endpoint.
Error Handling
Graph API returns standard HTTP status codes:
- 200 OK for success.
- 201 Created for resource creation.
- 204 No Content for deletion or updates without body.
- 401 Unauthorized (invalid/expired token).
- 403 Forbidden (insufficient permissions).
- 404 Not Found.
- 429 Too Many Requests (throttling). Includes Retry-After header.
Error responses contain JSON with error.code and error.message. Common codes: AuthenticationError, Authorization_RequestDenied, Request_BadRequest, ResourceNotFound.
Best Practices
Always use @odata.nextLink for pagination; do not assume page size.
Use delta queries for efficient synchronization instead of pulling full data repeatedly.
Batch related requests to reduce round trips.
Implement exponential backoff for throttling (retry after Retry-After or at least 5 seconds).
Use application permissions only when necessary; prefer delegated to follow user context.
Store tokens securely; never expose client secrets in client-side code.
Common Pitfalls
Wrong permission scope: E.g., using User.Read when User.Read.All is required for reading all users.
Missing admin consent: Application permissions always require admin consent; delegated permissions may require it for high-privilege scopes.
Incorrect token acquisition flow: Using implicit flow for server-side apps (should use authorization code flow).
Ignoring pagination: Only reading first 100 users and assuming all are returned.
Not handling throttling: Causing 429 errors and potential app suspension.
Step-by-Step: Registering an App and Calling Graph API
Create App Registration: In Azure portal, go to Azure Active Directory > App registrations > New registration. Enter name, supported account types (e.g., Accounts in this organizational directory only).
Note the Application (client) ID and Directory (tenant) ID.
Configure API Permissions: Under API permissions, click Add a permission > Microsoft Graph. Choose delegated or application permissions. Select scopes (e.g., User.Read.All). Click Grant admin consent if required.
Create a Client Secret: Under Certificates & secrets > New client secret. Add description and expiry (e.g., 2 years). Copy the secret value immediately.
Get Access Token: Use the following HTTP request:
POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&client_id={client_id}
&client_secret={client_secret}
&scope=https://graph.microsoft.com/.defaultCall Graph API: Use the token in Authorization header.
GET https://graph.microsoft.com/v1.0/users
Authorization: Bearer {access_token}Parse Response: JSON contains value array and @odata.nextLink if more results.
Advanced: Batching Example
Batch request:
POST https://graph.microsoft.com/v1.0/$batch
Content-Type: application/json
Authorization: Bearer {token}
{
"requests": [
{
"id": "1",
"method": "GET",
"url": "/users"
},
{
"id": "2",
"method": "GET",
"url": "/groups"
}
]
}Response contains ordered responses with status codes and bodies.
Advanced: Delta Query Example
First call:
GET https://graph.microsoft.com/v1.0/users/delta
Authorization: Bearer {token}Response includes @odata.deltaLink for next changes and @odata.nextLink for next page. Subsequent calls use deltaLink to get only changes since last call.
Register an Application in Azure AD
Navigate to Azure Active Directory > App registrations > New registration. Provide a name (e.g., 'My Graph App'), select supported account types (e.g., 'Accounts in this organizational directory only' for single-tenant). This creates an app registration with a unique Application (client) ID. The registration is the identity of your app that Azure AD uses to issue tokens. Also note the Directory (tenant) ID. After creation, you can configure authentication settings like redirect URIs for delegated flows.
Configure API Permissions
Under API permissions, click 'Add a permission', choose Microsoft Graph. Select either 'Delegated permissions' (requires signed-in user) or 'Application permissions' (runs in background). Choose scopes like User.Read.All, Mail.Send, etc. For application permissions, you must grant admin consent by clicking 'Grant admin consent for {tenant}'. Without consent, calls will return 403 Forbidden. Permissions are evaluated at runtime; the token includes the granted scopes.
Create a Client Secret or Certificate
Under Certificates & secrets, create a new client secret. Add a description and set expiry (recommended maximum 2 years). Copy the secret value immediately; it is not displayed again. For higher security, you can upload a certificate instead. The secret or certificate is used to authenticate your app when requesting tokens. Never expose secrets in client-side code.
Request an Access Token
Send an HTTP POST to https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token with Content-Type application/x-www-form-urlencoded. Include grant_type=client_credentials (for app-only), client_id, client_secret, and scope=https://graph.microsoft.com/.default. The response contains an access_token (JWT), expires_in (seconds), and token_type (Bearer). The token is valid for the default lifetime (60-90 minutes). Use this token in subsequent API calls.
Call Microsoft Graph API
Make an HTTP GET request to https://graph.microsoft.com/v1.0/users with Authorization: Bearer {access_token}. The response is JSON. For collections, the response includes a 'value' array. If there are more items, an '@odata.nextLink' property is included with a URL to the next page. Always check for nextLink and follow it until null. Use $top and $skip for manual pagination. For example, $top=50 returns up to 50 items.
Handle Pagination and Errors
Parse the response. If '@odata.nextLink' exists, make a GET request to that URL (it includes the skip token). Continue until no nextLink. Handle errors: check HTTP status. For 401, refresh token. For 403, check permissions. For 429 (throttling), read Retry-After header and wait that many seconds before retrying. Implement exponential backoff: wait 5s, 10s, 20s... up to a maximum. Log errors for debugging.
Enterprise Scenario 1: User Profile Synchronization
A large enterprise with 50,000 employees uses multiple HR systems. They need to synchronize user profile data (name, email, department) from Azure AD to an on-premises HR database nightly. Using Microsoft Graph API with application permissions (User.Read.All), a background service runs every night. It uses delta queries to fetch only users changed since last sync: first call gets all users and a delta link; subsequent calls use the delta link. This reduces data transfer from 50,000 to a few hundred records daily. The service handles pagination (default 100 per page) and throttling (10,000 requests per 10 minutes). Misconfiguration: if the app lacks User.Read.All, it gets 403. If delta link is not used, full sync takes hours and hits throttling limits. Production considerations: use multi-threading with a retry policy, store delta link in a database, and monitor for failures using Application Insights.
Enterprise Scenario 2: Email Automation for Customer Support
A SaaS company needs to send automated email notifications from a shared mailbox (e.g., support@company.com) using Microsoft Graph Mail.Send permission. The app uses delegated permissions with a service account (user context) to send emails on behalf of that mailbox. The app obtains tokens using the authorization code flow with a stored refresh token. Common issues: the service account must have a valid license; the app must have Mail.Send delegated permission granted by admin; the user (service account) must consent. If the refresh token expires (after 90 days of inactivity), the app must re-authenticate. Production: store refresh tokens securely (e.g., Key Vault), implement token refresh logic, and handle consent revocation.
Enterprise Scenario 3: Teams Bot for Meeting Scheduling
A company develops a Teams bot that schedules meetings using Microsoft Graph Calendar API. The bot uses delegated permissions (Calendars.ReadWrite, OnlineMeetings.ReadWrite) on behalf of the signed-in user. The bot uses MSAL.js for implicit flow (single-page app) or authorization code flow (server-side). The bot must handle user consent: if a user hasn't consented, the bot redirects to Azure AD consent page. Common pitfalls: using application permissions instead of delegated (would require admin consent and cannot act as user); not handling pagination when listing events; ignoring timezone differences (Graph returns datetime in UTC). Production: implement a consent cache, use delta queries to sync calendar changes, and handle throttling (e.g., 10,000 requests per 10 minutes per app per tenant).
Exactly What AZ-204 Tests on This Topic
AZ-204 Objective 3.1: 'Integrate with Microsoft Graph API.' The exam tests:
Understanding of OAuth 2.0 flows: authorization code, client credentials, implicit (legacy), on-behalf-of.
Permission scopes: delegated vs. application, and which scopes are needed for common operations (e.g., User.Read, Mail.Send, Files.Read.All).
Token acquisition using MSAL libraries (MSAL.NET, MSAL.js, MSAL Python).
Making REST calls: GET, POST, PATCH, DELETE to Graph endpoints.
Pagination: following @odata.nextLink, using $top and $skip.
Batching: constructing batch requests to $batch endpoint.
Delta queries: using deltaLink for incremental changes.
Subscriptions/webhooks: creating subscriptions, validation handshake, handling notifications.
Error handling: interpreting 401, 403, 429, and appropriate responses.
Common Wrong Answers and Why Candidates Choose Them
Using implicit flow for server-side apps: The exam may present a scenario where a server-side daemon needs to access Graph. Many candidates choose implicit flow because it's simpler, but implicit flow is for single-page apps and does not support client secrets. The correct flow is client credentials.
Choosing User.Read when User.Read.All is needed: For reading all users in the organization, User.Read only reads the signed-in user's profile. Candidates often overlook the scope difference. The exam will present a scenario requiring reading all users; the correct permission is User.Read.All (delegated or application).
Not including admin consent: For application permissions, admin consent is mandatory. Candidates may assume user consent is sufficient. The exam will test that admin consent must be granted via Azure portal or API.
Ignoring pagination: A question may ask how to retrieve all users. Candidates might answer 'use GET /users' without handling pagination. The correct answer involves following @odata.nextLink or using $top/$skip.
Using wrong token endpoint: The exam might include endpoints like https://login.windows.net or https://login.microsoftonline.com/common/oauth2/token (v1.0). The correct v2.0 endpoint is https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token.
Specific Values and Terms That Appear Verbatim
Scope: https://graph.microsoft.com/.default for app-only.
Page size: Default 100, max $top 999.
Batch limit: 20 requests per batch.
Notification validation timeout: 10 seconds.
Throttling limit: 10,000 requests per 10 minutes per app per tenant (for users resource).
Delta token: @odata.deltaLink.
Next link: @odata.nextLink.
Subscription expiration: Maximum 4230 minutes (approx 3 days) for most resources; can be renewed.
Edge Cases the Exam Loves to Test
Multi-tenant apps: Using common endpoint for tenant-agnostic tokens.
On-behalf-of flow: When a middle-tier API needs to call Graph on behalf of the user.
Consent revocation: What happens if admin revokes consent? Token becomes invalid.
Refresh token expiry: Refresh tokens can expire after 90 days of inactivity or if password change occurs.
Cross-origin requests (CORS): Graph does not support CORS for all endpoints; some require server-side proxy.
How to Eliminate Wrong Answers Using the Underlying Mechanism
If the question mentions a background service without user interaction, eliminate any answer that involves user consent or delegated permissions. The correct answer will involve client credentials flow and application permissions.
If the question involves reading all users in the organization, eliminate any answer that uses User.Read (that scope only reads the signed-in user).
If the question involves real-time notifications, look for answers that include creating a subscription with a notification URL and validation token.
If the question involves large data sets, look for answers that mention pagination or delta queries.
Microsoft Graph API base URL is https://graph.microsoft.com/v1.0 (stable) or /beta (preview).
All requests require an OAuth 2.0 access token from Azure AD endpoint: https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token.
Default page size for most collections is 100 items; use $top (max 999) and @odata.nextLink for pagination.
Batch requests are limited to 20 individual requests per call.
Delta queries use @odata.deltaLink to get incremental changes; first call returns full state.
Delegated permissions require user context; application permissions require admin consent.
Throttling limit for many resources is 10,000 requests per 10 minutes per app per tenant.
Webhook subscriptions require a validation handshake within 10 seconds.
Use MSAL libraries (MSAL.NET, MSAL.js) for token acquisition instead of raw HTTP.
Always handle 429 (Too Many Requests) with Retry-After header and exponential backoff.
These come up on the exam all the time. Here's how to tell them apart.
Delegated Permissions
Requires a signed-in user (user context).
Permissions are scoped to what the user can do.
Consent can be user or admin (for high-privilege).
Used in interactive apps (web, mobile, desktop).
Token includes user identity (sub claim).
Application Permissions
No signed-in user; app runs as background service.
Permissions are scoped to the app's identity.
Always requires admin consent.
Used for daemons, services, automated tasks.
Token does not include user identity.
Mistake
Microsoft Graph API can be called without authentication for read-only data.
Correct
All requests to Microsoft Graph require authentication via OAuth 2.0. There is no anonymous access. Even public data like user profiles requires a valid access token with appropriate permissions.
Mistake
Implicit flow is the best choice for server-side applications.
Correct
Implicit flow is designed for single-page applications (SPAs) and cannot securely store client secrets. Server-side applications should use authorization code flow (with PKCE) or client credentials flow (for daemons).
Mistake
Delegated permissions always require user consent, never admin consent.
Correct
Some delegated permissions (e.g., User.Read.All, Mail.Send) are high-privilege and require admin consent. Admin consent can be granted for all users in the tenant. User consent is only for low-privilege scopes like User.Read.
Mistake
The $batch endpoint can handle unlimited requests in a single call.
Correct
The batch endpoint accepts a maximum of 20 individual requests per batch. Requests are executed sequentially by default (dependent requests can be specified using dependsOn). Exceeding 20 results in a 400 Bad Request.
Mistake
Delta queries return all changes since the beginning of time on first call.
Correct
The first delta query returns the entire current state of the resource (e.g., all users) along with a deltaLink. Subsequent calls using that deltaLink return only changes (additions, updates, deletions) since the previous call. It does not return historical changes.
Reveal each answer, then mark whether you got it right. Score 60%+ to unlock the next chapter.
Send a POST request to https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token with Content-Type application/x-www-form-urlencoded and body: grant_type=client_credentials&client_id={appId}&client_secret={secret}&scope=https://graph.microsoft.com/.default. The response contains an access_token. Use this token in the Authorization header of Graph API calls.
Delegated permissions allow the app to act on behalf of a signed-in user. The app can only do what the user can do. Application permissions allow the app to run without a user, with its own identity. Application permissions always require admin consent and are used for background services.
When you receive a response from a collection endpoint, check for the @odata.nextLink property. If present, make a GET request to that URL to get the next page. Continue until nextLink is null. You can also use $top (max 999) and $skip to manually paginate, but nextLink is the recommended approach.
Yes, using the $batch endpoint. Send a POST to https://graph.microsoft.com/v1.0/$batch with a JSON body containing a 'requests' array of up to 20 request objects. Each object has id, method, url, and optional headers/body. Responses are returned in the same order.
Create a subscription using POST /subscriptions. You provide a notificationUrl (your endpoint) and resource (e.g., /users). Microsoft Graph will send a validation POST to your URL with a validationToken query parameter; your endpoint must respond with that token in the body within 10 seconds (HTTP 200). After validation, you receive notifications when changes occur.
Check the Retry-After header in the response and wait that many seconds before retrying. If no Retry-After header, use exponential backoff (e.g., start with 5 seconds, then 10, 20, up to 60 seconds). Also consider batching requests or using delta queries to reduce the number of calls.
Use GET /users with appropriate permissions (User.Read.All delegated or application). Handle pagination by following @odata.nextLink. You can also use $select to limit properties, $filter for specific criteria, and $orderby for sorting. For large tenants, consider using delta queries for incremental sync.
You've just covered Microsoft Graph API Integration — now see how well it sticks with free AZ-204 practice questions. Full explanations included, no account needed.
Done with this chapter?