Skip to main content

sub-pool-client

Python client + interactive CLIs for a sub-pool server — a credential broker for N Claude and OpenAI Codex subscription accounts.

You get three things in one package:

  1. sp-claude — a claude CLI wrapper. Leases a Claude account from the pool, runs the real claude binary against a per-session CLAUDE_CONFIG_DIR, rotates the access_token in the background, and swaps accounts transparently if the leased one cools mid-run.
  2. sp-codex — same flow for OpenAI Codex. Leases a Codex auth.json, drops it into an isolated CODEX_HOME, spawns the real codex binary, and rotates the token in the background.
  3. PooledClient / PooledCodexClient — Python SDK classes for programs and services. PooledClient is a drop-in subclass of ClaudeSDKClient from the official claude-agent-sdk; PooledCodexClient is a thin async wrapper around codex exec.

The agent loop — tool execution, filesystem I/O, hooks, MCP servers, the codex subprocess — all stays on the consumer's machine. The pool only sees lease lifecycles.

Install

pip install sub-pool-client
# or
uv pip install sub-pool-client

PyPI

Latest release tracks main. To install from source instead:

pip install git+https://github.com/subs-pool/sub-pool-client

Environment

Both the CLIs and the SDK read:

export SUB_POOL_URL=http://your-pool-host:8787
export SUB_POOL_KEY=cp-...   # admin issues this from the sub-pool UI

SUB_POOL_KEY's strategy decides which accounts you can reach.

1. CLIs

sp-claude — claude wrapper

sp-claude --setup            # one-time interactive config wizard
sp-claude                    # start the claude REPL on a leased account
sp-claude "explain this"     # single-prompt mode
sp-claude --account alice    # pin to a specific account
sp-claude --status           # show effective config and exit

sp-claude --setup writes pool URL + API key to ~/.sub-pool/cli.toml at 0o600. Subsequent invocations exec claude against a per-session config dir that holds the leased .credentials.json. The persistent home (~/.sub-pool/claude-home/) keeps your conversation history across sessions and is isolated from ~/.claude/ — sp-claude never reads or writes your real claude config.

If the leased account starts cooling (Anthropic rate-limit or quota window), a background watcher swaps to a different account transparently. Your running session sees one continuous claude process; only the access_token under the hood changes.

sp-codex — codex wrapper

sp-codex --setup
sp-codex                     # interactive REPL on a leased Codex account
sp-codex exec "summarize ."  # one-shot
sp-codex --account codex1

Shares ~/.sub-pool/cli.toml with sp-claude. Persistent CODEX_HOME lives at ~/.sub-pool/codex-home/.

2. Python SDK

PooledClient (Claude)

import asyncio
from claude_agent_sdk import ClaudeAgentOptions
from sub_pool_client import PooledClient

async def main():
    options = ClaudeAgentOptions(system_prompt="Be terse.")
    async with PooledClient(options=options) as client:
        print(f"leased {client.account} lease_id={client.lease_id}")
        await client.query("List three prime numbers.")
        async for msg in client.receive_response():
            print(type(msg).__name__, "->", msg)

asyncio.run(main())

PooledClient subclasses ClaudeSDKClient, so every option the SDK accepts (hooks, MCP servers, cwd, allowed_tools, permission_mode) works unchanged.

Rate-limit handling

from claude_agent_sdk import ClaudeSDKError

async with PooledClient(options=options) as client:
    try:
        await client.query("...")
        async for msg in client.receive_response():
            ...
    except ClaudeSDKError as e:
        if "429" in str(e) or "rate" in str(e).lower():
            await client.report_error("RateLimit", str(e))
        raise

report_error posts to /credentials/lease/{id}/report-error; the pool marks the account COOLING so the next lease skips it.

Concurrent agents

Multiple async with PooledClient() calls in the same process — or across sibling processes — with identical routing inputs (user_id, required_model) share a single lease. Coordination happens through ~/.sub-pool/client/<hash>/ (flock + holder list). First arriver leases; later arrivers refcount; last out releases. So N parallel agents on one account work up to Anthropic's per-account concurrency limit. Vary user_id (with the sticky_user strategy) to force different accounts in parallel.

PooledCodexClient (Codex)

import asyncio
from sub_pool_client import PooledCodexClient

async def main():
    async with PooledCodexClient() as codex:
        proc = await codex.exec(
            "summarize this repository",
            cwd=".",
            model="gpt-5.5",
        )
        stdout, stderr = await proc.communicate()
        print(stdout.decode())

asyncio.run(main())

PooledCodexClient writes the leased (sanitized) auth.json into an isolated CODEX_HOME and spawns codex exec against it. A background poll task rotates the on-disk auth.json before the access_token expires. On a backend error, call await codex.report_error("RateLimit", "...") so the pool cools the account.

Pool invariants this client relies on

  • The pool exclusively owns the refresh_token chain (both Anthropic and OpenAI). Leases hand out only the access_token; the refreshToken field in the leased credentials file is blank.
  • Token rotation happens through POST /credentials/lease/{lease_id}/token — a consumer crash can never leak the pool's refresh state.
  • account_names come from the pool's admin config. The pool decides which account fulfills each lease based on the API key's strategy.

The sub-pool server (admin UI, strategy reference, account lifecycle) is hosted separately — point this client at whatever pool URL your admin gave you via SUB_POOL_URL above.

Layout under ~/.sub-pool/

~/.sub-pool/
├── cli.toml                 # sp-claude / sp-codex shared config (0o600)
├── claude-home/             # sp-claude persistent CLAUDE_CONFIG_DIR
├── codex-home/              # sp-codex persistent CODEX_HOME
└── client/<hash>/           # PooledClient / PooledCodexClient
                             #   cross-process lease coordination

Override the SDK shared-dir root with SUB_POOL_CLIENT_DIR.

Examples

  • examples/hello.py — PooledClient end-to-end with a retry loop on 429.
  • examples/codex_hello.py — PooledCodexClient running codex exec.

License

(TBD — defer to upstream)

Release files for sub-pool-client 0.8.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sub-pool-client 0.8.2
File Size Uploaded
sub_pool_client-0.8.2.tar.gz 45.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for sub-pool-client 0.8.2
File Interpreter ABI Platform
sub_pool_client-0.8.2-py3-none-any.whl Python 3 none any Details

Total release size: 101.2 kB

Release files / sub_pool_client-0.8.2.tar.gz

Download URL sub_pool_client-0.8.2.tar.gz
Size 45.5 kB
Tags Source
SHA-256 checksum
How to use checksums
a8a582fb04a229578cd57b8dc7fd42cc3ee34ee83a7e59b8c7453104c2bf3b8c
BLAKE2b-256 checksum
How to use checksums
ad5fa47b1ae373e4c1f3b2dd29cd206de531b4ee2d35a1c7168662a4aa9dd504
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 26, 2026.

Transparency log

Release files / sub_pool_client-0.8.2-py3-none-any.whl

Download URL sub_pool_client-0.8.2-py3-none-any.whl
Size 55.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
66c980a36674138089ea14ac6c0fc01903ae511adf1b78fb406cca05e4e25c41
BLAKE2b-256 checksum
How to use checksums
8e4fa44a0fb2d264a76a0379bb451b8617aeec58e781050d9bc64ac0a279fcf5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 26, 2026.

Transparency log

Release history Release notifications | RSS feed

0.9.0

2 release files

This release

0.8.2 This release

2 release files

0.8.1

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.9

2 release files

0.6.8

2 release files

0.6.7

2 release files

0.6.6

2 release files

0.2.0

2 release files

0.1.2

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page