Resource Manager throttling and long-running operations
How Azure Resource Manager limits and completes requests — the 2024 regional token-bucket limits (per subscription, per service principal, per operation type), the x-ms-ratelimit-remaining headers, 429 with Retry-After and provider-specific throttles, how the TypeScript SDK retries by default (3 retries, Retry-After honoured) and how to tune it, and how asynchronous operations report progress (201/202, Azure-AsyncOperation, Location, provisioningState) and are awaited with SDK pollers.
markdownreferencev11VD0-ZV4Astage: buildtrust: officialservice: arminterface: sdkinterface: cliinterface: restARM throttling429 Too Many RequestsRetry-Aftertoken bucket Azure Resource Managerx-ms-ratelimit-remaining-subscription-readsx-ms-ratelimit-remaining-subscription-writesSubscriptionRequestsThrottledprovider throttling Microsoft.Networkmetrics API throttling getBatchretryOptions maxRetrieslong-running operationAzure-AsyncOperation headerLocation header 202provisioningState Succeeded Failed CanceledPollerLikebeginCreateOrUpdateAndWait deprecatedaz resource wait--no-wait
Throttling limits
Since 2024, Resource Manager throttles per region with a token bucket. A burst can spend the whole bucket; it then refills at a steady rate.
| Scope | Operation | Bucket | Refill per second |
|---|---|---|---|
| Subscription | reads | 250 | 25 |
| Subscription | writes | 200 | 10 |
| Subscription | deletes | 200 | 10 |
| Tenant | reads / writes / deletes | 250 / 200 / 200 | 25 / 10 / 10 |
- Subscription limits apply per service principal and per operation type. A global subscription limit is 15 times that, across all principals. One noisy identity throttles itself first.
- Free and trial subscriptions may get less.
- Providers throttle separately, per region of the resource: for example Microsoft.Network allows 1,000 writes and 10,000 reads per 5 minutes; Compute, Storage, Key Vault and Resource Graph have their own limits. Some providers return 429 for a temporary resource state, not volume.
- Metrics reads (
…/providers/microsoft.insights/metrics) are a common cause of subscription throttling. Use the batch metrics API.
What you get: HTTP 429 with a Retry-After value in seconds. Requests sent before it elapses count against you. Remaining budget appears in response headers: x-ms-ratelimit-remaining-subscription-reads, -writes, -deletes, and the tenant variants. Background-job throttling looks different: The request for subscription '…' could not be processed due to an excessive volume of traffic. Please try again later.
Retries in the TypeScript SDK
Every @azure/* client runs defaultRetryPolicy (@azure/core-rest-pipeline 1.25): it retries on 429 using Retry-After, on transport errors (DNS, resets), and otherwise with exponential backoff. Defaults: 3 retries, starting at 1 second. For batch scripts, raise it rather than writing your own loop:
import { DefaultAzureCredential } from "@azure/identity";
import { ResourceManagementClient } from "@azure/arm-resources";
const client = new ResourceManagementClient(new DefaultAzureCredential(), process.env.AZURE_SUBSCRIPTION_ID ?? "", {
retryOptions: { maxRetries: 8, retryDelayInMs: 2000, maxRetryDelayInMs: 60000 },
});
// Reads in a loop are the usual throttle source: list once, not get per item.
for await (const rg of client.resourceGroups.list()) {
console.log(rg.name, rg.location);
}
From the CLI, az retries too; for scripts, prefer one az resource list or Resource Graph query over hundreds of show calls.
Long-running operations
A create, update or delete that can't finish at once returns 201 Created or 202 Accepted and a provisioningState that isn't terminal (Succeeded, Failed and Canceled are terminal). To follow it:
- If the response has
Azure-AsyncOperation, poll that URL. Its body carries the status. - Otherwise poll the
LocationURL until it stops returning 202. - Wait
Retry-Afterseconds between polls when it's given. Accept status URLs of at least 4 KB.
In the TypeScript SDK the poller does this. In the 2026 packages (@azure/arm-resourcesdeployments, @azure/arm-resources 8), long-running methods return a PollerLike you can simply await; the older begin…AndWait helpers still exist but are marked deprecated:
import { DefaultAzureCredential } from "@azure/identity";
import { DeploymentsClient } from "@azure/arm-resourcesdeployments";
const deployments = new DeploymentsClient(new DefaultAzureCredential(), process.env.AZURE_SUBSCRIPTION_ID ?? "");
export async function deploy(resourceGroup: string, name: string, template: object) {
const result = await deployments.deployments.createOrUpdate(resourceGroup, name, {
properties: { mode: "Incremental", template },
});
return result.properties?.provisioningState; // terminal state
}
CLI: commands wait by default; --no-wait returns at once, and az resource wait --ids <id> --created (or --updated, --deleted, --custom "<JMESPath>") waits later.
A terminal Failed carries the operation's error. For deployments, read the failed operations (guides/arm/debug-failed-deployment.md).