feat(claude): scope overage/credit rejections to the requested model (subscription-only toggle)

Author: hank-warrenCreated Sep 17, 2026Updated Sep 17, 2026
LabelsFixed

Summary

On a Claude OAuth credential where usage credits (overage) are enabled but the org spend cap is reached, a 429 for a model that can only be served from overage cools down the entire credential for the upstream Retry-After (33.7h in my case), even though the same credential is still healthy and serving on its included subscription allowance.

I'd like a toggle to keep CPA on subscription allowance only — never spending usage credits, and never generalizing an overage-only rejection to the whole credential.

Version: v7.3.6 (Docker eceasy/cli-proxy-api:v7.3.6).

What happens

A claude-fable-5-1 request landed on a team-plan credential. Fable on that org can only be served from overage, so Anthropic returned 429 with these unified headers:

Anthropic-Ratelimit-Unified-Status                  = rejected
Anthropic-Ratelimit-Unified-Representative-Claim    = seven_day_overage_included
Anthropic-Ratelimit-Unified-7d-Status               = allowed        <-- included weekly is fine
Anthropic-Ratelimit-Unified-7d-Utilization          = 0.69
Anthropic-Ratelimit-Unified-5h-Utilization          = 0.00
Anthropic-Ratelimit-Unified-7d_oi-Status            = rejected
Anthropic-Ratelimit-Unified-7d_oi-Utilization       = 1.02
Anthropic-Ratelimit-Unified-Overage-Status          = rejected
Anthropic-Ratelimit-Unified-Overage-Disabled-Reason = org_spend_cap_reached
Retry-After                                         = 121180

CPA recorded a model-scoped cooldown for claude-fable-5-1 (correct) and a credential-level next_retry_after 33.7h out, which set unavailable: true on the credential. From GET /v0/management/auth-files:

{
  "account": "…", "unavailable": true, "status": "error",
  "next_retry_after": "2026-09-19T09:00:13+08:00",
  "cooldowns": [{ "scope": "model", "model_key": "claude-fable-5-1", "reason": "quota", "http_status": 429 }]
}

The credential then served no model at all — Opus and Sonnet included — for the full window.

Why that's wrong here

I called api.anthropic.com/v1/messages directly with the same credential while CPA had it benched:

POST /v1/messages  model=claude-opus-4-5-20251101  ->  HTTP 200
  anthropic-ratelimit-unified-status               = allowed
  anthropic-ratelimit-unified-representative-claim = five_hour
  anthropic-ratelimit-unified-7d-status            = allowed   (0.69 utilization)
  anthropic-ratelimit-unified-overage-status       = rejected  (org_spend_cap_reached)

The representative claim — and therefore the unified status — depends on which window the requested model draws from. For an overage-only model it is seven_day_overage_included and rejected; for a subscription-served model it is five_hour and allowed. So a credential-level Retry-After taken from an overage-only rejection does not describe the credential.

Requested behavior

Either or both, off by default:

  1. claude.model-level-cooling — the Claude counterpart of the existing codex.model-level-cooling: scope Anthropic quota cooldowns to the requested model rather than cooling the credential across sibling models.

  2. A "subscription only / no usage credits" toggle (e.g. claude.disable-usage-credits), which:

    • treats a rejection whose Overage-Status: rejected while 5h-Status/7d-Status are allowed as model-scoped, not credential-scoped;
    • optionally declines to route a request to a credential when the model would have to draw on overage.

A narrow, header-driven rule would cover the case without a new setting at all: if Unified-Status: rejected but the non-overage windows (5h, 7d) report allowed, and Overage-Status: rejected, apply the cooldown to the requested model only. That is exactly the information Anthropic already returns, and CPA already parses it into quota.signals.

Workaround

Per-credential excluded_models in the auth JSON (PATCH /v0/management/auth-files/fields) keeps overage-only models off the affected credential, so the rejection never happens. That works, but it requires knowing in advance which models a given org can only serve from credits, and it has to be maintained per credential as the model list changes.

Source: router-for-me/CLIProxyAPI