Safeguard
Developer Tools•

Why did my AI API return 429? Rate limit or spend cap, and how to tell

A 429 from an AI API can mean a rate limit, an acceleration limit or a monthly spend cap. Here is how to tell them apart and what to do for each.

Safeguard Security Team
8 min read

A 429 from an AI API means you exceeded a limit, but not always the one you think. It can be a per-minute rate limit, a sharp-growth acceleration limit, or a monthly spend cap. The retry-after header and the error details tell you which, and the right fix differs for each.

Three causes behind one status code

Anthropic's documentation describes two kinds of limits: spend limits set a maximum monthly cost, and rate limits set the maximum number of requests over a period. It also mentions acceleration limits, which can trigger when usage rises sharply. All of these can surface as a 429.

CauseTypical signFix
Rate limitretry-after header present, clears within secondsBack off, smooth traffic, cache, queue
Acceleration limitErrors after a sudden traffic jumpRamp up gradually, keep usage steady
Spend capNo retry-after, error details name a spend limitRaise the cap, wait for the next period, or fix the cause of spend

The spend cap is the dangerous one

The documentation says that once you reach a tier's monthly cap, usage pauses until the first day of the next month unless you request a higher limit. The response is a 429 with the same error type as a rate limit, but with no retry-after header, and the SDK's automatic retries fail until access resumes. A code path that treats every 429 as transient will loop against a wall.

The error body carries a code that separates the two. On the Messages API the documentation names it as enforced_spend_limit_reached. Branch on that field, not on the status alone.

If you set your own lower limit, the behavior differs again. The same page says requests then return HTTP 400 with an invalid request error whose message begins by saying you reached your specified usage limits. So a self-set cap and a tier cap look different to your code.

A triage routine

  1. Read the response headers. Look for retry-after and the remaining and reset fields for requests and tokens.
  2. Read the error body for a code that names a spend limit.
  3. Check the usage page in your provider console for the last hour. Was there a spike?
  4. If it is a rate limit, apply backoff with jitter and look at whether one service is hogging the pool.
  5. If it is a spend cap, stop retries and alert a human, because the next retries only add load.
  6. Find out why spend rose before you raise the cap. A runaway loop or a leaked key looks the same as growth until you check.

Raising a cap without finding the cause is how a small incident becomes a large invoice. The cost anomaly runbook walks through that investigation.

Make your client tell them apart

A small amount of code pays off. Classify errors into retry later, retry never, and page someone. A spend cap error belongs in the second or third group. A rate limit with a retry-after value belongs in the first, using that value as the wait. OpenAI's guidance says to respect Retry-After when present, use exponential backoff with jitter otherwise, cap attempts and total retry time, and not retry quota or billing errors.

Log the error code alongside the request ID so you can answer later which service hit which limit.

Worked example: three incidents, three fixes

These scenarios are illustrative.

Incident one: a sudden launch. A feature goes live at noon and traffic jumps within minutes. Errors begin at once, and most responses carry a retry-after header. This is a rate limit, possibly an acceleration limit. The fix is a queue with a concurrency cap, a gradual rollout, and a request for higher limits ahead of the next launch.

Incident two: the quiet month end. On the 27th, every call starts failing, no response carries a retry-after header, and the error body names a spend limit. Your SDK retries each call several times, which only adds load. The fix is to stop retries on that error code, page the owner, and check the usage page for a spike before raising anything.

Incident three: a limit you set yourself. After a finance review, someone sets a low monthly limit. Requests begin to return HTTP 400 rather than 429. Teams that only watch for 429 miss it, and customers see a generic failure. The fix is to alert on the 400 error type that names your specified limit, and to document who set the limit and why.

What to log for each error

Record these fields so triage takes minutes:

FieldWhy
HTTP statusFirst split between rate and cap
Error type and codeTells a spend cap from a rate limit
retry-after valueWhether waiting will work
Request IDLets the provider find the call
Workspace or key nameWhich service hit it
ModelLimits are per model class

Alert design

Page a human for spend cap errors, since nothing automatic will fix them. Ticket rate limit errors when they exceed a small share of requests over ten minutes. Chart daily spend against a rolling baseline so you see the climb before the wall.

Communicating with users during a limit

A good error message depends on the cause. For a rate limit, show a short "busy, trying again" state and retry quietly. For a spend cap, do not pretend it is temporary. Show a clear unavailable notice, route the user to a fallback such as a saved draft or a human, and alert your team. Customers forgive a clear message far faster than a spinner that never ends.

Write the three messages in advance, with owners for each, and test them in staging by forcing each error from a stub.

A short decision tree

Start with the header. If a retry-after value is present, wait that long and retry once, then use backoff. If it is absent, read the error code. A code that names a spend limit means stop. If the message names a limit you set yourself, you will see a different status, so check that too. If you are unsure, treat the error as non-retryable and alert a human, because a wrongly retried spend error wastes effort while a wrongly abandoned rate limit error only delays one job.

Teach the tree to everyone who writes AI integrations. Put it in your service template so each new project starts with the right classification. Add a test that feeds the three error shapes through the client and checks the decisions.

What this means for you

A 429 is not one problem. Read the headers and the error body before you react. Waiting fixes a rate limit. Nothing automatic fixes a spend cap, and retrying it only adds noise. Teach your code to tell them apart and your team to page a person for the second kind.

Common mistakes

Typical mistakes are retrying every 429, trusting an SDK's automatic retries to handle a spend cap, raising a cap without checking why spend rose, alerting only on one status code and forgetting that a limit you set yourself reports differently. Log the error code, classify the error, alert the right person and investigate before raising anything.

Try it yourself

Click through it at your own pace, or open it full screen. It shows monitoring that watches a system over time and raises an alert on change. Look for how a steady baseline makes a spike stand out, which is the same idea behind alerting on daily spend.

Quick check

Should I retry every 429?

No. Retry rate limit errors using the retry-after value or backoff with jitter. Do not retry spend cap or billing errors, because they will not clear until the cap resets or is raised.

Why is there no retry-after header on my 429?

Anthropic documents that the spend cap 429 never carries one. Other 429 causes may also omit it, so use backoff.

Is a self-set limit reported the same way as the tier cap?

Not on the provider documented here. A self-set limit returns HTTP 400, while the tier cap returns 429. Check your provider's current docs.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.