pool-anything logopool-anything Docs

GETTING STARTED

Overview

pool-anything is an agentic-code-tool-style helper for API keys: it reads your provider list, gathers keys into pools, rotates them, and integrates with your development tools. Available on your machine, behind one local endpoint, with a browser UI.

pool-anything is an AI-powered quota assistant that helps you build features, fix bugs, and automate development tasks. It understands your entire key inventory and can work across multiple providers and tools to get things done.

Get started

pool-anything runs as one server: the web UI, the JSON API, and the proxy. Choose how you run it below to get started. Most setups require a Node.js 22+ subscription or a local checkout. The terminal, Docker, and source builds all support custom providers.

Global install (recommended) — macOS, Linux, WSL, PowerShell, CMD:

npm i -g pool-anything # one-time install (server core: 0 deps; shell: Ink + React)
pool-anything           # → assistant shell (list pools, add keys…)
pool-anything serve     # → http://localhost:3000, data in ~/.pool-anything/

From source — macOS, Linux, WSL:

npm install # dev deps only: tsx, typescript
npm run dev     # http://localhost:3000, reloads as you edit

From source — Windows PowerShell:

npm install; npm run dev  # → http://localhost:3000

From source — Windows CMD:

npm install && npm run dev

Check it's alive:

curl localhost:3000/health        # → ok
curl localhost:3000/api/db/ping   # → {"ok":true,…}

Docker (maps the SQLite volume):

docker run -p 3000:3000 -v ./data:/app/data pool-anything

Docker Compose override:

PORT=3000 docker compose up

Compiled output:

npm run build && npm run start:dist

Init the database directly:

npm run db:init -- --seed

If you see The token '&&' is not a valid statement separator, you're in PowerShell, not CMD. If you see 'npm' is not recognized as an internal or external command, you're in CMD, not PowerShell. Your prompt shows PS C:\… when you're in PowerShell and C:\… when you're in CMD.

If the install command fails with syntax errors near unexpected token '<', a 403, or another curl error, see Troubleshoot installation to match the error to a fix and for alternative install methods.

36 providers ship preconfigured so pool-anything can use the registry auth style. If a provider is missing, setups do not need a code change: a pool with your own base_url works instead.

ⓘGlobal installs persist to ~/.pool-anything/pool-anything.db; source checkouts use data/pool-anything.db — so you stay on the latest cursor either way. Override with LOCAL_DB_PATH (or pool-anything --db).

You can also install with apt, dnf, or apk equivalents for Node 22 on Debian, Fedora, RHEL, and Alpine.

Then start pool-anything in any project. Replace your-project with the path to a project directory on your machine:

cd your-project
npm run dev

You'll be prompted to add keys on first run. If you've set the LOCAL_DB_PATH environment variable, pool-anything skips the default path and asks you to approve the key instead. That's it! Continue with the Quickstart →

✓See advanced setup for installation options, manual updates, or uninstallation instructions. Visit installation troubleshooting if you hit issues.

What you can do

Here are some of the ways you can use pool-anything:

Batch provider calls through one /proxy endpoint instead of juggling keys by hand. See the proxy.
Collect N keys under one named pool per provider; every call takes the next key via cursor % keys.length.
GET /api/pools/:id/next returns the next masked key and advances the cursor; POST /consume records tokens too.
Header, prefixed-header, and query:param injection are handled per provider. See injection table.
One JSON row adds a provider; per-pool base_url, key_header, key_prefix override at runtime.
Pools are independent rows — Groq, OpenRouter, Gemini side by side, each with its own cursor.
No SDK needed: curl -X POST localhost:3000/api/pools/1/proxy -d '{"path":"…"}'.
/usage rolls up SUM(tokens) with quota and remaining where the registry knows them.
One SQLite file, dependency-free core — copy data/pool-anything.db and the pools move with you.

Use pool-anything everywhere

Each surface connects to the same underlying pool engine, so your repo's pools, keys, and usage files, settings, and MCP servers work across all of them.

Beyond the Terminal and UI surfaces above, pool-anything integrates with scripted, chat, and browser workflows:

What I want to doBest option
Gather keys in a browser UIPools overview
Manage pools in plain languageAssistant shell: pool-anything, then list pools, add keys to groq, watch groq
Send one request with rotation + authProxy endpoint
Peek at the next key without spending itGET …/next
Start a task locally, continue on mobileREST everywhere
Run rotation on a recurring scheduleConsume + cron
Automate PR reviews and issue triageProxy in CI
Get automatic masked views on every keyKey manager
Route bug reports from Slack to pull requestsProxy + webhook
Build custom agents for your own workflowsRegistry + proxy

Next steps

Once you've installed pool-anything, these guides help you go deeper.


Quickstart — your first pool

  1. Open localhost:3000 and search a provider — say, groq.
  2. Click it. The setup dialog creates a pool for that provider.
  3. Paste an API key, press Enter. Repeat for every key you own.
  4. Press Done. The pool now appears on Pools and in the API key manager.
  5. Send traffic through it:
curl -X POST localhost:3000/api/pools/1/proxy \
  -H 'content-type: application/json' \
  -d '{"path":"/openai/v1/models","method":"GET"}'
ⓘWatch it turn: every proxy call returns the key_id and label that served it, so you can see keys take turns. Add "tokens": N to count usage too.

Prefer the terminal? The assistant shell does steps 1–4 without a browser: run pool-anything, then create pool Prod for groq and add keys to groq (paste one key per line, blank line finishes).

Run it

You need Node.js 22 or newer (the app uses the built-in node:sqlite module and native fetch). Nothing else — there are no runtime npm dependencies.

npm i -g pool-anything # one-time install
pool-anything           # assistant shell (list pools, add keys to groq, serve)
pool-anything serve     # http://localhost:3000 (--port, --open, --db, --data-dir)

Prefer source? npm install then npm run dev serves the same UI with file watching.

Scripts & environment

ScriptWhat it does
npm run devMain server on :3000 with file watching.
npm startMain server once, no watch.
npm run buildType-check and compile to dist/.
npm run start:distRun the compiled output.
npm run db:initCreate the local database (-- --seed adds a sample row).
PORTMain server port, default 3000.
HOSTInterface to bind, default 127.0.0.1 (loopback — the API has no auth).
PROXY_TIMEOUT_MSPer-attempt upstream timeout, default 30000.
ALLOW_PRIVATE_UPSTREAM1 disables SSRF protections — tests/loopback only, never in prod.
LOCAL_DB_PATHOverride where the SQLite file lives — default ~/.pool-anything/pool-anything.db for global installs, data/pool-anything.db in a checkout (pool-anything --db sets the same thing).
POOL_DATA_DIRData-directory alternative: <dir>/pool-anything.db (pool-anything --data-dir sets the same thing).
✓Local by default: the server binds 127.0.0.1 and the API has no auth — set HOST to another interface only behind your own auth layer.

How pooling works

Most providers hand each key its own small rate limit: requests per minute, tokens per day. pool-anything gathers N keys for the same provider into one pool and does four things with them: Pool, Rotate (cursor % keys.length), Proxy, and Track. It ships with 36 preconfigured providers and a JSON API for everything else.

Storage

One SQLite file through Node's built-in DatabaseSync. The schema creates itself at startup; totals are computed on read with SUM() — no counters to drift.

CREATE TABLE pools (id, provider, name, cursor, …);
CREATE TABLE pool_keys (id, pool_id, label, api_key, …);
CREATE TABLE usage (id, pool_id, key_id, tokens, …);

HTTP API

Base URL http://localhost:3000. Bodies and responses are JSON; errors are { "error": "…" }. List endpoints always mask keys — only the single-key view returns a raw value.

GET/api/providers

Every provider in the registry, with base URL, auth style, quota notes, docs link, and logo.

GET/api/pools

All pools, newest first: id, provider, name, cursor, base_url, key_header, key_prefix, created_at.

POST/api/pools

Create a pool. provider and name are required; overrides fall back to the registry when empty.

{ "provider": "groq", "name": "groq pool" }
GET/api/pools/:id

Pool summary with usage: key count, tokens used, quota and remaining, plus a per-key breakdown.

DELETE/api/pools/:id

Delete the pool with its keys and usage records.

Rotation endpoints

GET/api/pools/:id/next

Return the next key, masked, and advance the cursor. 400 if the pool has no keys, 404 if it doesn't exist.

{ "key_id": 2, "label": "key 2", "masked": "gsk_…9f" }
POST/api/pools/:id/consume

Rotate to the next key and record usage against it. tokens must be a positive integer.

GET/api/pools/:id/usage

Usage rollup: pool total, quota, remaining, and per-key sums.

The proxy

POST/api/pools/:id/proxy

Pick the next key, inject its credentials, forward the request, record usage, report back. path is appended to the pool's base URL; method defaults to POST.

{ "path": "/chat/completions", "method": "POST", "tokens": 24 }
ShapeInjectionSeen on
Authorization + prefixAuthorization: Bearer <key>Groq, OpenAI, OpenRouter
Plain header + prefix<name><prefix><key>x-api-key (Anthropic), xi-api-key (ElevenLabs)
query:<param>?<param>=<key>Mapbox, Google Maps, OpenWeather

Provider extras (like anthropic-version) merge in automatically. Network failures return 502 { "error": "upstream unreachable" }.

Key endpoints

GET/api/pools/:id/keys

List keys, masked: id, label, masked, info, created_at.

POST/api/pools/:id/keys

Add a key. Send label and api_key → { "id": 7 }.

GET/api/pools/:id/keys/:keyId

One key in full — the only endpoint that returns the raw value.

PATCH/api/pools/:id/keys/:keyId

Update any of label, api_key, info.

DELETE/api/pools/:id/keys/:keyId

Remove a key → { "ok": true }.

Also answering

GET /healthPlain-text ok liveness probe.
GET /api/db/pingDatabase path and row count.
GET /logos/<name>Provider logos, one-hour cache.

Project layout

One HTTP server, one SQLite file, one JSON registry.

pool-anything/
├── src/server.ts        # UI + JSON API + proxy (:3000)
├── src/pool/            # schema, providers, rotation
├── data/providers.json  # the registry
└── landing/             # this site

Provider registry

data/providers.json is a flat array. Malformed rows are filtered out; a built-in fallback applies if the file can't be read.

FieldMeaning
id · nameRegistry key and display name.
baseUrlUpstream root; the proxy appends the caller's path.
keyHeader · keyPrefixWhere the key goes — header plus prefix, or query:<param>.
extraHeadersAlways-sent headers, like anthropic-version.
quotaHuman-readable free-tier notes, shown in the UI.
poolabletrue by default, false for control-plane only.

Security

  • Keys are stored plaintext in SQLite; the file stays out of version control.
  • List endpoints mask keys. Only GET /keys/:id returns a raw value.
  • There is no auth on the API — localhost or behind your own gate.
  • Logo paths are validated; request bodies cap at 1 MB.
  • Upstream responses pass through as text, capped at 4000 chars — treat them as untrusted.