SharePoint Online Avoid Throttling Example CodePost Date: January 31, 2023Overview A few example code snippets of avoiding throttling in SharePoint Online Example of decorating traffic with User agent when using Client Side Object Model (CSOM) // Get access to source site using (var ctx = new ClientContext("https://contoso.sharepoint.com/sites/team")) { //Provide account and pwd for connecting to SharePoint Online var passWord = new SecureString(); foreach (char c in pwd.ToCharArray()) passWord.AppendChar(c); ctx.Credentials = new SharePointOnlineCredentials("contoso@contoso.onmicrosoft.com", passWord); // Add our User Agent information ctx.ExecutingWebRequest += delegate (object sender, WebRequestEventArgs e) { e.WebRequestExecutor.WebRequest.UserAgent = "NONISV|Contoso|GovernanceCheck/1.0"; }; // Normal CSOM Call with custom User-Agent information Web site = ctx.Web; ctx.Load(site); ctx.ExecuteQuery(); } Following sample is in C# format, but the similar User Agent information is recommended to be used even for the JavaScript libraries used in the SharePoint Online pages. Example of decorating traffic with User agent when using REST APIs HttpWebRequest endpointRequest = (HttpWebRequest) HttpWebRequest.Create(sharepointUrl.ToString() + "/_api/web/lists"); endpointRequest.Method = "GET"; endpointRequest.UserAgent = "NONISV|Contoso|GovernanceCheck/1.0"; endpointRequest.Accept = "application/json;odata=nometadata"; endpointRequest.Headers.Add("Authorization", "Bearer " + accessToken); HttpWebResponse endpointResponse = (HttpWebResponse)endpointRequest.GetResponse(); CSOM Code sample: ExecuteQueryWithIncrementalRetry extension method You’ll need to use SharePoint Online CSOM version 16.1.8316.1200 (December 2018 version) or higher. Add this extension method in a static class and use ExecuteQueryWithIncrementalRetry instead of ExecuteQuery to make your code handle throttling requests. public static void ExecuteQueryWithIncrementalRetry(this ClientContext clientContext, int retryCount, int delay) { int retryAttempts = 0; int backoffInterval = delay; int retryAfterInterval = 0; bool retry = false; ClientRequestWrapper wrapper = null; if (retryCount <= 0) throw new ArgumentException("Provide a retry count greater than zero."); if (delay <= 0) throw new ArgumentException("Provide a delay greater than zero."); // Do while retry attempt is less than retry count while (retryAttempts < retryCount) { try { if (!retry) { clientContext.ExecuteQuery(); return; } else { //increment the retry count retryAttempts++; // retry the previous request using wrapper if (wrapper != null && wrapper.Value != null) { clientContext.RetryQuery(wrapper.Value); return; } // retry the previous request as normal else { clientContext.ExecuteQuery(); return; } } } catch (WebException ex) { var response = ex.Response as HttpWebResponse; // Check if request was throttled - http status code 429 // Check is request failed due to server unavailable - http status code 503 if (response != null && (response.StatusCode == (HttpStatusCode)429 || response.StatusCode == (HttpStatusCode)503)) { wrapper = (ClientRequestWrapper)ex.Data["ClientRequest"]; retry = true; // Determine the retry after value - use the `Retry-After` header when available string retryAfterHeader = response.GetResponseHeader("Retry-After"); if (!string.IsNullOrEmpty(retryAfterHeader)) { if (!Int32.TryParse(retryAfterHeader, out retryAfterInterval)) { retryAfterInterval = backoffInterval; } } else { retryAfterInterval = backoffInterval; } // Delay for the requested seconds Thread.Sleep(retryAfterInterval * 1000); // Increase counters backoffInterval = backoffInterval * 2; } else { throw; } } } throw new MaximumRetryAttemptedException($"Maximum retry attempts {retryCount}, has be attempted."); } [Serializable] public class MaximumRetryAttemptedException : Exception { public MaximumRetryAttemptedException(string message) : base(message) { } }