SharePoint / SharePoint/sp-dev-docs
RateLimit headers are not returned by SharePoint online
@Ashlesha-MSFT is already working on this.
Since Mar 24, 2025.
- Dominant language
- PowerShell
- Stars
- 1.4k
- Forks
- 1.1k
- Avg merge
- 4d 12h
- Merged PRs (30d)
- 12
Description
Target SharePoint environment
SharePoint Online
What SharePoint development model, framework, SDK or API is this about?
SharePoint CSOM
Developer environment
Windows
What browser(s) / client(s) have you tested
- 💥 Internet Explorer
- 💥 Microsoft Edge
- 💥 Google Chrome
- 💥 FireFox
- 💥 Safari
- mobile (iOS/iPadOS)
- mobile (Android)
- not applicable
- other (enter in the "Additional environment details" area below)
Additional environment details
- Microsoft.SharePointOnline.CSOM 16.1.25110.12000
- .NET framework 4.8
Describe the bug / error
We have an RateLimit implementation based on this example on GitHub: https://github.com/OneDrive/samples/blob/master/scenarios/throttling-ratelimit-handling/readme.md
In a development environment we get RateLimit headers returned from SharePoint, but with ClientIDs from a few customers we almost never get RateLimit headers returned.
Instead the customer app is throttled with 429 Too Many Requests errors with retry times of sometimes even 200+ seconds.
In Fiddler the network traffic is clearly visible that there are no RateLimit headers.
In the production environment there are around 4 processes which run with the same clientid which do background processes (Windows agents), and 4 processes (websites) which serve user requests. The websites processes run under a different clientid, but this does not make a difference. All processes are affected with many 429 errors.
In which case does SharePoint online give not first RateLimit headers, but returns 429 errors?
Steps to reproduce
- Use the example on GitHub https://github.com/OneDrive/samples/blob/master/scenarios/throttling-ratelimit-handling/readme.md
- Change the function SharePointCSOMCallAsync
private async Task SharePointCSOMCallAsync(Microsoft.SharePoint.Client.ClientContext clientContext)
{
var site = clientContext.Site;
var rootWeb = site.RootWeb;
var contentTypes = rootWeb.ContentTypes;
var currentUser = rootWeb.CurrentUser;
var fields = rootWeb.Fields;
clientContext.Load(site, s => s.Url, s => s.ServerRelativeUrl);
clientContext.Load(rootWeb, r => r.Id, r => r.Description, r => r.Title, r => r.Url);
clientContext.Load(rootWeb, r => r.Language, r => r.ServerRelativeUrl);
clientContext.Load(currentUser, u => u.IsSiteAdmin, u => u.LoginName);
clientContext.Load(contentTypes, c => c.Include(ctt => ctt.Fields.Include(f => f.InternalName), cct => cct.FieldLinks.Include(fl => fl.Name), ctt => ctt.Name, ctt => ctt.Group));
clientContext.Load(fields, fi => fi.Include(f => f.InternalName, f => f.Id, f => f.Group, f => f.Hidden, f => f.ReadOnlyField, f => f.Title, f => f.CanBeDeleted));
clientContext.Load(rootWeb, r => r.RoleDefinitions.Where(rd => !(rd.RoleTypeKind == RoleType.Guest || rd.RoleTypeKind == RoleType.RestrictedGuest)).Include(rd => rd.Id, rd => rd.Name, rd => rd.BasePermissions, rd => rd.RoleTypeKind));
clientContext.Load(rootWeb, r => r.RoleAssignments.Include(ra => ra.Member.LoginName, ra => ra.Member.PrincipalType, ra => ra.Member.Id, ra => ra.RoleDefinitionBindings.Include(rdb => rdb.Id, rdb => rdb.RoleTypeKind, rdb => rdb.Name)));
var siteUsers = site.RootWeb.SiteUsers;
site.RootWeb.Context.Load(siteUsers, us => us.Include(u => u.Title, u => u.LoginName, u => u.PrincipalType, u => u.Email, u => u.Id));
loadDefaultTitleDescriptionResource(clientContext.Site.RootWeb);
await clientContext.ExecuteQueryAsync();
Console.WriteLine(site.RootWeb.Title);
IncrementCallsDone();
}
private static void loadDefaultTitleDescriptionResource(Microsoft.SharePoint.Client.Web web)
{
var cultureNameEN = "en-US";
var cultureNameNL = "nl-NL";
_ = web.TitleResource.GetValueForUICulture(cultureNameEN);
_ = web.TitleResource.GetValueForUICulture(cultureNameNL);
_ = web.DescriptionResource.GetValueForUICulture(cultureNameEN);
_ = web.DescriptionResource.GetValueForUICulture(cultureNameNL);
}
- Comment method 'SharePointRestCallAsync' and 'GraphRestCallAsync', so only method 'SharePointCSOMCallAsync' is called.
// Test a SharePoint Rest call
// await SharePointRestCallAsync(client, sharePointAccessToken);
// Test a Graph Rest call
//await GraphRestCallAsync(client, graphAccessToken);
// Test a CSOM call
await SharePointCSOMCallAsync(clientContext);
- Configure the app (for instance with 5 threads)
- Start Fiddler (or any other app which can show network traffic)
- Run the app
Expected behavior
In the development environment we get RateLimit headers.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.