claude-cli-proxy
v1.3.0
Published
Run a local Anthropic- and OpenAI-compatible API server on top of your Claude Code subscription. No API key required.
Downloads
909
Maintainers
Readme
claude-cli-proxy
Turn your Claude Code subscription into a local API server.
Run one command and you get an HTTP endpoint on http://127.0.0.1:7274 that speaks both the Anthropic Messages API and the OpenAI Chat Completions API. Point any existing tool, SDK or agent framework at it and it works — no API key, no per-token billing. Requests are served by the Claude Code CLI you already have installed and logged in.
npx claude-cli-proxy claude-cli-proxy v1.0.0 · Claude Code 2.1.226
Endpoint http://127.0.0.1:7274
Anthropic http://127.0.0.1:7274/v1/messages
OpenAI http://127.0.0.1:7274/v1/chat/completions
API key not required — send "not-needed" where your SDK insists on one
Model claude-sonnet-5
✓ claude-sonnet-5 responded in 3.7s
Ready to go. Point your client at http://127.0.0.1:7274Then see it work:
claude-cli-proxy test # a real query, with the request that produced it
claude-cli-proxy test --stream # streaming, with time to first token
claude-cli-proxy examples # copy-paste client code for your languageRequirements
- Node.js 20 or newer
- Claude Code installed and signed in. Run
claudeonce in a terminal and complete/loginif you haven't.
That's it. This package has zero dependencies.
Install
npm install -g claude-cli-proxyThen start it:
claude-cli-proxyOr run it without installing:
npx claude-cli-proxyOn startup it picks a working model, makes one real test call, and tells you when it's ready. If something is wrong — not logged in, quota exhausted, CLI missing — it says so and tells you how to fix it.
About the API key
You don't need one. The server runs on your machine against a Claude CLI that is already signed in, so there is no credential to check.
Most SDKs refuse to start without something in the key field. Pass the string not-needed — it works, and it documents itself when someone reads your config later:
client = OpenAI(api_key="not-needed", base_url="http://127.0.0.1:7274/v1")If you do want a key enforced — sharing the port with another user, say — start with --api-key and one is generated and stored for you.
Use it
Every snippet below is also available live, with your actual URL filled in:
claude-cli-proxy examples pythoncurl
curl http://127.0.0.1:7274/v1/messages \
-H "x-api-key: not-needed" \
-H "content-type: application/json" \
-d '{
"model": "claude-sonnet-5",
"max_tokens": 256,
"messages": [{"role": "user", "content": "Explain HTTP caching in two sentences."}]
}'Anthropic SDK
from anthropic import Anthropic
client = Anthropic(api_key="not-needed", base_url="http://127.0.0.1:7274")
message = client.messages.create(
model="claude-sonnet-5",
max_tokens=256,
messages=[{"role": "user", "content": "Hello!"}],
)
print(message.content[0].text)OpenAI SDK
from openai import OpenAI
client = OpenAI(api_key="not-needed", base_url="http://127.0.0.1:7274/v1")
completion = client.chat.completions.create(
model="claude-sonnet-5",
messages=[{"role": "user", "content": "Hello!"}],
)
print(completion.choices[0].message.content)JavaScript
import OpenAI from 'openai';
const client = new OpenAI({
apiKey: 'not-needed',
baseURL: 'http://127.0.0.1:7274/v1',
});
const completion = await client.chat.completions.create({
model: 'claude-sonnet-5',
messages: [{ role: 'user', content: 'Hello!' }],
});LangChain
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="claude-sonnet-5",
api_key="not-needed",
base_url="http://127.0.0.1:7274/v1",
)
print(llm.invoke("Hello!").content)Anything else
Most tools accept a custom base URL. Set these and many will just work:
export ANTHROPIC_BASE_URL=http://127.0.0.1:7274
export ANTHROPIC_API_KEY=not-needed
export OPENAI_BASE_URL=http://127.0.0.1:7274/v1
export OPENAI_API_KEY=not-neededStreaming
Both endpoints stream. Set "stream": true and you get standard server-sent events in the format each API defines, so SDK streaming helpers work unchanged.
There is also a plain-text mode that skips the event envelope entirely and streams raw text — useful for piping into a terminal:
curl -N http://127.0.0.1:7274/v1/messages \
-H "x-api-key: not-needed" \
-H "accept: text/plain" \
-H "content-type: application/json" \
-d '{"model":"claude-sonnet-5","max_tokens":512,"stream":true,
"messages":[{"role":"user","content":"Write a haiku about ports."}]}'Models
| Model | Use --model value | Context in | Max out |
|---|---|---|---|
| Claude Opus 5 | claude-opus-5 or opus | 1,000,000 | 64,000 |
| Claude Sonnet 5 | claude-sonnet-5 or sonnet | 1,000,000 | 64,000 |
| Claude Haiku 4.5 | claude-haiku-4-5 or haiku | 200,000 | 32,000 |
Sonnet 5 is the default. Which models you can actually reach depends on your Claude plan — check with:
claude-cli-proxy models --probeIf your client hardcodes a model name you can't change (many OpenAI tools send gpt-4o), map it onto a real model instead of getting a 404:
CLAUDE_API_OPENAI_MODEL_FALLBACK=claude-sonnet-5 claude-cli-proxyResponses always report the model that actually ran.
Commands
claude-cli-proxy Start the server
claude-cli-proxy test Send a real query and show the request used
claude-cli-proxy examples Copy-paste client code, URL already filled in
claude-cli-proxy api Every parameter, and whether it has any effect
claude-cli-proxy doctor Check your environment and report problems
claude-cli-proxy models List models (--probe tests each with a real call)
claude-cli-proxy key Show the API key (--rotate to replace it)
claude-cli-proxy update Update to the latest version
claude-cli-proxy uninstall Remove it (--purge also deletes local state)
claude-cli-proxy reinstall Remove and install againccproxy is a shorter alias. Every command has a detailed page:
claude-cli-proxy help testTry it before you integrate
test runs a real query and prints the exact request that produced it, so you can copy the shape into your own client:
claude-cli-proxy test # single response
claude-cli-proxy test --stream # streaming, reports time to first token
claude-cli-proxy test --openai --stream # against the chat completions route
claude-cli-proxy test "your own prompt"
claude-cli-proxy test --url http://127.0.0.1:7274 # use a server already runningWhich parameters actually do anything
claude-cli-proxy apiLists every parameter both endpoints accept, marked honoured (passed to the model), emulated (applied by this server, so approximate), or ignored (accepted so your request doesn't fail, but has no effect) — plus response headers and error codes.
For agents
Everything is available as JSON, and a running server describes itself over HTTP:
claude-cli-proxy api --json # the full request reference
claude-cli-proxy examples --json # every integration snippet
claude-cli-proxy test --json # request, response, timings
claude-cli-proxy doctor --json # environment state, exit code 1 if broken
claude-cli-proxy models --json # available models
claude-cli-proxy --json # connection details, then keep servingcurl http://127.0.0.1:7274/ # index: endpoints and auth status
curl http://127.0.0.1:7274/v1/help # the full reference, same as `api --json`/ and /v1/help never require a key — an agent has to be able to learn the surface before it knows whether one is needed.
Options
| Flag | Default | Description |
|---|---|---|
| --port <n> | 7274 | Port to listen on |
| --strict-port | off | Fail instead of moving to a free port |
| --host <addr> | 127.0.0.1 | Interface to bind |
| --model <id> | claude-sonnet-5 | Default model |
| --api-key [key] | off | Require a key. With no value, one is generated and stored |
| --allow-insecure | off | Permit a non-loopback bind without a key |
| --no-selfcheck | off | Skip the startup test call |
| --cors-origin <o> | off | Allow a browser origin |
| --timeout <s> | 900 | Per-request timeout |
| --concurrency <n> | 4 | Requests handled in parallel |
| --json | off | Machine-readable output |
| --quiet | off | Suppress logging |
If port 7274 is busy, the server moves to the next free port and tells you which one it picked. Use --strict-port if you'd rather it fail.
Every flag has an environment variable equivalent: CLAUDE_API_PORT, CLAUDE_API_HOST, CLAUDE_API_KEY, CLAUDE_API_DEFAULT_MODEL, CLAUDE_API_TIMEOUT, CLAUDE_API_CONCURRENCY, CLAUDE_API_MAX_OUTPUT_TOKENS, CLAUDE_API_MAX_INPUT_TOKENS, CLAUDE_API_CORS_ORIGIN, CLAUDE_API_SYSTEM_PROMPT, CLAUDE_API_SELFCHECK.
Endpoints
| Method | Path | Purpose |
|---|---|---|
| POST | /v1/messages | Anthropic Messages API |
| POST | /v1/chat/completions | OpenAI Chat Completions API |
| POST | /v1/messages/count_tokens | Estimate prompt size |
| GET | /v1/models | List models (both SDK formats) |
| GET | /v1/models/{id} | Model details |
| GET | /v1/limits | What this server supports |
| GET | /v1/help | Full request reference as JSON |
| GET | / | Index of endpoints and auth status |
| GET | /health | Status, no key required |
Use as a library
import { start } from 'claude-cli-proxy';
const server = await start({ port: 7274 });
console.log(server.url);
const check = await server.verify();
console.log(check.ok ? `ready on ${check.model}` : check.fatal.message);
await server.close();Good to know
It uses your subscription. Every request draws on your Claude plan's usage allowance, the same as using Claude Code directly. There is no per-token charge, but there is a quota — and when you hit it you get a 429 telling you so.
Keep it local. The default binding is 127.0.0.1, so only your machine can reach it. Anything that can reach the port can spend your quota, so binding to a wider interface is refused unless you add --api-key (or --allow-insecure to override). Browser requests from other origins are refused unless you allow them with --cors-origin.
Some parameters are accepted but ignored. temperature, top_p, tools, response_format and similar options aren't available through this backend. Requests using them succeed rather than failing; the x-claude-cli-ignored-params response header lists exactly what was dropped. GET /v1/limits documents the full picture.
max_tokens and stop_sequences are applied by this server, not by the model, so they're approximate rather than exact. Token counts from count_tokens are estimates; the usage figures in responses are the real ones.
Long prompts are fine. There's no input cap by default — send a whole document and the model decides what fits.
First request is slower. Expect roughly 3 seconds of fixed overhead per request. Repeated prompts with a shared prefix are cached automatically, which makes large-context work much faster after the first call.
Troubleshooting
Run this first:
claude-cli-proxy doctor"Claude Code CLI not found" — install it with npm install -g @anthropic-ai/claude-code, or point CLAUDE_BIN at the executable.
"not signed in" — run claude auth login. Installing Claude Code does not sign you in, so this is the most common gap. doctor checks it without spending any quota.
401 on every request — same cause: the CLI isn't signed in. This is never about the API key; the server doesn't require one.
429 — your Claude plan's usage limit is exhausted. This is a plan quota, not a key problem. Wait for the reset.
404 on a model name — the model isn't available through this server. Run claude-cli-proxy models, or set CLAUDE_API_OPENAI_MODEL_FALLBACK.
Client times out on long requests — raise the client's own timeout. The server keeps the connection alive while the model works, but many HTTP clients default to 30 or 60 seconds.
Uninstall
claude-cli-proxy uninstallAdd --purge to also delete the local state directory at ~/.claude-cli-proxy.
License
Proprietary. You may install and run this software for personal use or inside your organisation. Copying, modifying, redistributing or reusing its source is not permitted. See LICENSE for the full terms.
Versions 1.0.0 and 1.0.1 were released under MIT; that grant still stands for those versions.
