Limits and the 429 code
Five different limits with five different remedies hide behind a single 429 code. How to tell them apart with one look at the response.
Translation in progress
A 429 in the Lumean API is not one limit but five. They have different causes, different response bodies
and, most importantly, different remedies: what fixes one is useless against the other four.
The generic “wait and retry” is guaranteed to work in exactly two cases out of five.
Gotcha
The key rate limit does not send a Retry-After header, contrary to the introductory text in the OpenAPI spec,
which promises it for every 429. The missing header and the missing reason in the body are
the fastest way to tell the request-rate limit from the token quota.
How to tell them apart at a glance
Two checks in this order are enough: does the body have a reason field, and did a Retry-After
header arrive. The first check separates the request-rate limit from the four order-related limits;
the second tells you whether you can simply wait.
Five sources
| Source | reason | Retry-After | What to do |
|---|---|---|---|
| Key request rate | no | no | Your own backoff based on the key's windows |
| Key token quota | token_quota_exceeded | yes | Wait retry_after seconds |
| Subscription usage limit | limit_exceeded | no | Check GET /usage, buy more, or wait for the next period |
| Concurrent task slots | concurrency_limit_exceeded | no | Wait for your own orders to finish |
| Content policy cooldown | policy_cooldown_active | yes | Fix the text; don't retry it as is |
Key request rate
A limit on the key's own limits.requests.per_minute|per_hour|per_day windows. The body is
a bare envelope { "success": false, "message": "…" }: no reason, no Retry-After,
no X-RateLimit-*. Go by the key's configured windows and implement exponential
backoff yourself.
An important nuance: by default a key has no request-rate limit at all. A numeric default
applies only if the operator has set one in the environment. The effective windows are shown in your
dashboard. If you got this 429 while the windows are empty, you are almost certainly looking at one
of the four limiters below.
Key token quota
It counts tokens, not requests: the accounting unit of work volume (this is not money and not LMC).
The per_minute, per_hour, per_day, per_month windows come from the key's limits.tokens.*.
The check happens before creation: the order is not created and not charged.
- What happened
- The order was not created. This is the only 429 with a detailed body: `window` (the first window violated), `limit`, `used`, `requested`, `reset_at`, `retry_after`. It comes together with a `Retry-After` header.
- What to do right now
- Wait until `reset_at` — or `retry_after` seconds; it is the same value. The presence of `Retry-After` is exactly how you tell this case apart from the key rate limit, which has no such header at all. If you cannot wait, reduce `requested`: split the text into parts so that each one fits into what remains in the window (`limit − used`).
The body contains window (the first window exceeded), limit, used, requested, reset_at
and retry_after, which is enough to queue the job until the exact moment
instead of polling the API blindly.
Subscription usage limit
This is about your plan, not the key: the monthly generation volume is used up. The body contains only
message and reason: "limit_exceeded", with no quota fields and no Retry-After. Pausing won't help:
the response won't change until the end of the billing period.
Get Usage Limits
Concurrent task slots
concurrency_limit_exceeded limits neither rate nor volume but the number of your orders
in progress at the same time. A batch of hundreds of orders sent in a single burst hits exactly
this limit. The fix is not a pause between requests but waiting for the already running orders to finish: a retry
right after any of them reaches a terminal status will go through.
The effective ceiling and the current number of active orders are in GET /usage, in the entry with
limit_type: "concurrent". Its period_start, period_end and resets_in are null:
this limit has no period, so waiting for “the end of the month” is pointless.
Content policy cooldown
Five tasks in a row blocked by the content policy restrict creating new ones for 5 minutes,
for 10 on repeat offenses, then for 15 (it goes no higher). This is the only
order-related 429 that carries Retry-After, along with the retry_after (seconds)
and retry_at (ISO-8601) fields.
Infrastructure failures don't move the counter: only content blocks count, and any successful task resets both the counter and the level reached.
Do
Wait for retry_at, and before then fix the wording in the text that the policy rejected.
Don't
Retry the same text after the cooldown expires: it will hit the block again, raise the level to 10 minutes, and cost money on every attempt.
What to do as an integrator
- Read
reasonbeforeRetry-After: it alone answers the question “is this about rate, volume, plan, concurrency, or text”. - Respect
Retry-Afterwhen it is present. When it is absent, a timer-based retry helps only with the request-rate limit; in the other cases you needGET /usageor a text fix. - Don't confuse
429with402. The402codepayg_topup_requiredis not a rejection but a payment fork: the server offers to pay for the missing tokens from your LMC balance. A blind retry is useless here; you need to repeat the request withconfirm_payg_topupandquote_token. - Cap the number of attempts. Three of the five sources don't clear up on their own, and an endless retry loop won't bring the order a single second closer to completion.
Tip
Not sure which of the five you got? Paste the response body into the diagnostic tool on the
“Errors” page: it parses reason and the headers and shows the matching card.
Feed this page to an AI
Updated 04/09/2026 · spec 0.0.1