Skip to content

[Feature]: OpenRouter — automatic key rotation & model failover when the free-tier quota is exhausted #4024

Description

@nordz0r

Area

Provider adapters

What are you trying to accomplish?

Use OpenRouter free-tier models (openrouter/minimax/minimax-m3:free, openrouter/openrouter/free, etc.) behind a combo/failover target without manual intervention when a key's weekly free quota is exhausted (OpenRouter replies with 429 rate_limit_error, Weekly/Monthly Limit Exhausted... will reset at <date>).

What prevents this today?

OCX already has two related mechanisms:

  1. apiKeyPool multi-key failover (src/providers/key-failover.ts): on a 429, rotateKeyOn429() moves to the next key in the pool and puts the exhausted key into cooldown (default 60 s, cap 10 min, Retry-After respected).
  2. Combo failover across targets: a combo can fail over to another provider/model.

The gap for the OpenRouter free-tier case specifically:

  • The free-quota reset horizon is weekly (Your limit will reset at 2026-09-09 03:30:06), but MAX_COOLDOWN_MS caps a key's cooldown at 10 minutes. After 10 minutes the exhausted key becomes active again, and roughly every other request goes to a dead key, failing with 429 and only then rotating — the wasted attempt repeats all week until the quota actually resets.
  • The OpenRouter error body carries the exact reset timestamp (will reset at <date>), but the cooldown logic appears to only honor the Retry-After HTTP header; the body-parsed reset date is not used to extend the cooldown.
  • There is no per-key quota-state notion (e.g., exhausted_until: <ts>), so an exhausted key cannot be parked for the rest of the window the upstream itself declares.

What should OpenCodex do?

  1. Parse the OpenRouter 429 JSON body for rate_limit_error and extract the reset timestamp from the message; park the key in cooldown until that timestamp (bounded, e.g. up to 7 days, configurable).
  2. Optionally: a generic quotaExhaustedCooldownMs / quotaExhaustedUntilReset: true knob under retryOn429 (or a sibling policy) so other providers that report reset windows in the error body get the same behavior.
  3. With a single key in the pool, the same detection should feed combo-level failover: mark the target as quota-exhausted so the combo routes to the next target for the remainder of the window instead of burning one failed attempt per request.

Example usage or interface

{
  "providers": {
    "openrouter": {
      "authMode": "key",
      "apiKeyPool": [
        { "id": "key-a", "key": "sk-or-..." },
        { "id": "key-b", "key": "sk-or-..." }
      ],
      "retryOn429": {
        "enabled": true,
        "quotaResetFromBody": true,
        "maxQuotaCooldownMs": 604800000
      }
    }
  }
}

Expected behavior: after the upstream answers 429 Weekly/Monthly Limit Exhausted ... will reset at 2026-09-09 03:30:06, key-a is parked until that timestamp and all traffic goes to key-b (or the combo's next target) without per-request 429 tax.

Alternatives or workarounds

  • Manually pausing/removing the exhausted key in the dashboard until the reset date.
  • Using a short combo retry budget so every request pays one 429 before failing over (current de-facto behavior; wasteful on long windows).
  • External cron that swaps keys in the config when the weekly reset hits.

Checks

  • I searched existing issues and documentation.
  • This request describes a concrete OpenCodex workflow rather than merely naming a desired technology.
  • I removed secrets and personal data.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    account-poolOAuth, credentials, Codex pool, quota, failover, plansenhancementNew feature or requestproviderProvider adapters, OpenAI-compat presets, upstream API quirks

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions