Throttling Resource Manager requests

For each subscription and tenant, Resource Manager limits read requests to 15,000 per hour and write requests to 1,200 per hour. These limits apply to each Azure Resource Manager instance. There are multiple instances in every Azure region, and Azure Resource Manager is deployed to all Azure regions. So, in practice, limits are effectively much higher than these limits, as user requests are usually serviced by many different instances.

If your application or script reaches these limits, you need to throttle your requests. This article shows you how to determine the remaining requests you have before reaching the limit, and how to respond when you have reached the limit.

When you reach the limit, you receive the HTTP status code 429 Too many requests.

The number of requests is scoped to either your subscription or your tenant. If you have multiple, concurrent applications making requests in your subscription, the requests from those applications are added together to determine the number of remaining requests.

Subscription scoped requests are ones the involve passing your subscription ID, such as retrieving the resource groups in your subscription. Tenant scoped requests don't include your subscription ID, such as retrieving valid Azure locations.

Remaining requests

You can determine the number of remaining requests by examining response headers. Each request includes values for the number of remaining read and write requests. The following table describes the response headers you can examine for those values:

Response header Description
x-ms-ratelimit-remaining-subscription-reads Subscription scoped reads remaining. This value is returned on read operations.
x-ms-ratelimit-remaining-subscription-writes Subscription scoped writes remaining. This value is returned on write operations.
x-ms-ratelimit-remaining-tenant-reads Tenant scoped reads remaining
x-ms-ratelimit-remaining-tenant-writes Tenant scoped writes remaining
x-ms-ratelimit-remaining-subscription-resource-requests Subscription scoped resource type requests remaining.

This header value is only returned if a service has overridden the default limit. Resource Manager adds this value instead of the subscription reads or writes.
x-ms-ratelimit-remaining-subscription-resource-entities-read Subscription scoped resource type collection requests remaining.

This header value is only returned if a service has overridden the default limit. This value provides the number of remaining collection requests (list resources).
x-ms-ratelimit-remaining-tenant-resource-requests Tenant scoped resource type requests remaining.

This header is only added for requests at tenant level, and only if a service has overridden the default limit. Resource Manager adds this value instead of the tenant reads or writes.
x-ms-ratelimit-remaining-tenant-resource-entities-read Tenant scoped resource type collection requests remaining.

This header is only added for requests at tenant level, and only if a service has overridden the default limit.

Retrieving the header values

Retrieving these header values in your code or script is no different than retrieving any header value.

For example, in C#, you retrieve the header value from an HttpWebResponse object named response with the following code:

response.Headers.GetValues("x-ms-ratelimit-remaining-subscription-reads").GetValue(0)

In PowerShell, you retrieve the header value from an Invoke-WebRequest operation.

$r = Invoke-WebRequest -Uri https://management.azure.com/subscriptions/{guid}/resourcegroups?api-version=2016-09-01 -Method GET -Headers $authHeaders
$r.Headers["x-ms-ratelimit-remaining-subscription-reads"]

Or, if want to see the remaining requests for debugging, you can provide the -Debug parameter on your PowerShell cmdlet.

Get-AzureRmResourceGroup -Debug

Which returns many values, including the following response value:

DEBUG: ============================ HTTP RESPONSE ============================

Status Code:
OK

Headers:
Pragma                        : no-cache
x-ms-ratelimit-remaining-subscription-reads: 14999

To get write limits, use a write operation:

New-AzureRmResourceGroup -Name myresourcegroup -Location westus -Debug

Which returns many values, including the following values:

DEBUG: ============================ HTTP RESPONSE ============================

Status Code:
Created

Headers:
Pragma                        : no-cache
x-ms-ratelimit-remaining-subscription-writes: 1199

In Azure CLI, you retrieve the header value by using the more verbose option.

az group list --verbose --debug

Which returns many values, including the following values:

msrest.http_logger : Response status: 200
msrest.http_logger : Response headers:
msrest.http_logger :     'Cache-Control': 'no-cache'
msrest.http_logger :     'Pragma': 'no-cache'
msrest.http_logger :     'Content-Type': 'application/json; charset=utf-8'
msrest.http_logger :     'Content-Encoding': 'gzip'
msrest.http_logger :     'Expires': '-1'
msrest.http_logger :     'Vary': 'Accept-Encoding'
msrest.http_logger :     'x-ms-ratelimit-remaining-subscription-reads': '14998'

To get write limits, use a write operation:

az group create -n myresourcegroup --location westus --verbose --debug

Which returns many values, including the following values:

msrest.http_logger : Response status: 201
msrest.http_logger : Response headers:
msrest.http_logger :     'Cache-Control': 'no-cache'
msrest.http_logger :     'Pragma': 'no-cache'
msrest.http_logger :     'Content-Length': '163'
msrest.http_logger :     'Content-Type': 'application/json; charset=utf-8'
msrest.http_logger :     'Expires': '-1'
msrest.http_logger :     'x-ms-ratelimit-remaining-subscription-writes': '1199'

Waiting before sending next request

When you reach the request limit, Resource Manager returns the 429 HTTP status code and a Retry-After value in the header. The Retry-After value specifies the number of seconds your application should wait (or sleep) before sending the next request. If you send a request before the retry value has elapsed, your request isn't processed and a new retry value is returned.

Next steps