Home

Microsoft Graph - Don't Get Throttled!

%3CLINGO-SUB%20id%3D%22lingo-sub-328696%22%20slang%3D%22en-US%22%3EMicrosoft%20Graph%20-%20Don't%20Get%20Throttled!%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-328696%22%20slang%3D%22en-US%22%3E%3CP%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3EThe%20Microsoft%20Graph%20endpoint%20is%20normally%20highly%20performant.%20The%20infrastructure%20behind%20Microsoft%20365%20will%20allocate%20computing%20resources%20based%20on%20demand%20to%20ensure%20that%20periods%20of%20high%20traffic%20levels%20do%20not%20result%20in%20degraded%20performance.%20In%20addition%20to%20dynamic%20scaling%20of%20resources%2C%20another%20mechanism%20that%20Microsoft%20uses%20is%20throttling.%20If%20you%20make%20too%20many%20requests%20then%20you%20will%20start%20to%20get%20HTTP%20429%20(too%20many%20requests)%20response%20codes%20returned.%20Not%20all%20resources%20are%20metered%20and%20while%20this%20initially%20applied%20just%20to%20the%20Outlook%20APIs%20but%20we%20can%20assume%20that%20more%20resources%20will%20be%20metered%20over%20time.%3C%2FP%3E%0A%3CP%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3E%26nbsp%3B%3C%2FP%3E%0A%3CH1%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2016.0pt%3B%20color%3A%20%231e4e79%3B%22%20id%3D%22toc-hId-1871815227%22%20id%3D%22toc-hId-1900261696%22%3EHow%20many%20requests%20is%20%22too%20many%22%3F%3C%2FH1%3E%0A%3CP%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3ESo%20the%20obvious%20question%20is%3A%20what%20is%20the%20definition%20of%20%22too%20many%20requests%3F%22.%20The%20answer%20to%20this%20is%20that%20you%20will%20get%20throttled%20if%20you%20make%20a%20number%20of%20requests%20that%20would%20be%20detrimental%20to%20the%20service%20as%20a%20whole%2C%20which%20isn't%20actually%20that%20helpful.%20A%20figure%20that%20has%20been%20%3CA%20href%3D%22https%3A%2F%2Fdeveloper.microsoft.com%2Fen-us%2Foffice%2Fblogs%2Fthrottling-coming-to-outlook-api-and-microsoft-graph%2F%22%20target%3D%22_self%22%20rel%3D%22noopener%20noreferrer%20noopener%20noreferrer%22%3Estated%20publicly%3C%2FA%3E%20is%2010%2C000%20requests%20in%20a%2010-minute%20period.%20This%20applies%20on%20a%20per%20user%20and%20per%20application%20ID%20basis.%20So%20you%20could%20make%2010%2C000%20requests%20on%20behalf%20of%20each%20user%20or%20group%20in%20your%20tenant%20in%20a%2010-minute%20period%20before%20triggering%20the%20throttle%2C%20and%20if%20you%20exceed%20the%20threshold%20for%20a%20particular%20user%20then%20it%20is%20only%20that%20user%20that%20gets%20throttled.%20If%20you%20are%20using%20app-only%20permissions%20then%20that%20counts%20as%20a%20single%20user.%3C%2FP%3E%0A%3CP%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3EThis%20is%20all%20well%20and%20good%2C%20but%20for%20practical%20purposes%20you%20have%20to%20accept%20that%20these%20rules%20could%20change%20at%20any%20time%2C%20and%20in%20addition%20you%20could%20well%20find%20that%20a%20lower%20threshold%20kicks%20in%20if%20the%20service%20is%20becoming%20overloaded%20for%20whatever%20reason.%20You%20might%20also%20find%20you%20get%20throttled%20if%20you%20make%20a%20large%20number%20of%20requests%20across%20your%20tenant%2C%20the%20'rules'%20above%20notwithstanding.%20The%20reality%20is%20that%20unless%20you%20are%20making%20a%20small%20number%20of%20requests%2C%20you%20should%20design%20your%20application%20on%20the%20assumption%20that%20you%20could%20get%20a%20429%20response%20at%20any%20time%20and%20build%20in%20logic%20to%20deal%20gracefully%20with%20that%20situation%20when%20it%20arises.%3C%2FP%3E%0A%3CP%3E%3CCITE%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%209.0pt%3B%20color%3A%20%23595959%3B%22%3E%26nbsp%3B%3C%2FCITE%3E%3C%2FP%3E%0A%3CP%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3EIf%20your%20application%20is%20throttled%20you%20will%20get%20a%26nbsp%3B%3CSPAN%20style%3D%22font-weight%3A%20bold%3B%22%3ERetry-After%3C%2FSPAN%3E%20response%20header%20in%20addition%20to%20the%20429%20response%20code%2C%20telling%20you%20the%20number%20of%20seconds%20your%20application%20needs%20to%20wait%20before%20making%20more%20requests.%20You%20also%20get%20a%20descriptive%20Rate-Limit-Reason%20header%20(I%20hesitate%20to%20call%20it%20an%20error%20message)%20which%20you%20probably%20will%20want%20to%20log%20rather%20than%20show%20to%20a%20user.%20Obviously%20you%20will%20want%20your%20app%20to%20wait%20for%20the%20Retry-After%20period%20before%20making%20another%20request%2C%20otherwise%20you%20will%20just%20get%20more%20429s%20and%20probably%20extend%20the%20throttling%20period%20even%20further.%3C%2FP%3E%0A%3CP%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3E%26nbsp%3B%3C%2FP%3E%0A%3CH1%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2016.0pt%3B%20color%3A%20%231e4e79%3B%22%20id%3D%22toc-hId--680341734%22%20id%3D%22toc-hId--651895265%22%3EAvoiding%20Throttling%3C%2FH1%3E%0A%3CP%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3EClearly%20it%20is%20preferable%20not%20to%20be%20throttled%20in%20order%20to%20keep%20your%20application%20performant.%20There%20are%20a%20number%20of%20strategies%20you%20can%20use%3A%3C%2FP%3E%0A%3CP%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3E%26nbsp%3B%3C%2FP%3E%0A%3CUL%20style%3D%22margin-left%3A%20.375in%3B%20direction%3A%20ltr%3B%20unicode-bidi%3A%20embed%3B%20margin-top%3A%200in%3B%20margin-bottom%3A%200in%3B%22%20type%3D%22disc%22%3E%0A%3CLI%20style%3D%22margin-top%3A%200%3B%20margin-bottom%3A%200%3B%20vertical-align%3A%20middle%3B%22%3E%3CSPAN%20style%3D%22font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3EDon't%20do%20automatic%20retries%20as%20these%20will%20accumulate%20requests%20against%20any%20throttling%20limit.%3C%2FSPAN%3E%3C%2FLI%3E%0A%3CLI%20style%3D%22margin-top%3A%200%3B%20margin-bottom%3A%200%3B%20vertical-align%3A%20middle%3B%22%3E%3CSPAN%20style%3D%22font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3ERetrieve%20multiple%20items%20in%20one%20request%2C%20e.g.%20get%20a%20page%20of%20list%20items%20rather%20than%20retrieving%20individually%2C%20and%20use%20%24select%20to%20avoid%20getting%20properties%20you%20don't%20need%20to%20keep%20the%20payload%20down.%3C%2FSPAN%3E%3C%2FLI%3E%0A%3CLI%20style%3D%22margin-top%3A%200%3B%20margin-bottom%3A%200%3B%20vertical-align%3A%20middle%3B%22%3E%3CSPAN%20style%3D%22font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3EIncrease%20the%20value%20of%20%24top%20to%20get%20larger%20pages%20(default%20is%2010%20but%20maximum%20is%201000)%3C%2FSPAN%3E%3C%2FLI%3E%0A%3CLI%20style%3D%22margin-top%3A%200%3B%20margin-bottom%3A%200%3B%20vertical-align%3A%20middle%3B%22%3E%3CSPAN%20style%3D%22font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3ECache%20data%20locally%20where%20needed%20to%20avoid%20re-fetching%20data%2C%20while%20being%20careful%20not%20to%20breach%20data%20privacy%2C%20retention%20policies%20and%20terms%20of%20use%20of%20the%20Microsoft%20Graph%3C%2FSPAN%3E%3C%2FLI%3E%0A%3CLI%20style%3D%22margin-top%3A%200%3B%20margin-bottom%3A%200%3B%20vertical-align%3A%20middle%3B%22%3E%3CSPAN%20style%3D%22font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3EUse%20webhook%20notifications%20rather%20than%20polling%20requests%20to%20detect%20changes%20(you%20can%20also%20use%20delta%20queries%20to%20reduce%20the%20response%20payload).%3C%2FSPAN%3E%3C%2FLI%3E%0A%3CLI%20style%3D%22margin-top%3A%200%3B%20margin-bottom%3A%200%3B%20vertical-align%3A%20middle%3B%22%3E%3CSPAN%20style%3D%22font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3EUse%20JSON%20batching%20to%20combine%20multiple%20requests%20into%20a%20single%20batch%20request.%3C%2FSPAN%3E%3C%2FLI%3E%0A%3C%2FUL%3E%0A%3CP%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3EYou%20should%20think%20of%20the%20Retry-After%20period%20as%20a%20recommendation.%20If%20you%20re-try%20the%20request%20after%20the%20Retry-After%20time%20has%20elapsed%20it%20is%20still%20possible%20that%20you%20will%20get%20a%20429%20response.%20This%20means%20that%20throttling%20is%20still%20in%20effect.%20In%20this%20case%20you%20should%20again%20wait%20for%20the%20(possibly%20updated)%20Retry-After%20period%20and%20try%20again%2C%20repeating%20this%20process%20until%20the%20request%20succeeds.%3C%2FP%3E%0A%3CP%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3EIt%20is%20worth%20noting%20that%20not%20all%20of%20the%20Microsoft%20Graph%20APIs%20provide%20a%20Retry-After%20header.%20It%20is%20possible%20for%20an%20API%20to%20have%20throttling%20implemented%20but%20not%20yet%20support%20the%20Retry-After%20header.%20In%20this%20case%20a%20strategy%20would%20be%20to%20have%20an%20initial%20wait%20period%20and%20keep%20increasing%20it%20exponentially%20with%20each%20429%20response.%3C%2FP%3E%0A%3CP%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3EMore%20guidance%20is%20provided%20in%20the%20Microsoft%20Graph%20%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fgraph%2Fthrottling%22%20target%3D%22_self%22%20rel%3D%22noopener%20noreferrer%20noopener%20noreferrer%22%3Edocumentation%3C%2FA%3E.%3C%2FP%3E%0A%3CP%20style%3D%22margin%3A%200in%3B%20margin-left%3A%20.375in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3E%26nbsp%3B%3C%2FP%3E%0A%3CH1%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2016.0pt%3B%20color%3A%20%231e4e79%3B%22%20id%3D%22toc-hId-1062468601%22%20id%3D%22toc-hId-1090915070%22%3EMicrosoft%20Graph%20Data%20Connect%3C%2FH1%3E%0A%3CP%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3EThe%20Microsoft%20Graph%20API%20is%20designed%20for%20transactional%20access%20to%20Microsoft%20365%20data%20on%20a%20user-by-user%20basis.%20Some%20applications%20really%20require%20bulk%20access%20to%20data%2C%20for%20example%20aggregation%20of%20data%20across%20a%20tenant%2C%20fraud%20detection%2C%20analysing%20resource%20utilization%20and%20efficiency%20or%20building%20organizational%20directories%2C%20to%20name%20a%20few%20use%20cases.%20Typically%20these%20types%20of%20processing%20occur%20on%20a%20scheduled%20basis%2C%20for%20example%20every%20night.%3C%2FP%3E%0A%3CP%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3EIn%20this%20case%20the%20Microsoft%20Graph%20API%20endpoint%20is%20a%20very%20inefficient%20way%20of%20doing%20this%20and%20the%20application%20is%20likely%20to%20encounter%20throttling%20limits.%20A%20better%20solution%20in%20this%20case%20is%20to%20use%20a%20new%20feature%20(currently%20in%20preview%20at%20the%20time%20of%20writing)%20called%20Microsoft%20Graph%20Data%20Connect.%20It%20allows%20you%20to%20get%20at%20the%20same%20data%20that's%20available%20through%20Microsoft%20Graph%20APIs%20(currently%20only%20a%20limited%20subset%20is%20available)%2C%20but%20in%20a%20scalable%20way.%20It%20also%20allows%20you%20to%20build%20sophisticated%20analysis%20pipelines%20using%20tools%20like%20Azure%20Data%20Factory.%20All%20this%20processing%20occurs%20within%20the%20Azure%20data%20centre%20for%20better%20data%20protection.%3C%2FP%3E%0A%3CP%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3EFor%20more%20information%20see%20the%20Microsoft%20Graph%20Data%20Connect%20%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Fgraph%2Fdata-connect-concept-overview%22%20target%3D%22_self%22%20rel%3D%22noopener%20noreferrer%20noopener%20noreferrer%22%3Edocumentation%3C%2FA%3E.%3C%2FP%3E%0A%3CP%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3EMore%20articles%20on%20my%20%3CA%20href%3D%22https%3A%2F%2Fwww.spdoctor.com%2F%22%20target%3D%22_self%22%20rel%3D%22nofollow%20noopener%20noreferrer%20noopener%20noreferrer%22%3Eblog%3C%2FA%3E.%3C%2FP%3E%0A%3CP%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3E%26nbsp%3B%3C%2FP%3E%0A%3CP%20style%3D%22margin%3A%200in%3B%20font-family%3A%20Calibri%3B%20font-size%3A%2011.0pt%3B%22%3E%26nbsp%3B%3C%2FP%3E%3C%2FLINGO-BODY%3E
Bill Ayers
MVP

The Microsoft Graph endpoint is normally highly performant. The infrastructure behind Microsoft 365 will allocate computing resources based on demand to ensure that periods of high traffic levels do not result in degraded performance. In addition to dynamic scaling of resources, another mechanism that Microsoft uses is throttling. If you make too many requests then you will start to get HTTP 429 (too many requests) response codes returned. Not all resources are metered and while this initially applied just to the Outlook APIs but we can assume that more resources will be metered over time.

 

How many requests is "too many"?

 

So the obvious question is: what is the definition of "too many requests?". The answer to this is that you will get throttled if you make a number of requests that would be detrimental to the service as a whole, which isn't actually that helpful. A figure that has been stated publicly is 10,000 requests in a 10-minute period. This applies on a per user and per application ID basis. So you could make 10,000 requests on behalf of each user or group in your tenant in a 10-minute period before triggering the throttle, and if you exceed the threshold for a particular user then it is only that user that gets throttled. If you are using app-only permissions then that counts as a single user.

 

This is all well and good, but for practical purposes you have to accept that these rules could change at any time, and in addition you could well find that a lower threshold kicks in if the service is becoming overloaded for whatever reason. You might also find you get throttled if you make a large number of requests across your tenant, the 'rules' above notwithstanding. The reality is that unless you are making a small number of requests, you should design your application on the assumption that you could get a 429 response at any time and build in logic to deal gracefully with that situation when it arises.

 

If your application is throttled you will get a Retry-After response header in addition to the 429 response code, telling you the number of seconds your application needs to wait before making more requests. You also get a descriptive Rate-Limit-Reason header (I hesitate to call it an error message) which you probably will want to log rather than show to a user. Obviously you will want your app to wait for the Retry-After period before making another request, otherwise you will just get more 429s and probably extend the throttling period even further.

 

Avoiding Throttling

 

Clearly it is preferable not to be throttled in order to keep your application performant. There are a number of strategies you can use:

 

  • Don't do automatic retries as these will accumulate requests against any throttling limit.
  • Retrieve multiple items in one request, e.g. get a page of list items rather than retrieving individually, and use $select to avoid getting properties you don't need to keep the payload down.
  • Increase the value of $top to get larger pages (default is 10 but maximum is 1000)
  • Cache data locally where needed to avoid re-fetching data, while being careful not to breach data privacy, retention policies and terms of use of the Microsoft Graph
  • Use webhook notifications rather than polling requests to detect changes (you can also use delta queries to reduce the response payload).
  • Use JSON batching to combine multiple requests into a single batch request.

 

You should think of the Retry-After period as a recommendation. If you re-try the request after the Retry-After time has elapsed it is still possible that you will get a 429 response. This means that throttling is still in effect. In this case you should again wait for the (possibly updated) Retry-After period and try again, repeating this process until the request succeeds.

 

It is worth noting that not all of the Microsoft Graph APIs provide a Retry-After header. It is possible for an API to have throttling implemented but not yet support the Retry-After header. In this case a strategy would be to have an initial wait period and keep increasing it exponentially with each 429 response.

 

More guidance is provided in the Microsoft Graph documentation.

 

Microsoft Graph Data Connect

 

The Microsoft Graph API is designed for transactional access to Microsoft 365 data on a user-by-user basis. Some applications really require bulk access to data, for example aggregation of data across a tenant, fraud detection, analysing resource utilization and efficiency or building organizational directories, to name a few use cases. Typically these types of processing occur on a scheduled basis, for example every night.

 

In this case the Microsoft Graph API endpoint is a very inefficient way of doing this and the application is likely to encounter throttling limits. A better solution in this case is to use a new feature (currently in preview at the time of writing) called Microsoft Graph Data Connect. It allows you to get at the same data that's available through Microsoft Graph APIs (currently only a limited subset is available), but in a scalable way. It also allows you to build sophisticated analysis pipelines using tools like Azure Data Factory. All this processing occurs within the Azure data centre for better data protection.

 

For more information see the Microsoft Graph Data Connect documentation.

 

More articles on my blog.