claude-relay: Claude Code API server
claude-relay is a self-hosted Claude Code API server. It exposes Claude Code through the OpenAI Chat Completions API (/v1/chat/completions) and the Anthropic Messages API (/v1/messages). Point any OpenAI or Anthropic client library at it by changing the base URL, and your requests run through Claude Code using your Claude subscription login or an Anthropic API key.
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8000/v1", api_key="not-needed")
reply = client.chat.completions.create(
model="claude-sonnet-4-6",
messages=[{"role": "user", "content": "Hello, Claude"}],
)
print(reply.choices[0].message.content)
Run it:
claude auth login # or: export ANTHROPIC_API_KEY=...
uvx claude-relay-server # serves http://localhost:8000/v1
See INSTALL.md for pipx/pip, an always-on service and Docker.
Highlights
- Two APIs, one server. OpenAI-compatible
/v1/chat/completionsand Anthropic-compatible/v1/messages. Both support streaming (SSE). - Honours system prompts. Your system message replaces Claude Code's own prompt on every endpoint, and
/v1/messagesalso acceptssystemas a list of text blocks, as the Anthropic SDK sends it. - Tools off by default. Both endpoints run a single turn without tools, so untrusted text in a prompt can't run commands. To give Claude its Bash/Read/Write tools, the server must set
ALLOW_TOOLS=trueand the request must send"enable_tools": true. - Isolated from host settings. Requests don't load the host's
~/.claude/CLAUDE.md, settings, skills or plugins. SetCLAUDE_SETTING_SOURCES=user,project,localto opt back in. - Flexible auth. Claude CLI login (subscription),
ANTHROPIC_API_KEY, AWS Bedrock or Google Vertex AI, auto-detected. - Ships deploy tooling.
scripts/install-service.shsets up an always-on systemd user service, and theDockerfilebuilds a non-root production image. See Always-on local service and Docker.
What is claude-relay for?
claude-relay is for developers who want to call Claude Code from tools and code written against the OpenAI or Anthropic APIs. Typical uses:
- Using an existing OpenAI Python or JavaScript SDK app with Claude, with only a base-URL change.
- Connecting chat UIs, editors and agent frameworks that accept a custom OpenAI-compatible endpoint.
- Scripting Claude Code over plain HTTP/curl instead of the CLI.
It runs on the official Claude Agent SDK, which bundles the Claude Code CLI, so no separate Node.js install is needed.
Version
Current Version: 4.0.0
- Safer defaults: CORS allows no origins (
CORS_ORIGINS=[]), and aHostallow-list (ALLOWED_HOSTS) blocks DNS rebinding. Claude Code tools needALLOW_TOOLS=trueon the server as well as"enable_tools": truein the request. - Real model names: responses report the model that actually answered. The default is
sonnet, and non-Claude names likegpt-4oget a 400 unlessMODEL_ALIASESmaps them. - New
/v1/responsesendpoint (OpenAI Responses API, text only, with streaming)./v1/messagesstreams for real and returns Anthropic-style errors. - Effort, thinking and fallback:
reasoning_effort/X-Claude-Effort, a working thinking budget, andFALLBACK_MODEL/X-Claude-Fallback-Model. - Accurate responses: real token usage and stop reasons, no output filtering, a 401
claude_cli_not_authenticatedwhen the Claude Code login is missing, and anerrorevent when a stream fails. - Sessions: capped by
MAX_SESSIONS,MAX_SESSION_MESSAGESandSESSION_TTL_MINUTES; the session endpoints need the API key when one is set. - Removed: MCP support,
/v1/compatibility,/v1/debug/request,FAST_MODEL, and theX-Claude-Allowed-Tools/X-Claude-Permission-Modeheaders.
Upgrading from 3.x: if clients reach the server by any name other than localhost (a LAN IP, a container name, a Tailscale name), add it to ALLOWED_HOSTS. Add MODEL_ALIASES for clients that send gpt-* model names, ALLOW_TOOLS=true if they send enable_tools, and CORS_ORIGINS for browser clients. MAX_TIMEOUT is in milliseconds.
3.0.0:
- Published on PyPI as
claude-relay-server: run it withuvx claude-relay-server, or install theclaude-relaycommand withuv tool install/pipx install - New
claude-relaycommand with--host,--port,--api-key,--generate-api-keyand--env-file - Binds to
127.0.0.1by default, and warns if you expose it without an API key - No startup prompt: the interactive "Enable API key protection?" question is gone, so it runs cleanly from scripts and services
- Docker image at
ghcr.io/anandhakrishnanaji/claude-relay
Upgrading from 2.x: the Python package moved from src to claude_relay. Replace uvicorn src.main:app with claude-relay (or uvicorn claude_relay.main:app). If you used the systemd service, rerun ./scripts/install-service.sh. If you relied on the server listening on all interfaces, pass --host 0.0.0.0 and set an API key.
2.3.0:
- Live
/v1/modelsdiscovery: WhenANTHROPIC_API_KEYis set, the wrapper fetches Anthropic's live model list (cached) instead of serving the static fallback - Dynamic default Sonnet:
DEFAULT_MODELresolves to the latest Sonnet at startup whenANTHROPIC_API_KEYis configured; falls back toclaude-sonnet-4-6otherwise - Operator overrides: New
CLAUDE_MODELS_OVERRIDEandMODEL_LIST_*env vars - Updated catalog: Claude 4.6 family added to the static fallback list
Features
🔥 Core API Compatibility
- OpenAI-compatible
/v1/chat/completionsendpoint - Anthropic-compatible
/v1/messagesendpoint - OpenAI Responses API
/v1/responses(text only, stateless) - Support for both streaming and non-streaming responses
- Compatible with OpenAI Python SDK and all OpenAI client libraries
- Automatic model validation and selection
🛠 Claude Agent SDK Integration
- Official Claude Agent SDK integration
- Accurate token counting - input/output tokens from SDK
- Session management - proper session IDs and continuity
- Enhanced error handling with detailed authentication diagnostics
- Modern SDK features - Latest capabilities and improvements
🔐 Multi-Provider Authentication
- Automatic detection of authentication method
- Claude CLI auth - works with existing
claude authsetup - Direct API key -
ANTHROPIC_API_KEYenvironment variable - AWS Bedrock - enterprise authentication with AWS credentials
- Google Vertex AI - GCP authentication support
⚡ Advanced Features
- System prompt support via SDK options
- Optional tool usage - Enable Claude Code tools (Read, Write, Bash, etc.) when needed
- Fast default mode - Tools disabled by default for OpenAI API compatibility
- Optional API key protection -
--api-keyor--generate-api-key - Comprehensive logging and debugging capabilities
🌐 Interactive Landing Page
- API Explorer at root URL (
http://localhost:8000/) - Live endpoint testing - Expandable accordions fetch real-time data
- Light/dark theme toggle - Persists preference in localStorage
- Copy-to-clipboard - One-click copy for Quick Start commands
- Version badge and GitHub link
Quick Start
You need Python 3.10+ (uv fetches it for you) and credentials for Claude:
- a Claude subscription: install the Claude Code CLI and run
claude auth login, or - an Anthropic API key in
ANTHROPIC_API_KEY, or - AWS Bedrock / Google Vertex AI (see Configuration).
uvx claude-relay-server
That downloads and starts the server on http://localhost:8000. Point your client at http://localhost:8000/v1 (OpenAI) or http://localhost:8000 (Anthropic) and stop it with Ctrl+C when you're done.
Note: The Claude Code CLI is bundled with the Claude Agent SDK, so Node.js is only needed if you want the standalone CLI for
claude auth login.
Installation
To keep the claude-relay command installed instead of using uvx:
uv tool install claude-relay-server # or: pipx install claude-relay-server
claude-relay # start it
uv tool upgrade claude-relay-server # later, to update
pip install claude-relay-server into a virtualenv works too.
From source (to work on claude-relay itself):
git clone https://github.com/anandhakrishnanaji/claude-relay
cd claude-relay
uv venv .venv && uv pip install --python .venv/bin/python -e .
.venv/bin/uvicorn claude_relay.main:app --reload --port 8000 # auto-reloads on code changes
Configuration
Settings come from command-line flags, then environment variables, then a .env file in the directory you run claude-relay from (or the file given with --env-file). See .env.example for every option. The common ones:
# Explicit authentication method (optional)
# Options: cli, api_key, bedrock, vertex
# If not set, auto-detects based on available credentials
# CLAUDE_AUTH_METHOD=cli
# Optional API key clients must send as "Authorization: Bearer <key>"
# API_KEY=your-optional-api-key
# Interface and port (same as --host / --port)
CLAUDE_WRAPPER_HOST=127.0.0.1
PORT=8000
# Timeout in milliseconds
MAX_TIMEOUT=600000
# Browser origins allowed to call the API (JSON list; default: none)
CORS_ORIGINS=[]
# Host header allow-list (default: localhost,127.0.0.1,::1,[::1],*.localhost)
# ALLOWED_HOSTS=localhost,127.0.0.1
# Honour "enable_tools": true in requests (default: false, such requests get a 403)
# ALLOW_TOOLS=true
# Working directory for Claude Code (optional)
# If not set, uses an isolated temporary directory for security
# CLAUDE_CWD=/path/to/your/workspace
📁 Working Directory Configuration
By default, Claude Code runs in an isolated temporary directory to prevent it from accessing the wrapper's source code. This enhances security by ensuring Claude Code only has access to the workspace you intend.
Configuration Options:
-
Default (Recommended): Automatically creates a temporary isolated workspace
# No configuration needed - secure by default claude-relay -
Custom Directory: Set a specific workspace directory
export CLAUDE_CWD=/path/to/your/project claude-relay
-
Via .env file: Add to your
.envfileCLAUDE_CWD=/home/user/my-workspace
Important Notes:
- The temporary directory is automatically cleaned up when the server stops
- This prevents Claude Code from accidentally modifying the wrapper's own code
- Cross-platform compatible (Windows, macOS, Linux)
🔐 API Security Configuration
By default the server listens on 127.0.0.1 with no API key, which is what you want for local development: only programs on your machine can reach it.
To require a key, pass one or let claude-relay generate one:
claude-relay --api-key my-secret # or set API_KEY in the environment / .env
claude-relay --generate-api-key # prints a random key for this run
Clients then send Authorization: Bearer <key> (with the OpenAI SDK, pass it as api_key).
If you listen on another interface (--host 0.0.0.0), set an API key. claude-relay warns at startup if you don't, because anyone who can reach the port can use your Claude account.
🛡️ Rate Limiting
Built-in rate limiting protects against abuse and ensures fair usage:
- Chat Completions (
/v1/chat/completions,/v1/messages,/v1/responses): 10 requests/minute - Auth Status (
/v1/auth/status): 10 requests/minute - Health Check (
/health): 30 requests/minute
Rate limits are applied per IP address using a fixed window algorithm. The IP is the TCP peer: X-Forwarded-For is ignored unless the proxy's address is in FORWARDED_ALLOW_IPS (for example FORWARDED_ALLOW_IPS=127.0.0.1 behind a local nginx). Behind such a proxy without it, all clients share one limit. When exceeded, the API returns HTTP 429 with a structured error response:
{
"error": {
"message": "Rate limit exceeded. Try again in 60 seconds.",
"type": "rate_limit_exceeded",
"code": "too_many_requests",
"retry_after": 60
}
}
Configure rate limiting through environment variables:
RATE_LIMIT_ENABLED=true
RATE_LIMIT_CHAT_PER_MINUTE=10
RATE_LIMIT_AUTH_PER_MINUTE=10
RATE_LIMIT_HEALTH_PER_MINUTE=30
Running the Server
claude-relay # http://localhost:8000
claude-relay --port 9000 # another port
claude-relay --host 0.0.0.0 --api-key KEY # reachable from your network
claude-relay --env-file ~/claude-relay.env
claude-relay --debug # verbose logging
claude-relay --help
python -m claude_relay runs the same command. If the port is taken, claude-relay exits with an error instead of silently picking another port, so your clients' base URL stays correct.
To check your Claude credentials before starting:
claude --version
claude --print --model claude-haiku-4-5-20251001 "Hello"
Always-on local service (Linux, systemd)
Run the wrapper in the background at a fixed URL, starting at boot and restarting on crashes. It uses your Claude subscription login, so no ANTHROPIC_API_KEY is needed.
Requirements: uv and a logged-in Claude CLI (claude auth login).
./scripts/install-service.sh
The script:
- creates
.venv(Python 3.12) and installs the project - writes
.envif it doesn't exist (CLI auth,127.0.0.1:8787, rate limiting off) - installs and starts the
claude-wrappersystemd user service - enables linger so the service keeps running after you log out
- waits for
/healthand prints the URL
Your fixed URL is http://claude.localhost:8787/v1. Any *.localhost name resolves to your own machine, so no DNS or /etc/hosts setup is needed.
from openai import OpenAI
client = OpenAI(base_url="http://claude.localhost:8787/v1", api_key="not-needed")
The service listens on 127.0.0.1 only, so other devices can't reach it. To expose it on your network, set HOST=0.0.0.0 and add an API_KEY to .env so nobody else can use your subscription.
Managing the service:
systemctl --user status claude-wrapper # check it
systemctl --user restart claude-wrapper # after git pull or editing .env
journalctl --user -u claude-wrapper -f # live logs
PORT=9000 ./scripts/install-service.sh # reinstall on another port (delete .env first)
./scripts/install-service.sh --uninstall # remove the service
After a git pull that changes dependencies, rerun ./scripts/install-service.sh. If your Claude login expires, run claude auth login and restart the service.
Docker
Each release publishes a multi-arch image (amd64, arm64) to GHCR. It runs as a non-root user (uid 1000), which the Claude CLI requires when tools are enabled.
docker run -d --name claude-relay --restart unless-stopped \
-p 127.0.0.1:8000:8000 \
-v ~/.claude:/home/app/.claude \
-e CLAUDE_CONFIG_DIR=/home/app/.claude \
ghcr.io/anandhakrishnanaji/claude-relay:latest
- The
~/.claudemount is read-write so the CLI can refresh its login token. To use an API key instead, drop the mount and pass-e ANTHROPIC_API_KEY=.... - If your uid isn't 1000, build the image yourself:
docker build --build-arg UID=$(id -u) --build-arg GID=$(id -g) -t claude-relay . - To give Claude a workspace, add
-v /path/to/project:/workspace -e CLAUDE_CWD=/workspace. -p 127.0.0.1:8000:8000keeps it local. If you publish the port more widely, add-e API_KEY=....
The image is built from the Dockerfile in the repo root, with the dependency versions pinned in poetry.lock. The included docker-compose.yml builds the same image.
Environment Variables
| Variable | Description | Default |
|---|---|---|
CLAUDE_WRAPPER_HOST |
Interface to bind (--host) |
127.0.0.1 (0.0.0.0 in the Docker image) |
PORT |
Server port (--port) |
8000 |
API_KEY |
Key clients must send as a bearer token (--api-key) |
- |
MAX_TIMEOUT |
Request timeout in milliseconds (600000 = 10 minutes) | 600000 |
CLAUDE_CWD |
Working directory | temp dir |
CLAUDE_SETTING_SOURCES |
Comma-separated filesystem settings the CLI loads per request (user, project, local). Empty isolates requests from the host's ~/.claude/CLAUDE.md, settings, skills and plugins. |
empty |
CORS_ORIGINS |
JSON list of browser origins allowed to call the API. ["*"] allows any origin, without credentials; only use it with API_KEY set. |
[] (none) |
ALLOWED_HOSTS |
Comma-separated Host header allow-list; wildcards like *.example.com are allowed. Protects against DNS rebinding. Add every name clients use (LAN IP, container name, ...). * disables the check. |
localhost,127.0.0.1,::1,[::1],*.localhost |
FORWARDED_ALLOW_IPS |
Comma-separated proxy IPs whose X-Forwarded-For is trusted for the client IP (rate limits, logs). Read by claude-relay; plain uvicorn trusts 127.0.0.1 by default. |
empty (none) |
MAX_SESSIONS |
Most sessions kept in memory at once. Creating one more evicts the least recently used. | 1000 |
SESSION_TTL_MINUTES |
Minutes of inactivity before a session expires. | 60 |
MAX_SESSION_MESSAGES |
Most messages a session keeps; the oldest non-system messages are dropped first. | 200 |
ALLOW_TOOLS |
Honour "enable_tools": true in requests. Tools run Bash/Write/Edit with permissions bypassed. When off, such requests get a 403. |
false |
CLAUDE_AUTH_METHOD |
Auth method: cli, api_key, bedrock, vertex |
auto-detect |
ANTHROPIC_API_KEY |
Direct Anthropic API key. Optional — also unlocks live /v1/models discovery and dynamic latest-Sonnet default. Not required when using Bedrock, Vertex, or Claude CLI subscription auth. |
- |
DEFAULT_MODEL |
Override the default model. When unset and ANTHROPIC_API_KEY is configured, the wrapper resolves the latest Sonnet at startup; otherwise it uses the sonnet alias, which Claude Code maps to its latest Sonnet. |
auto |
MODEL_ALIASES |
Comma-separated name=model pairs applied to the request's model on both endpoints, for clients that hard-code other names, e.g. gpt-4o=sonnet,gpt-4o-mini=haiku. |
- |
FALLBACK_MODEL |
Model Claude Code switches to when the requested one is overloaded or unavailable. Ignored when it equals the request's model. Per request: X-Claude-Fallback-Model header. |
- |
CLAUDE_MODELS_OVERRIDE |
Comma-separated model IDs to advertise via /v1/models. Takes precedence over both live and static lists. |
- |
MODEL_LIST_CACHE_TTL_SECONDS |
Cache TTL for live /v1/models results. |
3600 |
MODEL_LIST_ERROR_TTL_SECONDS |
Short cache TTL applied when the live fetch fails, so transient outages don't suppress live discovery for the full hour. | 60 |
MODEL_LIST_REQUEST_TIMEOUT_SECONDS |
HTTP timeout for the live model fetch. | 5 |
Management
docker logs -f claude-relay # View logs
docker stop claude-relay # Stop
docker start claude-relay # Start
docker rm claude-relay # Remove
Test
curl http://localhost:8000/health
curl http://localhost:8000/v1/models
Usage Examples
Using curl
# Basic chat completion (no auth)
curl -X POST http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-6",
"messages": [
{"role": "user", "content": "What is 2 + 2?"}
]
}'
# With API key protection (when enabled)
curl -X POST http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-generated-api-key" \
-d '{
"model": "claude-sonnet-4-6",
"messages": [
{"role": "user", "content": "Write a Python hello world script"}
],
"stream": true
}'
Using OpenAI Python SDK
from openai import OpenAI
# Configure client (automatically detects auth requirements)
client = OpenAI(
base_url="http://localhost:8000/v1",
api_key="your-api-key-if-required" # Only needed if protection enabled
)
# Alternative: Let examples auto-detect authentication
# The wrapper's example files automatically check server auth status
# Basic chat completion
response = client.chat.completions.create(
model="claude-sonnet-4-6",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "What files are in the current directory?"}
]
)
print(response.choices[0].message.content)
# Output: Fast response without tool usage (default behaviour)
# Enable tools when you need them (e.g., to read files)
response = client.chat.completions.create(
model="claude-sonnet-4-6",
messages=[
{"role": "user", "content": "What files are in the current directory?"}
],
extra_body={"enable_tools": True} # Enable tools (server needs ALLOW_TOOLS=true)
)
print(response.choices[0].message.content)
# Output: Claude will actually read your directory and list the files!
# Check real costs and tokens
print(f"Cost: ${response.usage.total_tokens * 0.000003:.6f}") # Real cost tracking
print(f"Tokens: {response.usage.total_tokens} ({response.usage.prompt_tokens} + {response.usage.completion_tokens})")
# Streaming
stream = client.chat.completions.create(
model="claude-sonnet-4-6",
messages=[
{"role": "user", "content": "Explain quantum computing"}
],
stream=True
)
for chunk in stream:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="")
# Responses API (text only)
response = client.responses.create(
model="claude-sonnet-4-6",
instructions="Be brief.",
input="Explain quantum computing",
)
print(response.output_text)
for event in client.responses.create(model="claude-sonnet-4-6", input="Hi", stream=True):
if event.type == "response.output_text.delta":
print(event.delta, end="")
/v1/responses takes input as a string or a list of messages (text parts only; images and files are dropped), instructions as the system prompt, and stream. It is stateless and text only: requests with tools, tool_choice: "required", previous_response_id or conversation get HTTP 400, so send the whole conversation in input each time. max_output_tokens, temperature, top_p and reasoning are accepted but not applied.
Supported Models
The wrapper exposes Claude's full model catalog. When ANTHROPIC_API_KEY is set, /v1/models returns Anthropic's live list (cached for 1 hour) and the wrapper picks the latest Sonnet as DEFAULT_MODEL at startup. When the key is absent — for example, when running with Bedrock, Vertex, or Claude CLI subscription auth — the static list below is served and the sonnet alias is the default. Operators who want a curated list regardless of auth can set CLAUDE_MODELS_OVERRIDE.
Responses report the model that actually ran (e.g. claude-haiku-5-5 for a request for haiku).
Effort and thinking: on /v1/chat/completions, OpenAI reasoning_effort maps to Claude's effort level (none/minimal -> low, low, medium, high, xhigh). The X-Claude-Effort header (low, medium, high, xhigh, max) overrides it, and X-Claude-Max-Thinking-Tokens sets a thinking budget (0 turns thinking off). /v1/messages accepts Anthropic's thinking and output_config.effort. max_tokens is ignored: Claude Code has no per-request output cap.
Model names are checked before Claude Code runs. Names that are not Claude models (no claude in the name and not an alias, e.g. gpt-4o) get a 400 unless MODEL_ALIASES maps them. Other claude-* names are passed through, so new models work before this list is updated; if Claude Code does not know one, the request fails with a 400 (model_not_found on /v1/chat/completions, invalid_request_error on /v1/messages). Bedrock ARNs without claude in them need a MODEL_ALIASES entry. Every error on /v1/messages (including 401, 403, 422, 429 and 5xx) uses the Anthropic shape {"type": "error", "error": {"type": ..., "message": ...}}; mid-stream failures arrive as an error event.
Aliases
sonnet, opus, haiku, fable, best, opusplan, sonnet[1m], opus[1m] and fable[1m]. Claude Code resolves them to its current model of that family, and /v1/models lists them (unless CLAUDE_MODELS_OVERRIDE is set).
Claude 5.x
claude-opus-5-5,claude-opus-5claude-sonnet-5-5,claude-sonnet-5claude-haiku-5-5claude-fable-5-1,claude-fable-5
Claude 4.x
claude-opus-4-8,claude-opus-4-7,claude-opus-4-6,claude-opus-4-5-20251101,claude-opus-4-1-20250805claude-sonnet-4-6,claude-sonnet-4-5-20250929claude-haiku-4-5-20251001
Note: Claude 3.x models are not supported by the Claude Agent SDK. The model parameter is passed to Claude Code via the SDK's model selection.
Session Continuity 🆕
The wrapper now supports session continuity, allowing you to maintain conversation context across multiple requests. This is a powerful feature that goes beyond the standard OpenAI API.
How It Works
- Stateless Mode (default): Each request is independent, just like the standard OpenAI API
- Session Mode: Include a
session_idto maintain conversation history across requests
Using Sessions with OpenAI SDK
import openai
client = openai.OpenAI(
base_url="http://localhost:8000/v1",
api_key="not-needed"
)
# Start a conversation with session continuity
response1 = client.chat.completions.create(
model="claude-sonnet-4-6",
messages=[
{"role": "user", "content": "Hello! My name is Alice and I'm learning Python."}
],
extra_body={"session_id": "my-learning-session"}
)
# Continue the conversation - Claude remembers the context
response2 = client.chat.completions.create(
model="claude-sonnet-4-6",
messages=[
{"role": "user", "content": "What's my name and what am I learning?"}
],
extra_body={"session_id": "my-learning-session"} # Same session ID
)
# Claude will remember: "Your name is Alice and you're learning Python."
Using Sessions with curl
# First message (add -H "Authorization: Bearer your-key" if auth enabled)
curl -X POST http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-6",
"messages": [{"role": "user", "content": "My favourite color is blue."}],
"session_id": "my-session"
}'
# Follow-up message - context is maintained
curl -X POST http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-6",
"messages": [{"role": "user", "content": "What's my favourite color?"}],
"session_id": "my-session"
}'
Session Management
The wrapper provides endpoints to manage active sessions:
GET /v1/sessions- List all active sessionsGET /v1/sessions/{session_id}- Get session detailsDELETE /v1/sessions/{session_id}- Delete a sessionGET /v1/sessions/stats- Get session statistics
# List active sessions
curl http://localhost:8000/v1/sessions
# Get session details
curl http://localhost:8000/v1/sessions/my-session
# Delete a session
curl -X DELETE http://localhost:8000/v1/sessions/my-session
Session Features
- Automatic Expiration: Sessions expire after
SESSION_TTL_MINUTES(default 60) of inactivity - Bounded History: A session keeps at most
MAX_SESSION_MESSAGES(default 200) messages, dropping the oldest first; a new system message replaces the stored one - Streaming Support: Session continuity works with both streaming and non-streaming requests
- Memory Persistence: Full conversation history is maintained within the session
- Efficient Storage: Only active sessions are kept in memory
Examples
See examples/session_continuity.py for comprehensive Python examples and examples/session_curl_example.sh for curl examples.
API Endpoints
Core Endpoints
GET /- Interactive landing page with API explorerPOST /v1/chat/completions- OpenAI-compatible chat completions (supportssession_id)POST /v1/messages- Anthropic-compatible messages endpointPOST /v1/responses- OpenAI Responses API (text only, stateless, supports streaming)GET /v1/models- List available modelsGET /v1/auth/status- Check authentication status and configurationGET /version- Get API versionGET /health- Health check endpoint
Session Management Endpoints 🆕
GET /v1/sessions- List all active sessionsGET /v1/sessions/{session_id}- Get detailed session informationDELETE /v1/sessions/{session_id}- Delete a specific sessionGET /v1/sessions/stats- Get session manager statistics
Limitations & Roadmap
🚫 Current Limitations
- Images in messages are converted to text placeholders
- Function calling not supported (tools work automatically based on prompts)
- OpenAI parameters not yet mapped:
temperature,top_p,max_tokens,logit_bias,presence_penalty,frequency_penalty - Multiple responses (
n > 1) not supported
🛣 Planned Enhancements
- Tool configuration - allowed/disallowed tools endpoints
- OpenAI parameter mapping - temperature, top_p, max_tokens support
- Enhanced streaming - better chunk handling
- MCP integration - Model Context Protocol server support
✅ Recent Improvements (v2.2.0)
- Interactive Landing Page: API explorer with live endpoint testing
- Anthropic Messages API: Native
/v1/messagesendpoint - Explicit Auth Selection:
CLAUDE_AUTH_METHODenv var - Tool Execution Fix:
enable_tools: truenow works correctly
✅ v2.0.0 - v2.1.0 Features
- Claude Agent SDK v0.1.18 with bundled CLI
- Multi-provider auth (CLI, API key, Bedrock, Vertex AI)
- Session continuity and management
- Real-time cost and token tracking
- System prompt support
Troubleshooting
-
Claude CLI not found: claude-relay runs the CLI bundled with
claude-agent-sdk, orclaudeonPATHif the SDK has none. Reinstall claude-relay, or checkwhich claude. -
Authentication errors:
# Shows whether the CLI is logged in (no model call); claude-relay runs this at startup claude auth status # If it says "loggedIn": false, run: claude auth login
-
Timeout errors:
- Increase
MAX_TIMEOUTin.env - Note: Claude Code can take time for complex requests
- Increase
Testing
The test suite runs in-process with the Claude Agent SDK mocked, so it needs no running server and makes no Claude calls:
poetry install --with dev
poetry run pytest tests/
poetry run black --check claude_relay tests
To try a running server by hand, use the scripts in examples/, or:
curl http://localhost:8000/health
curl http://localhost:8000/v1/auth/status | python -m json.tool
FAQ
Can I use my Claude Pro or Max subscription with the OpenAI SDK?
Yes. Run claude auth login on the host, start claude-relay, and point the OpenAI SDK's base_url at http://<host>:8000/v1. Check the Terms Compliance section first: subscription use is for your own personal use.
Does it support streaming?
Yes, on all three: pass "stream": true to get server-sent events. /v1/messages streams Anthropic events (message_start ... message_stop), so client.messages.stream(...) in the Anthropic SDK works. Claude Code hands over whole messages, so a short answer usually arrives as one text delta.
Does it support OpenAI function calling?
No. OpenAI-style tools/function calling is not mapped. Claude Code's own tools (Bash, Read, Write and others) can be turned on per request with "enable_tools": true once the server sets ALLOW_TOOLS=true.
Which models can I use?
Any model Claude Code accepts, such as sonnet, claude-opus-5-5 and claude-haiku-4-5-20251001. Map other names (e.g. gpt-4o) with MODEL_ALIASES. With ANTHROPIC_API_KEY set, /v1/models returns Anthropic's live list. See Supported Models.
Is it safe to expose on a network?
Set API_KEY so clients must send a bearer token, keep tools off unless you need them, and keep it on a private network. See API Security Configuration.
How is this different from calling the Anthropic API directly?
Requests go through Claude Code, so you can use a Claude subscription login instead of per-token API billing, and optionally give Claude its coding tools. If you already have an API key and don't need Claude Code, the Anthropic API is simpler.
Terms Compliance
This wrapper is designed to be compliant with Anthropic's Terms of Service.
Requirements for Users
Important: You must have your own valid Claude subscription or API access to use this wrapper.
- Claude Pro or Max subscription - For CLI authentication (
claude auth login) - Anthropic API key - Available at platform.claude.com
- AWS Bedrock or Google Vertex AI - For enterprise cloud authentication
This wrapper does not provide Claude access - it provides an OpenAI-compatible interface to Claude services you already have access to.
How This Wrapper Works
- Uses the official Claude Agent SDK - The same SDK Anthropic provides for developers
- Each user authenticates individually - No credential sharing or pooling
- Format translation only - Converts OpenAI-format requests to Claude SDK calls
- No reselling - Users access Claude through their own subscriptions/API keys
Personal vs Commercial Use
| Use Case | Recommended Authentication | Notes |
|---|---|---|
| Personal projects | CLI Auth (Pro/Max) or API Key | Acceptable at moderate scale |
| Business/Commercial | API Key, Bedrock, or Vertex AI | Use platform.claude.com |
| High-scale applications | Bedrock or Vertex AI | Enterprise authentication recommended |
Note on Consumer Plans: Claude Pro and Max subscriptions are primarily designed for individual, interactive use. Using them through wrappers or automated implementations is acceptable for personal projects at moderate scale. For business use or applications that scale significantly, Anthropic's commercial API offerings at platform.claude.com are more appropriate.
Authentication Methods
| Method | Terms | Compliance |
|---|---|---|
ANTHROPIC_API_KEY |
Commercial Terms | Explicitly allowed for programmatic access |
| AWS Bedrock | Commercial Terms | Explicitly allowed for programmatic access |
| Google Vertex AI | Commercial Terms | Explicitly allowed for programmatic access |
| CLI Auth (Pro/Max) | Consumer Terms | Uses official SDK with official auth methods |
CLI Authentication Note
Using CLI auth (claude auth login) with this wrapper is functionally equivalent to using Claude Code directly - both use the Claude Agent SDK with your personal subscription. Anthropic provides the SDK with CLI auth support, and this wrapper simply provides an alternative interface format.
What This Wrapper Does NOT Do
- Does not share or pool credentials between users
- Does not include or expose API keys or credentials
- Does not resell API access
- Does not train competing AI models
- Does not scrape or harvest data
- Does not bypass authentication or rate limits
User Responsibilities
By using this wrapper, you agree to:
- Comply with Anthropic's Terms of Service
- Comply with Anthropic's Usage Policy
- Use your own valid Claude subscription or API access
- Not share your credentials with others
- Use commercial API access for business applications
Disclaimer
This is an independent open-source project, not affiliated with or endorsed by Anthropic. Users are responsible for ensuring their own usage complies with Anthropic's terms. Anthropic reserves the right to modify their Terms of Service at any time.
When in doubt, use ANTHROPIC_API_KEY authentication which is explicitly permitted for programmatic access under the Commercial Terms.
For Anthropic's official terms, see:
Licence
MIT Licence
Contributing
Contributions are welcome! Please open an issue or submit a pull request. Maintainers: see RELEASING.md for how to cut a release.
Metadata
Release files for claude-relay-server 4.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| claude_relay_server-4.0.0.tar.gz | 71.3 kB | With provenance | Details for claude_relay_server-4.0.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| claude_relay_server-4.0.0-py3-none-any.whl | Python 3 | none | any | Details for claude_relay_server-4.0.0-py3-none-any.whl |
Total release size: 142.7 kB
Release files / claude_relay_server-4.0.0.tar.gz
| Download URL | claude_relay_server-4.0.0.tar.gz |
|---|---|
| Size | 71.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ffeb3b92f5fb07314b197f95a93b71d420b34f748d18518943b0f69eecef3e64
|
|
BLAKE2b-256 checksum How to use checksums |
f73e322b379d7693e5ca937dd67a921825089259a7986d059e1855bc64a40337
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 30, 2026.
Transparency logRelease files / claude_relay_server-4.0.0-py3-none-any.whl
| Download URL | claude_relay_server-4.0.0-py3-none-any.whl |
|---|---|
| Size | 71.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8160fc8aaf91a7ce36185bcf165603e89d6075a296152c317c07db5027d3fe98
|
|
BLAKE2b-256 checksum How to use checksums |
3f09fe7a4d8b825b88f4c708f687430cee9b676c78b0567ff41545ab17c89690
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 30, 2026.
Transparency logSecurity for claude-relay-server 4.0.0
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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for claude_relay_server-4.0.0.tar.gz
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 Oct 30, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.