{"error":{"message":"You exceeded your current quota, please check your plan and billing details.","type":"insufficient_quota","param":null,"code":null}}
Your key was accepted and your request was understood — an invalid key returns
401 and says so, an unsupported region returns 403, and a model that is
temporarily overloaded returns 503 with server_is_overloaded. This is a 429:
the request was declined on budget or pacing grounds, on the account side, by
OpenAI. What the body does not tell you is which budget, and the candidates
have different fixes, one of which is “add money” and one of which is “send
fewer requests per minute”.
Data as of 2026-09. Vendor limits and defaults change; check the official docs for current values before acting on any number below.
The field you need is the one that is null
OpenAI’s own guidance for billing errors is to inspect error.code, precisely
because error.type stays broad — insufficient_quota is a family, not a
diagnosis. In the body you pasted, code is null.
That is the whole problem with this particular error string. The discriminator
the vendor points you at is empty, the message is generic prose written to
cover several conditions, and param is only ever populated for a different
class of error. Reading this JSON harder will not tell you anything more than
you already know. The information you need is in two places the body does not
touch: the response headers that came with it, and your console.
If you are catching this in code, capture the x-request-id response header at
the same time. Both official SDKs expose it — response._request_id on success,
and on a failure you catch the status error and read .request_id in Python or
.requestID in Node. Without it, a support conversation about a 429 is a
description of a feeling.
The distinct ways an OpenAI account says “no” at 429
These are separate conditions with separate documented codes. You are trying to work out which one produced a body whose code field is empty, so match on the observation, not on the wording.
- Ordinary rate limit — you exceeded requests or tokens per minute. The
documented shape carries no special code, which makes it the best fit for a
null code field. Observation:
x-ratelimit-remaining-requestsorx-ratelimit-remaining-tokensat or near zero. - Ramp-rate limit (
slow_down, typerate_limit_error) — documented to occur even when you are within your per-minute limits, because it reflects how fast your traffic grew rather than its level. Observation: headroom in the headers, plus a sharp increase in traffic in the minutes before. credit_balance_exhausted— prepaid credits gone. Observation: a zero balance in the console. Nothing client-side helps.organization_spend_limit_exceeded/project_spend_limit_exceeded— a cap you configured, at org or project level. Observation: the limit exists in your own settings, and the spend figure has reached it.organization_usage_limit_exceeded— the monthly usage limit OpenAI assigns to your tier, which is a different thing from the caps you set yourself. Published tiers run from Free and Tier 1 at $100 per month, Tier 2 at $500, Tier 3 at $1,000, Tier 4 at $5,000, to Tier 5 at $200,000, with promotion happening automatically as paid spend accumulates.
Two header details decide most cases quickly. Retry-After appears on a 429
caused by a temporary rate limit — the docs are explicit that its presence
does not mean quota or billing errors can be resolved by waiting. And the
reset headers are duration strings like 6m0s or 1s, not timestamps, so
you subtract nothing and simply wait them out.
Is retrying useful?
Yes — exactly one retry, and only as a test. After that, no.
The body you have is under-determined, and a second attempt is the cheapest way
to split the two halves of the list above. A pacing limit clears on its own as
the window moves: the retry succeeds, or it fails with a shorter wait in
Retry-After, and you now know you have a throughput problem. A budget
condition does not move at all: an identical body comes back instantly, and the
docs say it plainly — retrying billing, spend or quota errors will not restore
access; you have to change the credits or the limits first.
Three rules for that single retry. Honor Retry-After when it is present, and
do not invent a wait of your own — no base delay is published for you to copy,
and the guidance for hand-rolled clients is exponential backoff with jitter plus
a cap on both attempts and total time. Remember that failed requests still
count against your per-minute limit, so hammering a pacing 429 actively makes
it worse. And check what your SDK already did: the official clients retry 429s
twice by default with a short exponential backoff, which means the body you are
reading may be the third refusal, not the first. If you add your own loop,
disable or account for the SDK’s, or you multiply your request count without
noticing.
Why your colleague’s key works and yours does not
Limits are enforced per organization and per project — never per user. Two engineers on the same team hitting different outcomes is not evidence that one key is broken; it is evidence that the keys belong to different projects, or that one of them is drawing on a pool the other is not.
There is a precise signal for this: the x-ratelimit-limit-project-tokens,
-remaining-project-tokens and -reset-project-tokens headers appear only
when a project-scoped limit applies. If you see them, the limit refusing you
is the project’s, and raising something at the organisation level will change
nothing.
Fix by which limit refused you
- Ordinary rate limit — cut concurrency before you touch anything else.
Per-minute limits are about arrival rate, so halving the number of in-flight
requests moves the needle faster than any prompt edit. Note one OpenAI-specific
rule while you are there: your rate limit is calculated as the maximum of
max_tokensand the estimated tokens in your request, so an inflatedmax_tokensyou never reach still burns limit. That is the opposite of Anthropic’s documented behaviour — if you have worked on both platforms, this is the habit that will cost you. - Ramp-rate
slow_down— introduce a gradient instead of going from idle to full throughput in one step. The documented rule of thumb is that once traffic reaches 1 million input tokens per minute you should increase by no more than 50% every 15 minutes, with the docs’ own caveat that the exact point where the ramp-rate limit applies varies by model and by traffic conditions. - Credits exhausted — add credits. No code change helps, and every retry is a request you are spending rate limit on to reconfirm something you already know.
- A spend limit you set — raise or remove it where you set it, at the level you set it, or wait for the monthly reset. Check the project level as well as the organisation level; they are separate settings with separate codes.
- The tier usage limit — this one is assigned rather than configured, so the path is a limit-increase request or support. Tier promotion follows paid spend automatically, which means time and usage are the other lever.
Confirming it, by case
The confirmations differ, and using the wrong one is how people ship a “fix” that fails again the next afternoon.
For a budget condition, the fix is confirmed the first time the identical request — same key, same project, same model — returns a normal response. Nothing except an actual change to credits or limits can produce that transition, so a single success is sufficient evidence here.
For a pacing condition, a single success proves nothing, because the window
moved while you were reading this. Re-run the real workload at the intended
concurrency for longer than a minute and watch x-ratelimit-remaining-requests
and x-ratelimit-remaining-tokens stay above zero for the whole run. If either
one dips to zero, you are still rate limited and simply have not been refused
yet.
For a ramp-rate condition, the test is the shape of the traffic, not its total: start from idle, climb in steps, and confirm no refusal at the step size you intend to deploy with.
Related quota failures
- openai.RateLimitError: Error code: 429 insufficient_quota is this same condition seen through the Python SDK’s exception class — read that one if you are deciding what to catch and how to branch in code rather than reading a raw body.
- exceeded retry limit, last status: 429 Too Many Requests is what you get when a client retries this for you and gives up. If that is where you started, the number of attempts already spent matters, because each one counted against the limit.
- The API rate limit calculator is the fastest way to find out whether your intended request rate and token volume fit inside the tier you are on — which is the difference between a budget problem and a pacing problem before you ever send a request.