Fixing Azure OpenAI 429 errors: tokens-per-minute, quota, and what actually works
The 429 is the most-hit error in Azure AI work, and most of the advice about it is "add retries" — which works right up until it quietly becomes your architecture. Here is what the error actually means, why you hit it far sooner than the math suggests, and the fixes in order of effort, from a header you are probably ignoring to capacity you might genuinely need to buy.
A 429 can mean a deployment exceeded its token or request limit, or that shared service capacity is temporarily constrained. Read the error body and rate-limit headers before assuming your approved quota is exhausted. Honor retry-after-ms when supplied (milliseconds), or retry-after (seconds or an HTTP date), and bound retries. Compare effective limits with configured quota before buying capacity.
Let me walk through it the way I would with someone on my team: what the limiter actually counts, the cheap fixes, the expensive ones, then the uncomfortable question of whether you have a retry problem or a capacity problem. They feel the same at 2am. They are not.
What a 429 actually means here
A 429 is a throttling response, but it does not identify the cause by itself. Standard deployments can be throttled because of TPM/RPM limits, short traffic bursts, or temporary capacity pressure that lowers the effective rate limit. Microsoft’s quota guide distinguishes these cases. Persistent throttling below approved quota warrants a support investigation, not an automatic purchase.
The budget has two dials:
- TPM — tokens per minute. The total token throughput your deployment may consume in a minute. This is the main dial.
- RPM — requests per minute. A cap on call count regardless of size. You do not set RPM separately; it rides along with your TPM allocation at a fixed ratio. The ratio varies by model and Microsoft adjusts these over time, so check the current quota documentation rather than trusting a number in a blog post, including this one.
Now the part that explains half the confused Slack threads I have seen: where the quota lives. Quota is held per subscription, region, model and deployment type. Think of it as a pool: your subscription might hold, say, some amount of GPT-4o TPM in East US 2. Every deployment you create for that model in that region carves its slice out of that one pool. Create three deployments of the same model in the same region and they are splitting the same budget, not tripling it. Deploy the same model in a different region, and you are drawing from a different pool entirely. This is why "just make another deployment" sometimes helps (different region or model) and sometimes does nothing at all (same region, same model, same exhausted pool).
Read the response headers rather than looking for a made-up exception attribute. Azure can return retry-after-ms in milliseconds; retry-after uses seconds or an HTTP date. Recent official SDKs handle these headers, so configure a bounded retry policy instead of stacking several independent retry loops.
Why you hit the limit sooner than you think
Here is where the arithmetic most people do in their heads goes wrong. You look at your TPM number, estimate your average request at a few hundred tokens, multiply, and conclude you have enormous headroom. Then you get rate limited at a fraction of that volume. Three reasons, in descending order of how often they are the culprit.
The limiter counts max_tokens at request time, not actual usage
This is the single most common surprise, and as documented as of 2026 it works like this: when your request arrives, the limiter estimates its cost as your prompt tokens plus the full max_tokens you asked for — before the model has generated a single token. Set max_tokens to 4,000 "just to be safe" and every call reserves roughly 4,000 tokens of your minute budget, even when the completion comes back at 80 tokens. The unused reservation is not the bill you pay in dollars — billing is on actual usage — but it absolutely is the budget you burn against the rate limiter.
The practical consequence: a lazy default max_tokens copied from a quickstart can cut your effective throughput by 10x or more. If you take one thing from this article, audit that parameter first.
Bursts inside the minute matter
The limit says "per minute," but enforcement does not politely wait for the minute to end. The platform evaluates your rate over short sub-minute windows — behavior Microsoft has described in terms of one-second and ten-second buckets, though I would hedge on the exact mechanics since they are an implementation detail that can change. What it means for you: firing your entire minute's worth of traffic in the first two seconds will draw 429s even though your total for the minute was technically under budget. Smooth traffic passes; spiky traffic of the same volume gets throttled. If your app fans out twenty parallel calls the instant a user clicks, that burst is the problem, not your monthly volume.
Streaming does not exempt you
A streamed response feels lighter, and people half-consciously assume it is metered differently. It is not. A streaming request is estimated and counted the same way at admission — prompt plus max_tokens, reserved up front. Streaming improves perceived latency for your user; it does nothing for your quota.
Billing charges you for the tokens you used. The rate limiter charges you for the tokens you asked permission to use. Those are very different numbers.
The immediate fixes, in order of effort
These are the same-day fixes, cheapest first. Most teams need the first three and never think about them again.
1. Read Retry-After and honor it
Honor a valid server retry delay when available: retry-after-ms is milliseconds, while retry-after is seconds or an HTTP date. The header is not guaranteed on every response. Recent official SDKs handle retries; configure their limits and inspect the actual response before adding another loop. Use bounded backoff with jitter when no valid delay is provided.
2. Backoff with jitter, done right
For the official client, configure max_retries and a timeout as shown below. The request timeout applies to each attempt; set an overall deadline in your application too. A raw REST integration needs to parse the actual response headers, respect their units, and use bounded exponential backoff with jitter when no valid server delay is available.
# Configure the official Python client once; it handles retry headers.
import os
from openai import AzureOpenAI
client = AzureOpenAI(
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
api_key=os.environ["AZURE_OPENAI_API_KEY"],
api_version=os.environ["AZURE_OPENAI_API_VERSION"],
max_retries=2,
timeout=30.0,
)
# Use a supported API version and your deployed model name for calls.
# Do not add another outer retry loop without accounting for these retries.
Jitter spreads retries so throttled workers do not all wake together. Avoid stacking SDK retries with a second retry library: the total attempts and wait time multiply. Log the error code, effective limits and final failure so retrying does not hide sustained overload.
3. Right-size max_tokens per call
Given what section two established, this is the highest-value cheap fix. Stop using one global max_tokens. A classification call that returns one word does not need the same ceiling as a report generator. Set each call type's limit to a sane maximum for what it actually produces, and your effective TPM can multiply without touching quota at all.
4. Get background work off the interactive path
If nightly summarization jobs, embedding backfills, and user-facing chat all share one deployment, your batch work is spending the quota your users need at the worst moments. Push background work through a queue with a controlled concurrency and let it drain slowly — it does not care about latency, so make it the traffic that waits. If the volume is large, Azure OpenAI's Batch API runs it at lower priority (and, at the time of writing, lower cost) without touching your interactive limits at all.
| Fix | Effort | When it is the right tool |
|---|---|---|
| Honor Retry-After / SDK retries | Minutes | Always — this is the floor, not a strategy |
| Backoff with jitter | An hour | Raw REST calls, or frameworks that bypass SDK retries |
| Right-size max_tokens | An hour | Any app with a copied-from-quickstart global value |
| Queue background work | A day or two | Batch jobs sharing a deployment with users |
| Raise deployment TPM | Minutes, if pool has room | Deployment slice is small but subscription quota is not |
| Quota increase request | Days (not guaranteed) | The regional pool itself is exhausted |
| Second-region deployment + router | Days | Real headroom needed now; data residency allows it |
| Provisioned throughput (PTU) | Capacity planning; hourly or reservation billing | Sustained, measured production load — never a dev fix |
The capacity fixes
If throttling persists, compare observed traffic with effective limits and configured quota. Investigate shared-capacity pressure and contact support for sustained throttling below approved quota. If measured demand exceeds the allocation, then evaluate queueing, allocation changes or additional capacity.
Raise the deployment's TPM allocation
Remember the pool model: your deployment holds a slice of the subscription's regional quota for that model. Often the slice is small while the pool still has room — especially if the deployment was created with a default allocation. In the Azure AI Foundry portal (or the quota blade), check the model's quota for your region and drag the deployment's TPM up to what the pool allows. This is a two-minute change and it is genuinely common for it to be the whole fix.
Request a quota increase
If the pool itself is exhausted, you can file a quota increase request from the same quota view. Be honest with yourself about timelines: these are reviewed, not automatic, and approval depends on regional capacity for that model. Days, sometimes longer, sometimes declined for constrained models. File it early, but do not make it your only plan.
Split across a second region
Since quota pools are per region, a deployment of the same model in a second region is new budget. The pattern is a thin router in front: send traffic to region A, spill to region B on a 429 (or round-robin if you prefer). Azure API Management can do this with policy, or twenty lines in your own gateway does it fine. Two caveats that deserve respect: data residency — if your compliance posture requires prompts and outputs to stay in a geography, confirm the second region qualifies before you route a single request; and consistency — model versions and feature availability can differ slightly by region, so pin versions explicitly.
PTU: provisioned throughput, and what it is actually for
Everything above is pay-as-you-go, where you share pooled capacity and the TPM/RPM limits are how the platform keeps sharing fair. Provisioned throughput units (PTUs) are the other model: you reserve dedicated capacity for your deployment. What you get is predictable throughput and, just as valuable in production, predictable latency — capacity and quota requirements differ from Standard deployments. What you pay is a commitment: PTUs support hourly billing or reservation discounts that only makes sense at sustained volume, and I will hedge on figures because they change — check current Azure pricing and run your own arithmetic.
The honest note, because vendors will not say it plainly: PTU is not a fix for a development annoyance. If your 429s come from a demo, a test suite, or a bursty internal tool, PTU is a truck you are buying to deliver one letter. It is for measured, sustained production load where the commitment is cheaper than the equivalent pay-go tokens — or where latency predictability is worth the premium on its own. There is also a useful middle pattern: spillover, where your PTU deployment handles the base load and overflow spills to a pay-go deployment instead of failing. Microsoft has been building this out as a managed feature; availability and mechanics have been shifting, so verify the current state in the docs before you design around it.
Every class is open to read. A free account saves your progress. Azure resources used for labs may incur charges.
The part the retry tutorials skip
Here is the honest gap. Retries do not create capacity. Every retry is the same work submitted again, later — which means under sustained overload, retries convert your capacity shortage into a latency problem and then hide it. The request eventually succeeds, your error rate looks fine, and meanwhile your p95 latency has tripled because half your traffic is sleeping in backoff loops. I have watched teams tune retry parameters for weeks when the true situation was simple: they needed more TPM than they owned.
The test is frequency. A 429 during a traffic spike, absorbed by backoff, invisible to users — that is the system working. 429s every day, at predictable hours, with users feeling the slowdown — that is not a transient error, that is a capacity-planning problem wearing an error code. Retrying harder at that point is denial with jitter.
The way out is to measure before you buy. Two numbers, from your logs, this week: tokens per request (prompt and completion separately — and compare completion actuals against your max_tokens settings, because the gap between them is free throughput you are leaving on the table) and requests per user session at peak. Multiply out to peak TPM demand, add sensible headroom, and now you know whether the answer is a bigger allocation, a second region, or PTUs — and you can defend the spend with arithmetic instead of vibes. Cost and capacity are the same conversation here; what AI workloads actually cost on Azure walks the money side of the same measurement.
This measure-then-provision habit is the difference between operating AI infrastructure and being surprised by it — the same discipline we drill in the free classes, and in more depth in the live cohort, where students size and deploy real workloads rather than reading about them.
A diagnosis checklist you can run today
- Confirm it is really a rate limit. Read the error body and retry/rate-limit headers; distinguish token or request throttling from temporary service-capacity pressure. (A 429 for exceeding a spending cap is a different beast with a similar face — the error body tells you which.)
- Check your max_tokens defaults. If a global value is 4x your typical completion, you found it.
- Look at burst shape. Are the 429s clustered in the same seconds? Smooth the fan-out.
- Separate traffic classes. Is batch work sharing the users' deployment?
- Check the quota blade. Is the deployment's TPM slice small while the regional pool has room?
- Only then talk about quota increases, second regions, or PTUs — with measured tokens-per-request in hand.
If you work through those six in order, you will fix the large majority of 429 situations without spending a dollar. And if you want the surrounding context — what Azure AI Foundry is and how deployments fit into it — the Foundry explainer is the right companion read, with the AI infrastructure overview behind it.
Questions people also ask
What does error 429 mean in Azure OpenAI?
A 429 can reflect token/request throttling, a burst within the minute, or temporary shared-capacity pressure. Read the error body and effective rate-limit headers. Respect retry-after-ms or retry-after when present, with the correct units, and bound retries; the status code alone does not prove the service is healthy or your quota exhausted.
How do I fix the 429 rate limit error in Azure OpenAI?
Read the error and effective limits; honor the actual retry headers with bounded retries. Smooth bursts, queue background work, and choose an appropriate output limit for the model/API. If demand exceeds allocation, evaluate quota or capacity options. Persistent failures below approved quota need investigation rather than automatic spending.
Does max_tokens affect Azure OpenAI rate limits?
Yes, and more than most people expect. As documented as of 2026, the rate limiter estimates a request's cost using your prompt size plus the max_tokens you set, at the time the request arrives — before the model generates anything. A request with max_tokens set to 4,000 reserves roughly that much from your tokens-per-minute budget even if the completion comes back at 80 tokens. Right-sizing max_tokens per call is often the single cheapest fix for 429s.
What is the difference between TPM and RPM in Azure OpenAI?
TPM is tokens per minute — the total token throughput a deployment may use in a minute, counting prompt tokens plus reserved completion tokens. RPM is requests per minute — a cap on how many calls you can make regardless of size. When you assign TPM to a deployment, an RPM limit comes with it at a fixed ratio that varies by model, so check the current Microsoft documentation for the ratio on your model. You can hit either limit first: many small requests trip RPM, a few large ones trip TPM.
Should I use provisioned throughput (PTU) to fix 429 errors?
Only if you have sustained production load. Provisioned throughput units reserve dedicated capacity, which gives you predictable latency and throughput, but they come with a monthly-commitment style of pricing that only pays off at steady volume — check current Azure pricing, because the economics change. For a development annoyance or bursty low traffic, backoff, right-sized max_tokens, and a modest TPM increase are the right tools. PTU is a capacity purchase, and it should follow measurement of your real token usage, not frustration.
It worked for them.
Sources and review
Reviewed 1 October 2026. This example configures SDK retries; it does not call a live model. Output-limit parameter names vary by model and API: check whether your deployment uses max_tokens, max_completion_tokens or max_output_tokens.