Browse all topics

Microsoft Graph API basics

By Emil Björk · Microsoft ecosystem consultant, Gothenburg

An introduction to the Microsoft Graph API — what it is, how to authenticate, the day-to-day query patterns, and the gotchas that bite every new integration.

The Microsoft Graph API is the single REST endpoint for accessing almost everything in Microsoft 365 programmatically. Users, groups, mail, calendars, files, Teams chats, Planner tasks, Intune devices, security alerts, audit logs — all of it lives behind https://graph.microsoft.com/v1.0/ and /beta/. If you're automating anything in Microsoft 365 in 2026, Graph is the front door; the older workload APIs (Exchange Web Services, the Azure AD Graph API) are either retired or in managed decline.

The shape of the API

Every Graph request looks roughly like:

GET https://graph.microsoft.com/v1.0/users/{id}/messages?$top=10
Authorization: Bearer {access-token}

Common patterns:

  • Hierarchical URLs/users/{id}/mail/folders/{folderId}/messages mirrors the conceptual hierarchy of the data.
  • OData query options$select, $filter, $expand, $top, $skip, $orderby for narrowing and shaping responses.
  • $select is especially important — by default Graph returns a default property set; specify exactly the fields you need. It's also a performance lever: unselected properties don't have to be computed server-side.
  • @odata.nextLink paginates long result sets. Follow it verbatim — don't try to construct page URLs yourself, and don't assume a page size; Graph can return fewer items than $top asked for.
  • Delta queries/delta endpoints let you sync changes incrementally rather than re-pulling everything. Store the @odata.deltaLink and call it on the next run; you get only what changed. Any sync job that re-enumerates all users or all messages on every run is doing it wrong.

Two patterns that new Graph developers consistently miss:

  • Advanced directory queries — filtering on many directory properties requires the header ConsistencyLevel: eventual plus $count=true in the query. Without it, filters like endsWith on a mail attribute fail with unhelpful errors. This trips up nearly everyone once.
  • JSON batchingPOST /$batch bundles up to 20 requests into one HTTP call. For dashboards and provisioning scripts that make many small reads, batching is the difference between seconds and minutes.

Authentication

Graph requires an OAuth 2.0 access token from Microsoft Entra ID, with one of two permission types:

  • Delegated permissions — the app acts on behalf of a signed-in user. The token reflects the intersection of what the app is allowed and what the user can do.
  • Application permissions — the app acts as itself, no signed-in user. Used for daemons, scheduled jobs, and backend services. Always requires admin consent.

Each permission scope (e.g., Mail.Read, User.ReadWrite.All, Sites.FullControl.All) is documented per endpoint. Always request the least-privileged scope that satisfies the use case — the reference docs list permissions from least to most privileged for a reason.

Practical guidance on credentials: for anything running in Azure, use a managed identity and skip client secrets entirely. If the workload runs elsewhere, prefer certificate credentials or workload identity federation over client secrets, and put an expiry-monitoring process around whatever you choose. Expired secrets on forgotten automation accounts are one of the most common self-inflicted outages in Microsoft 365 — and over-broad application permissions on stale app registrations are one of the most common audit findings.

For SharePoint and Exchange application permissions specifically, look at resource-specific consent (Sites.Selected, and Exchange's application access policies) — it scopes an app to named sites or mailboxes instead of the whole tenant. Granting tenant-wide Sites.FullControl.All to an app that touches one site is a habit worth breaking.

The SDKs

Microsoft publishes SDKs for .NET, JavaScript / TypeScript, Java, Python, PHP, Go, and PowerShell. They handle token acquisition, retry, pagination, and typed responses. For admins, the Microsoft Graph PowerShell SDK (Connect-MgGraph) is the successor to the retired MSOnline and AzureAD modules, so scripts you write against it are the ones with a future.

For ad-hoc exploration, Graph Explorer at developer.microsoft.com/graph/graph-explorer is the best learning environment — sign in, run requests, see permission requirements, copy code snippets in your language of choice. It's also the fastest way to answer "does this endpoint return what I think it returns" before writing any code.

Change notifications

Polling is not the only option. Graph change notifications (webhooks) push events — new messages, group membership changes, file updates — to an endpoint you host, with subscriptions you renew on a schedule. Combined with delta queries for catch-up after downtime, this is the correct architecture for anything near-real-time. Just budget for the renewal plumbing: subscriptions are deliberately short-lived.

Common gotchas

  • Throttling: Graph rate-limits aggressively, per app and per tenant, with different budgets per workload. Implement exponential backoff and respect the Retry-After header — the SDKs do this for you, which is a good reason to use them. Sequential, batched, resumable jobs beat massively parallel ones.
  • /beta is unstable — features there can change or vanish without notice. Explore in beta, ship on /v1.0.
  • Licensing isn't checked by the API alone — some endpoints (Teams export, certain reports) have metered billing or licensing preconditions that surface as confusing errors.
  • Consent is a governance surface — audit your tenant's app permissions at entra.microsoft.com → Enterprise applications regularly. Over-permissioned apps are a standing attack surface, and consent phishing targets exactly this.

For any non-trivial Microsoft 365 integration — provisioning users, exporting data, automating tasks, building Copilot agents — Microsoft Graph is the right starting point, and the habits above (least privilege, $select, delta, backoff) are the difference between an integration that scales and one that gets throttled into uselessness.

Further reading

Spot something wrong or want a topic covered? Send it through the contact form.