May 27, 2026 · 8 min read
Microsoft Graph throttling in production: what the docs don't tell you
Every Microsoft Graph integration works in the demo. Ten users, a hundred messages, a polite polling loop — Graph answers instantly and everyone signs off. Then it meets a real tenant: forty thousand mailboxes, a migration weekend, another vendor's integration hammering the same limits. Suddenly a third of your requests come back 429, and the sync that took minutes takes hours.
The mistake is treating throttling as an error condition. In production Graph, 429s are the weather — you don't prevent them, you build for them.
Polling is the original sin
Most throttling pain traces back to one architectural decision: polling for changes. Fetching every mailbox on a schedule burns your request budget re-reading data that hasn't changed — which is almost all of it, almost all the time.
Graph offers two tools that eliminate most of that traffic. Delta queries return only what changed since your last sync token, turning a full-mailbox scan into a handful of requests. Change notifications invert the model entirely: Graph calls you when something changes. A subscription plus delta reconciliation typically cuts request volume by 95% or more — we've moved integrations from 'throttled nightly' to 'never throttled' on this change alone.
Backoff that actually works
When 429s do arrive, the response carries a Retry-After header. Honor it exactly — not your own exponential guess layered on top, and never a retry storm from ten workers that all failed together. Serialize retries per resource, add jitter, and cap concurrent requests per tenant well below the documented limits, because those limits are shared with every other app in the tenant.
The subtler trap is what happens upstream while you wait. If requests keep arriving during a throttle window, you need a queue with depth monitoring, not a thread pool quietly filling up. Idempotent handlers make this survivable: a message processed twice must cost nothing.
Design for the worst tenant, not the demo tenant
The integrations that survive are designed for their noisiest customer: throttle-aware from the first architecture diagram, observable enough that you see rising 429 rates before users see delays, and honest in their SLAs about what 'near real-time' means under load.
This is a large part of what we do in our Microsoft 365 & Graph work — and it's the difference between an integration that passed the demo and one that's still quietly syncing 40,000 items a day three years later.