Skip to main content

Engineering

How Power Claude's Account Rotator Works: Proxy Core and Watchdog

Power Claude account rotator architecture: proxy core and watchdog
Organize profiles locally — manual selection for new sessions; each job stays on its originating profile.

Power Claude is two pieces of code: a local proxy between Claude Code and Anthropic's API, and a watchdog process that observes session lifecycle events. The combination is what produces "rotation that you never see." This is the engineering breakdown — how the proxy decides where each request goes, how the watchdog detects the signals the proxy uses, and the anti-ping-pong logic that turns a naive pool into a working one.

The proxy core — what sits between Claude Code and Anthropic

The proxy is a small HTTP server running on a local loopback port (typically 127.0.0.1:18450, configurable). When the extension activates, it modifies the Anthropic SDK's base URL inside the Claude Code process so that all API traffic targets the proxy instead of api.anthropic.com directly.

The proxy's responsibilities, in execution order per request:

  1. Receive the request from Claude Code.
  2. Identify which pooled account should serve it.
  3. Forward the request to api.anthropic.com with the chosen account's credentials substituted in.
  4. Stream the response back to Claude Code unchanged.
  5. Parse X-RateLimit-* headers from the response and persist them per account.

Two of those steps are where the interesting work lives — step 2 (account selection) and step 5 (state capture). The other three are mechanical.

The proxy does not buffer requests. It streams. A long completion that takes thirty seconds to generate streams through the proxy in real time. The latency overhead per request is sub-millisecond on a local connection. Claude Code does not notice the proxy is there.

The budget-aware router

Step 2 — account selection — is where naive implementations fail. Round-robin "next account in the list" routing produces ping-pong: route to account A, A is hot, account A returns 429, route to account B, account B is also hot, return 429 to Claude Code. The pool has six accounts but the router never tried four of them because the position-in-list pointer never reached them.

A working router replaces "next account in list" with "account with most projected remaining budget for this request." The state needed:

  • Per-account, the most recent X-RateLimit-Remaining-Tokens, X-RateLimit-Remaining-Requests, X-RateLimit-Reset-Tokens, and X-RateLimit-Reset-Requests.
  • A per-account "in flight" counter — how many requests this account is currently mid-stream on.
  • An estimate of the current request's likely cost (input tokens are exactly known; output tokens are estimated from recent average).

The decision, condensed:

def pick_account(pool, estimated_cost):
    now = time.time()
    candidates = []
    for account in pool:
        # Refresh known budget against reset times
        tpm_remaining = account.remaining_tokens
        if now > account.tpm_reset_at:
            tpm_remaining = account.limit_tokens
        rpm_remaining = account.remaining_requests
        if now > account.rpm_reset_at:
            rpm_remaining = account.limit_requests
        # Subtract in-flight requests' projected cost
        in_flight_tokens = sum(req.estimated_cost for req in account.in_flight)
        usable_tpm = max(0, tpm_remaining - in_flight_tokens)
        if usable_tpm > estimated_cost and rpm_remaining > 0:
            candidates.append((account, usable_tpm))
    if not candidates:
        return None  # Pool exhausted, queue
    return max(candidates, key=lambda x: x[1])[0]

The router runs this on every request. The state is small — kilobytes for a pool of twenty accounts. The lookup is sub-millisecond.

When no candidate exists — every account is over-committed — the router queues the request behind whichever account will free up soonest (smallest X-RateLimit-Reset-* time). The queue is brief; Anthropic's reset times for TPM are within the current minute, for RPM the same.

Sticky routing per conversation

The router cannot pick a fresh account on every turn within a single conversation. The prompt cache discount depends on the same conversation hitting the same account across turns. If turn 1 lands on account A and the cache is built for account A, sending turn 2 to account B requires re-building the cache at full token cost.

The sticky-routing rule:

  • A new conversation lands on the budget-best account at first contact.
  • Subsequent turns of the same conversation stay on the same account.
  • The conversation can move only if the sticky account caps mid-flight — at which point the router picks the next-best account by projected budget, and the new account becomes the sticky one for the remainder of the conversation.

The "conversation" identity comes from Claude Code's X-Conversation-Id or equivalent session marker (the exact mechanism varies by Claude Code version). The proxy keys its sticky-routing table by this identity.

The result is that long-running conversations rarely move accounts. The accounts in the pool absorb conversations as they start and serve them through completion. Account A might handle the morning's interactive coding session while account B handles the autonomous lint loop running in parallel.

The watchdog — observing what the proxy cannot see

The proxy sees API traffic. It does not see Claude Code's internal events — TodoWrite updates, auto-compact runs, session lifecycle events, the editor exiting. Those signals live in the JSONL transcripts and in the Claude Code hook system.

The watchdog is a separate process (running in the same VS Code extension host) that subscribes to:

  • PostToolUse for TodoWrite — every time the agent updates its todo list, capture the new list keyed by session ID.
  • PreCompact — before auto-compact runs, snapshot the pre-compact context. The compact event is one of the four crash signatures and the only one the proxy cannot see.
  • Stop — fires when a turn ends normally. Updates the heartbeat.
  • SessionStart and SessionEnd — track session lifecycle directly.
  • A JSONL file watcher — detects writes to active session transcripts, mtime updates, and orphan tool_use blocks that indicate crashes.

The watchdog persists captured state to a local store (under ~/.power-claude/sessions/). On editor restart, the watchdog reads back the most recent state per session and offers one-click resume.

The watchdog also feeds the proxy. When the watchdog sees a 429 record in the JSONL — or a PreCompact event — it tells the proxy. The proxy uses this to update its own per-account state more aggressively than waiting for the next request's X-RateLimit-* headers. The combination of header-derived state (from the proxy) and event-derived state (from the watchdog) is what produces the "rotation that happens before you see the 429."

Anti-ping-pong logic — the structural detail

The simplest failure mode of a pool router is ping-pong between two near-capped accounts. The mechanism:

  • Account A's TPM remaining is 5K. Account B's is 6K. Both are below the typical per-turn cost.
  • Request 1 needs 8K. Router picks B (more remaining). B returns 429.
  • Router falls back to A. A returns 429.
  • Both accounts are marked over-budget. The router has no candidate.

The right behavior in this state is to queue the request behind whichever account's TPM reset time is soonest, not to ping-pong. The implementation:

def serve_request(req, pool):
    account = pick_account(pool, req.estimated_cost)
    if account is None:
        # No candidate fits. Find soonest-reset account.
        soonest = min(pool, key=lambda a: a.tpm_reset_at)
        wait = soonest.tpm_reset_at - time.time()
        if wait < MAX_QUEUE_WAIT:
            queue_until(req, soonest.tpm_reset_at)
            account = soonest
        else:
            return upstream_429(req)
    response = forward(req, account)
    return response

The queue is short — usually under a minute, sometimes seconds. From Claude Code's perspective, this looks like a slightly slower turn rather than a 429. The session continues. The TodoWrite list does not get interrupted.

When the wait would be too long (configurable, default 60 seconds), the router gives up and returns the 429 upstream. At that point Claude Code's normal Retry-After handling kicks in, and the watchdog records the event for the user to see in the rotation pane.

Pre-emptive rotation — the headline feature

The combination of header-derived state and event-derived state produces a useful property: the proxy can rotate accounts before they hit a 429.

The mechanism:

  • After each response from account A, the proxy knows A's remaining TPM and RPM for the current minute.
  • The proxy estimates the cost of the next request (input is known, output is estimated).
  • If A's remaining budget is less than the projected cost plus a small safety margin, the next request routes to a different account.
  • A's budget continues to recover; B serves the request; no 429 is ever generated.

The user-visible effect is that 429s become rare. The router has been silently moving traffic around in anticipation. The first time a developer notices is when the status bar shows the active account changing mid-session even though no error has surfaced.

The safety margin matters. Too small and you generate 429s anyway. Too large and you stop using account A long before its budget is actually exhausted. The default is 15% of the per-minute cap, calibrated against observation. Users can tune it per deployment.

This is the part most people never wire up themselves, because it reads like infrastructure work — which is exactly why we ship it as a one-line install. The budget-aware router makes the rotation call for you on every request; it's a free download to try, no credit card. If you'd rather see the tiers first, the 14-day Premium Trial is $0 today and you can cancel anytime before day 15.

Frequently asked questions

Does the proxy ever see my prompt content?

The proxy is a forwarder, not a content filter. Prompts pass through it as opaque streams from Claude Code to api.anthropic.com. The proxy reads only the routing-relevant headers and the X-RateLimit-* response metadata. Prompt and response bodies are not logged, parsed, or stored.

What happens if the proxy crashes mid-session?

Claude Code's API calls fail until the proxy restarts. The watchdog supervises the proxy and restarts it on crash within a few seconds. During the gap, in-flight requests fail with a network error — Claude Code's retry logic handles it. The session does not die.

Can I run the proxy without the watchdog?

Yes. The proxy is independent of the watchdog. You can run rotation-only without session-resilience tracking. The watchdog is what handles crash recovery and TodoWrite preservation; the proxy handles routing.

Why doesn't the proxy just pre-emptively swap on every turn?

Per-turn account swapping breaks the prompt cache. Each turn on a new account is a fresh cache build, which costs more in tokens than the rotation saves. Sticky routing per conversation is the right granularity — swap only when the sticky account is genuinely capping out.

What if my pool has only one account?

The proxy still runs and the watchdog still works. The router has no routing decision to make — every request goes to the only account. The benefit reduces to session-resilience and cost analytics; rotation is a no-op. The 14-day Premium Trial covers this use case as well — rotation with a single account still benefits from the watchdog and cost analytics.

What developers are reporting

This is the feature developers keep asking for by name. GitHub issue #20131 — one of the most up-voted in the repo — asks that "when hitting usage limits on one account, users would like to seamlessly switch to another account." Seamless, pre-emptive switching across your own pool is precisely what the rotator does.

Closing

The proxy and the watchdog together are what produce the "rotation that you never see." Each piece is small. The composition is what matters. This architecture, productized, ships as a VS Code extension, with the budget-aware router and the watchdog both running quietly while Claude Code does the work.

If you want to run it against a pool of your own, the 14-day Premium Trial is $0 today and you can cancel anytime before day 15 — it adds the budget-aware router, the watchdog, and usage analytics on top of rotation. Not ready to put a card down? Install Power Claude Free instead: the 7-day free trial gives you full Pro access, no credit card.