Throttling¶
Microsoft Graph and SharePoint both throttle with HTTP 429 and a
Retry-After header; Graph also reports how close an app is to its limit. The
library reads these signals, retries with the server's delay (falling back to
exponential backoff with jitter) and can pace a whole fleet proactively.
Unlike SharePoint's static service limits, Graph throttling is
dynamic — evaluated per scope (per app, per tenant, per app + tenant, per
resource) and per request type — so the library reacts to the signals rather
than enforcing a fixed budget. The quotas below are declared on the Graph
resource classes with @limit(...) (reference-only) and shown in the Bound
at column.
Signals¶
| Header | Direction | Meaning |
|---|---|---|
Retry-After |
response | Seconds to wait before retrying a 429/503 |
x-ms-throttle-limit-percentage |
response | Share of the limit consumed (0.8–1.8); >= 1.0 means throttling |
x-ms-resource-unit |
response | The cost of this request (Identity resource units) |
x-ms-throttle-scope |
response | The throttled scope (<Scope>/<Limit>/<AppId>/<TenantId...>) |
x-ms-throttle-information |
response | Why (CPULimitExceeded, WriteLimitExceeded, ...) |
x-ms-throttle-priority |
request | low / normal / high — low is throttled first |
What the library does¶
result = client.users.top(10).get().execute_query_retry() # honor Retry-After + backoff
client.execute_batch(concurrency=5) # retries throttled sub-requests
client.with_rate_limit() # pace a fleet from the signals (proactive)
client.with_throttle_priority("low") # mark background work low priority
client.with_rate_limit() reads every response (including batch sub-responses)
and gates the group on Retry-After, Graph's x-ms-throttle-limit-percentage
and SharePoint's health score. See
office365.runtime.http.throttling and office365.runtime.retry.
Best practices to avoid throttling¶
- Prefer delta queries and change notifications over polling/scanning.
- Batch related operations (JSON batching) and reduce operations per request.
- Don't retry immediately — honor
Retry-After(the fastest recovery). - For bulk extraction, use Microsoft Graph Data Connect (not throttled).
Quotas (reference)¶
The numbers below are the tested limits Graph enforces; the first one reached
triggers throttling. They're declared on the model (@limit) — see the
Bound at column.
| Service | Limit | Scope | Type | Bound at |
|---|---|---|---|---|
| call records | 15,000 requests / 20s | app | any | CallRecord |
| call records | 10,000 requests / 20s | tenant | any | CallRecord |
| call records | 1,500 requests / 20s | app+tenant | any | CallRecord |
| call records | 40 requests / 20s | resource | any | CallRecord |
| cloud communications | 50,000 requests / 15s | app+tenant | any | Call |
| cloud communications | 10,000 requests / 30s | app+tenant | any | Presence |
| cloud communications | 750 requests / 30s | app | read | VirtualEvent |
| cloud communications | 15 requests / 30s | app | write | VirtualEvent |
| excel | 5,000 requests / 10s | app | any | Workbook |
| excel | 1,500 requests / 10s | app+tenant | any | Workbook |
| identity | 150,000 resource_units / 20s | app | any | DirectoryObject, SubscribedSku, Domain |
| identity | 35,000 requests / 300s | app | write | DirectoryObject, SubscribedSku, Domain |
| identity | 18,000 requests / 300s | tenant | write | DirectoryObject, SubscribedSku, Domain |
| identity | 3,500 resource_units / 10s | app+tenant | any | DirectoryObject, SubscribedSku, Domain |
| identity | 3,000 requests / 150s | app+tenant | write | DirectoryObject, SubscribedSku, Domain |