claude-oauth-proxy lets you use Claude through an OpenAI-compatible API.
It is built for tools that already know how to talk to OpenAI-style endpoints, but where you want Claude behind the scenes instead.
Important: this project uses a Claude OAuth flow that works for real users in practice, but Anthropic does not publicly position third-party Claude Pro/Max OAuth as a normal supported integration surface. Treat it as an experimental compatibility path.
Choose one way to run the proxy:
- Local binary: fastest way to get started
- Docker Compose: good for containerized workflows
- Helm: the supported Kubernetes path
Then the setup is always the same:
- authenticate to Claude once
- save the resulting token file somewhere writable
- run the proxy
- point your client at
http://127.0.0.1:9999/v1
Download the latest binary for your platform from the Releases page. Extract it and place it somewhere on your PATH.
If you prefer to build from source:
go build -o dist/claude-oauth-proxy ./cmd/claude-oauth-proxyAuthenticate:
claude-oauth-proxy loginRun the proxy:
claude-oauth-proxy serveBy default it listens on http://127.0.0.1:9999 and uses the local API key sk-proxy-local-key.
Point your client at the proxy:
export OPENAI_BASE_URL="http://127.0.0.1:9999/v1"
export OPENAI_API_KEY="sk-proxy-local-key"Quick smoke test:
curl http://127.0.0.1:9999/v1/models \
-H "Authorization: Bearer sk-proxy-local-key"If you already authenticated with the Claude CLI (claude), the proxy can use those credentials directly. Point it at your existing credentials:
claude-oauth-proxy serve --seed-file ~/.claude/.credentials.jsonThe proxy reads the seed on first start, then writes refreshed tokens to its own path. Your Claude CLI credentials are never modified.
In Docker Compose, mount the file as a read-only volume instead:
environment:
CLAUDE_OAUTH_PROXY_SEED_FILE: /config/credentials.json
volumes:
- ~/.claude/.credentials.json:/config/credentials.json:ro
- claude-oauth-data:/var/lib/claude-oauth-proxySee docs/deploy/docker-compose.md for a complete example.
Copy the example files:
mkdir -p claude-oauth-proxy-compose
cp examples/docker-compose/docker-compose.yml claude-oauth-proxy-compose/
cp examples/docker-compose/.env.example claude-oauth-proxy-compose/.env
cd claude-oauth-proxy-compose
mkdir -p runtimeDo a one-time Claude login inside the container:
docker compose run --rm claude-oauth-proxy login --no-browserThat command will:
- print a Claude login URL
- ask you to sign in with your Claude account
- ask you to paste the
?code=...value from the redirect URL - save
tokens.jsoninto./runtime
Start the proxy:
docker compose up -dMore detail: docs/deploy/docker-compose.md
Kubernetes users should use the Helm chart shipped in this repository.
Add the chart repo:
helm repo add claude-oauth-proxy https://bonztm.github.io/claude-oauth-proxy
helm repo updateInstall the chart:
kubectl create namespace claude-oauth-proxy
kubectl create secret generic claude-oauth-proxy \
--namespace claude-oauth-proxy \
--from-literal=api-key=sk-proxy-local-key
helm upgrade --install claude-oauth-proxy claude-oauth-proxy/claude-oauth-proxy \
--namespace claude-oauth-proxy \
--create-namespace \
--set config.apiKey.existingSecret.name=claude-oauth-proxyBootstrap login inside the running pod:
kubectl exec -it -n claude-oauth-proxy deployment/claude-oauth-proxy -- \
/usr/local/bin/claude-oauth-proxy login --no-browserThen port-forward and point your client at the service.
More detail: docs/deploy/kubernetes.md
- exposes an OpenAI-compatible API for Claude-backed requests
- supports tool use / function calling (translated to Anthropic tool_use blocks)
- applies prompt caching automatically to reduce cost and latency (see
docs/caching.md) - stores OAuth tokens in a local file and reuses them across runs
- refreshes tokens automatically while the proxy is running
- retries transparently on 401 with a forced token refresh
- optional theoretical cost tracking via OpenRouter pricing (see
docs/configuration.md) - supports:
GET /v1/models(unauthenticated — allows client model discovery without credentials)POST /v1/chat/completions(streaming and non-streaming, requires API key)
- exposes health endpoints:
GET /healthGET /healthzGET /livezGET /readyGET /readyz
The first login is manual by design:
- the proxy opens or prints a Claude OAuth URL
- you sign in with your Claude account
- you paste the returned
code - the proxy exchanges that code for tokens
- the tokens are stored on disk and reused on future runs
Default token path on a host machine:
~/.config/claude-oauth-proxy/tokens.json
Extra usage and reauthentication: if your Claude account enters "extra usage" (beyond the included allowance on your plan), you may need to reauthenticate the proxy. Claude can invalidate existing OAuth sessions when usage tiers change, so if you start seeing errors after hitting extra usage, run a fresh login.
You can force a fresh login with:
claude-oauth-proxy serve --reloginor:
claude-oauth-proxy loginPublished images use three channels:
latestand<release-number>for published releasesnightlyandnightly-<shortsha>for non-release builds frommaindevelopanddevelop-<shortsha>for non-mainbranch builds
If you want the safest default, use latest or a pinned release like 1.2.3.
Most OpenAI-compatible tools only need two environment variables:
export OPENAI_BASE_URL="http://127.0.0.1:9999/v1"
export OPENAI_API_KEY="sk-proxy-local-key"Add to ~/.config/opencode/opencode.json:
{
"provider": {
"claude-proxy": {
"npm": "@ai-sdk/openai-compatible",
"name": "Claude OAuth Proxy",
"options": {
"baseURL": "http://127.0.0.1:9999/v1",
"apiKey": "sk-proxy-local-key"
},
"models": {
"claude-sonnet-4-6": {
"name": "Claude Sonnet 4.6",
"tool_call": true,
"reasoning": true,
"modalities": { "input": ["text", "image"], "output": ["text"] },
"limit": { "context": 200000, "output": 16384 }
}
}
}
}
}aider --openai-api-base http://127.0.0.1:9999/v1 \
--openai-api-key sk-proxy-local-key \
--model openai/claude-sonnet-4-6Or set in environment:
export OPENAI_API_BASE="http://127.0.0.1:9999/v1"
export OPENAI_API_KEY="sk-proxy-local-key"
aider --model openai/claude-sonnet-4-6Add to .continue/config.yaml:
models:
- name: Claude via Proxy
provider: openai
model: claude-sonnet-4-6
apiBase: http://127.0.0.1:9999/v1
apiKey: sk-proxy-local-keyfrom openai import OpenAI
client = OpenAI(
base_url="http://127.0.0.1:9999/v1",
api_key="sk-proxy-local-key",
)
response = client.chat.completions.create(
model="claude-sonnet-4-6",
messages=[{"role": "user", "content": "Say hello in one short sentence."}],
)
print(response.choices[0].message.content)curl http://127.0.0.1:9999/v1/chat/completions \
-H "Authorization: Bearer sk-proxy-local-key" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-6",
"messages": [
{"role": "user", "content": "Say hello in one short sentence."}
]
}'claude-oauth-proxy serve [--listen-addr :9999] [--api-key sk-proxy-local-key] [--relogin] [--no-browser] [--code <code>] [--cost-tracking]
claude-oauth-proxy login [--no-browser] [--code <code>]
claude-oauth-proxy status
claude-oauth-proxy logout
claude-oauth-proxy config validate
claude-oauth-proxy versionGuides:
- Docker Compose deployment
- Kubernetes / Helm deployment
- Configuration reference
- Prompt caching
- Helm chart details
- Testing and validation
Community:
Run tests:
go test ./...Check coverage:
go test -coverprofile=coverage.out ./...
go tool cover -func=coverage.outThe current baseline threshold for this repo is 90% total statement coverage.