Hello Pawan Rawal, your understanding of ARM throttling is mostly correct, but there are some important nuances that might be causing the 429 errors you're seeing — even when the X-Ms-Ratelimit-Remaining-* headers look fine.
In your case, since you're calling the usages list and quotas list APIs for the Microsoft.Compute provider across multiple subscriptions, you might be running into throttling that's not reflected in the standard subscription-level headers.
Even though the headers like X-Ms-Ratelimit-Remaining-Subscription-Reads and Global-Reads are still showing available quota, azure also applies hidden throttling at the API or resource provider level (in this case, Microsoft.Compute and Microsoft.Quota)
So, it’s possible you're hitting a per-API limit specifically for these usage/quota endpoints, especially if you're calling them in a tight loop or concurrently across many subscriptions.
These limits aren’t publicly documented and don’t show up in the standard rate limit headers — which is why the 429 response can be confusing. If you’re using a single service principal to access multiple subscriptions, azure may also throttle based on the overall request volume across the tenant or identity. That means you're potentially hitting a tenant-level ceiling, even though each individual subscription looks okay. Look out for headers like X-Ms-Ratelimit-Remaining-Tenant-Reads or X-Ms-Ratelimit-Remaining-Tenant-Global-Reads. If they’re present and low, that could explain it. But again, in many cases, these limits are enforced even if the headers don’t give you a clear signal.
Please note, Azure has been shifting toward a regional throttling model, which means even if your subscription and global limits are fine, repeated calls to quota APIs in a single region (e.g. East US) could still hit backend throttling.
So, if your requests are region-targeted (or defaulting to a specific region), consider spreading them out or adding delays between calls.
Additonally answering your follow up questions-
- “Are you referring to
X-Ms-Ratelimit-Remaining-Subscription-Reads when you talk about provider-level throttling?”
Ans- Not exactly. The headers you're seeing:
-
X-Ms-Ratelimit-Remaining-Subscription-Reads
-
X-Ms-Ratelimit-Remaining-Subscription-Global-Reads
are subscription-wide rate limit indicators — they don’t tell you anything about resource provider-level or API-specific throttling.
Unfortunately, Azure doesn’t expose separate headers for provider-level or operation-specific throttling, even though those limits are enforced behind the scenes. So yes, you're not missing any headers. This is one of the key challenges as you can be throttled at the provider/API level without visible signals in the headers.
- “Are those tenant-level headers (
X-Ms-Ratelimit-Remaining-Tenant-Reads, etc.) returned for Usages/Quotas APIs?”
Ans- In practice, no, not always and not for the Usages/Quotas List APIs, based on what we’ve observed too. These headers are:
- Inconsistently returned across different ARM APIs
- Sometimes only present in responses from high-volume or management-plane APIs (like deployments, policy, or RBAC)
So you're correct for the Usages List and Quotas List APIs, those tenant-level headers are typically not included.
They’re mentioned in some Microsoft docs and GitHub discussions but not universally implemented and there’s no official public documentation that defines tenant-level rate limits in detail.
- “What are the actual tenant-level limits? Are they documented?”
Ans- No tenant-level limits are not publicly documented by Microsoft.
- “We’re making one request per subscription per second (300+ subscriptions), but still throttled — how can we stay within tenant-level limits?”
Ans- Even though 1 request/sec/subscription sounds modest, at 300 subscriptions, that's still 300 requests/sec hitting ARM, possibly from the same Service principal / Region / Resource provider (Microsoft.Compute) / Tenant
This aggregate rate may exceed the internal per-tenant or per-identity thresholds, especially if the APIs have lower backend limits (e.g., 100-200 calls/sec across a tenant for certain provider APIs).
There’s evidence that some ARM APIs (including /usages and /quotas) have backend thresholds of around 50–100 requests/sec across a tenant or SPN, even if per-subscription headers look fine.