nikolas.sapa
September 17, 2026

Rate limits became a UI problem

402 Budget pool quota has been exhausted.

That came back from AgentRouter on every Claude and GPT model I asked for. I did what the error invites you to do: checked the balance, considered rotating keys, started looking for an upstream incident.

All of that was wasted. The balance was fine — one key showed usage of $321 against a sentinel limit of 100,000,000, which is the number a provider writes when there is no cap at all. There was nothing to rotate, because there was nothing wrong with my credentials.

It was policy, and it was published

The provider's status endpoint is public. No auth, HTTP 200, and it returns their announcement feed. Announcement #16, dated three weeks earlier:

To keep the service running long-term, Claude and GPT models have been switched to limited supply, distributed in daily batches, first-come first-served. Currently two batches per day at Beijing 07:00 and 19:00. When the batch is used up you get exactly "402 Budget pool quota has been exhausted."

The error string I'd been grepping for was in their announcement, verbatim. The 402 fires after a batch is drained and before the next one lands. It has nothing to do with my account.

Announcement #9, from May, is even more direct:

Our target is service to developers, not provision of stable LLM service; if you need stability, go to OpenRouter or official APIs.

They told everyone what they were, months ago. I was debugging a documented product decision.

The evidence that settled it

One probe, same key, same minute:

  • glm-5.3 → 200
  • claude-opus-5, claude-opus-4-8, gpt-5.6-sol, gpt-6-astra → 402
  • deepseek-v4-flash → 403 on key one, 200 on key two

A real outage doesn't sort itself by model family and then serve one key but not another on the same model. That pattern is rationing plus a per-token authorization difference, and it took one request to see it.

The fix was a distinction, not a repair

There was nothing to repair. What was missing was a way to tell two states apart that produce identical-looking failure:

  • Rationed — some models serve, the default one doesn't, and it'll come back on a schedule.
  • Broken — nothing serves.

So the dashboard now probes a fallback set when the default model 402s, and reports partial with a message naming which models are still live. A platform showing partial is rationing by design. A flat error is a real outage. That's the whole change, and it's worth more than any retry logic I could have written, because retry logic against a daily batch is just a way to burn the next batch faster.

Why this is a UI problem now

Someone on Hacker News today is rendering Claude and Codex rate limits as AIM away messages. I have a menu-bar app whose entire job is showing me every AI coding limit I'm subject to, and I look at it more than I look at most dashboards I've built.

Both of those are the same instinct, and the instinct is correct. Rate limits stopped being exceptional and became ambient. When a constraint fires every single day on a posted schedule, it isn't an incident — it's a level in a tank, and you want it on screen before you start work, not in a stack trace after.

The mistake underneath my wasted day was treating a status code as a diagnosis. 402 means payment required. It does not tell you whose payment, or whether payment is even the subject. Here it meant "a shared pool you don't own has been drained by people who woke up earlier than you," which no HTTP status will ever carry.

Read the provider's own announcements before you read your own logs. It cost me a day to learn that the answer was on a public endpoint the whole time, sitting there with no authentication in front of it.