Skip to main content

Quotamux

Route your coder to the subscription with the most quota left.

多模型多订阅额度调度器 —— 让任意 coder 自动落到余量最多的那个订阅上。

License: MIT


The problem

You pay for several AI coding subscriptions. Then this happens:

kimi-A     weekly  7% left   ← burned out on Tuesday
kimi-B     weekly 46% left
minimax-1  weekly 73% left
minimax-2  weekly 99% left   ← never touched

One subscription is exhausted while another sits idle, because nothing tells your tooling which pool still has room. You find out when a job dies with a 429.

Worse, the usual reflex is to estimate usage from your own logs. Local logs are a proxy: they miss calls that bypass them, their accounting differs from the provider's, and your own grouping code can be wrong. Every conclusion built on a proxy inherits its bias. (Ask us how we know — a full day of modelling, all of it wrong.)

What it does

Asks each provider how much is actually left, then hands your coder the environment for the pool with the most quota remaining — not merely one that is non-empty. "Non-empty is good enough" is exactly how one subscription burns out while another idles.

$ quotamux
池                       周剩      窗剩  重置
🔴kimi-A                  7%     99%  2026-08-04 00:39
🟢kimi-B                 46%    100%  2026-08-04 05:25
🟢minimax-1              73%     98%  2026-08-03 01:00
🟢minimax-2              99%    100%  2026-08-03 01:00 ←选它
🟡claude-max             22%     86%  2026-07-31 23:59
# Works with ANY coder — quotamux only emits environment variables
eval "$(quotamux --export)" && claude -p "fix the failing test"
eval "$(quotamux --export)" && aider --message "..."
eval "$(quotamux --export)" && your-own-agent

Install

pip install quotamux        # or: pipx install quotamux

Configure

Providers ship built in. Declare your subscriptions in ~/.quotamux/config.yaml (YAML or JSON). You only ever write environment-variable names — never secrets:

providers:
  kimi:
    subscriptions:
      # Multiple keys under one subscription share ONE quota pool.
      # (Verified: same-account keys return byte-identical usage.)
      # So rotating to another key of the same account when throttled is a no-op.
      - name: "kimi-team"
        keys: [KIMI_CODE_API_KEY, KIMI_CODE_API_KEY_1, KIMI_CODE_API_KEY_2]
      - name: "kimi-personal"
        keys: [KIMI_CODE_API_KEY_5]

Point elsewhere with QUOTAMUX_CONFIG=/path/to/config.yaml.

Supported providers

Provider Weekly Rate window Usage endpoint
Kimi Code {base}/usages
MiniMax /v1/token_plan/remains
Anthropic (Claude Max/Pro) /api/oauth/usage
OpenAI ⚠️ config only, untested /v1/organization/costs
DeepSeek pay-as-you-go, no pool

Two of these endpoints are not in any public documentation. We found them by reading the vendors' own clients — Kimi's from its CLI source, Anthropic's from strings on the Claude Code binary. If a dashboard can show a number, something serves that number; follow the signpost back to the machine.

Add a provider — config only, no code

Adding a vendor means adding a block to your config. Two parse modes cover every provider we have met:

providers:
  acme:
    display: ACME AI
    usage:
      url: "https://api.acme.example/v1/quota"
      auth: bearer                    # or: x-api-key
      headers: {api-version: "2026-01-01"}
      parse:
        mode: ratio                   # vendor returns absolute remaining/limit
        week:   {remaining: "quota.remaining", limit: "quota.limit", reset: "quota.reset_at"}
        window: {list: "windows", remaining: "detail.remaining", limit: "detail.limit"}
    lane:
      base_url: "https://api.acme.example/anthropic"
      model: "acme-1"
      auth_env: ANTHROPIC_AUTH_TOKEN
    subscriptions:
      - name: "acme-seat-1"
        keys: [ACME_KEY_1]
mode when the vendor returns fields
ratio absolute remaining / limit week.{remaining,limit,reset}, window.{list,remaining,limit}
percent remaining percent select.{path,where}, week.{pct,reset_ms}, window.{pct}
percent_used used percent (utilization) week.{used_pct,reset}, window.{used_pct}

Declare the models a provider serves so --model can find it:

    models:
      - {id: "acme-1", aliases: [acme, a1]}
      # usage_row: only when the vendor reports quota per model — names the row to read
      - {id: "acme-turbo", aliases: [turbo], usage_row: turbo}

A test in this repo proves the claim: a fully fictional provider that appears nowhere in the source runs end to end from config alone.

"Most quota left" — three levels

All three levels pick the emptiest-loaded pool, never just "one that still has something". What changes between levels is which pools are eligible, and that depends on what the task needs:

quotamux --pick                 # any model will do → most weekly quota left, full stop
quotamux --model opus --pick    # needs this model  → only pools that serve it
quotamux --model k3,m3 --pick   # either is fine    → pools serving k3 OR m3,
                                #                      then the emptiest across vendors

The third form is the one you want most days: "this job runs fine on Kimi or MiniMax". It keeps the job runnable (right model) and stops one subscription burning out while another idles (most quota left). Order expresses preference — with --model k3,m3, a pool serving both gets k3.

Where a vendor reports quota per model (MiniMax returns one row per model), the per-model figure wins. A subscription can look 90 % free overall while the model you need is down to 5 %.

Spread: one seat per vendor (--spread N)

Greedy --pick has a blind spot: dispatch three review agents in a row and all three land on the same emptiest pool — diverse prompts, same model. For cross-checking work (adversarial review, multi-model panels, N-version generation) model diversity is the point, and quota greed quietly destroys it.

quotamux --spread 3                 # 3 pools, all different vendors, emptiest first
quotamux --spread 2 --model k3,m3   # per line: provider:subscription + model id

Within each vendor the best pool still wins (window-eligible, most weekly left); vendors are then ranked by quota and the top N returned — one line each, so a dispatcher can readarray and pin one seat per line. If fewer than N distinct vendors qualify you get fewer lines (and a note on stderr) — it never pads with a second pool from the same vendor, because that would look heterogeneous while reintroducing the very bias you asked it to remove.

Selection rule

  1. If --model is given, drop pools that do not serve it. Weekly quota is worthless on a pool that will not run your model.
  2. A pool is eligible when its tightest rate window has ≥ --min-window percent left (default 15). Weekly quota is useless if you are about to get throttled.
  3. Among eligible pools, pick the one with the most quota remaining — per-model if the vendor reports it, otherwise subscription-level. This is the rule that stops one subscription burning out while another idles.
  4. Pay-as-you-go pools are excluded unless you pass --allow-metered — spending money should be an explicit decision.
  5. Nothing eligible → exit code 1. Queue and wait for the window. Buying another subscription rarely fixes what is a scheduling problem.

Safety

  • Secrets are never printed by quotamux, --pick or --json. Only --export emits keys — that is its entire job — and it writes them to stdout so you can eval them. Do not pipe --export into logs or CI output.
  • Config holds environment-variable names, never key material.
  • Native subscriptions (Claude Max OAuth) emit no environment at all. Injecting a base URL or bearer token there downgrades OAuth and kills the subscription channel — a mistake we made in production so you do not have to.

Notes

  • Queries run concurrently and are cached for 60 s (--fresh to bypass), so launching a batch of workers does not hammer the usage endpoints.
  • quotamux --json is stable machine output; build your own policy on top of it.

License

MIT — see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

quotamux-0.2.0.tar.gz (26.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

quotamux-0.2.0-py3-none-any.whl (21.2 kB view details)

Uploaded Python 3

File details

Details for the file quotamux-0.2.0.tar.gz.

File metadata

  • Download URL: quotamux-0.2.0.tar.gz
  • Upload date:
  • Size: 26.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for quotamux-0.2.0.tar.gz
Algorithm Hash digest
SHA256 4ba0a032d40e1917fa568d3f69438085061f3d7db57c199db4f983bd1cb6d08d
MD5 13f98e196aefd0875df01a9851bf8c49
BLAKE2b-256 606eb77982cc9888d5d753912909a1c3c8e33cddd504084fb9b7a7b5cc31d171

See more details on using hashes here.

Provenance

The following attestation bundles were made for quotamux-0.2.0.tar.gz:

Publisher: release.yml on yandie-AI/quotamux

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file quotamux-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: quotamux-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 21.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for quotamux-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 04764acaa6de2844578d159acee992cbbbf6e5b9753279d53716972a27ca34c9
MD5 00fd9ce15ebbd09ae82a51ea1a09a380
BLAKE2b-256 ff404df40ae16c8ca589217515db3ec2a8ccce32428aa3a0d3a7133d9ba239f9

See more details on using hashes here.

Provenance

The following attestation bundles were made for quotamux-0.2.0-py3-none-any.whl:

Publisher: release.yml on yandie-AI/quotamux

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page