A lightweight Go proxy that exposes GitHub Copilot as both OpenAI-compatible and Anthropic-compatible API endpoints.
- OpenAI API Compatible:
/v1/chat/completions,/v1/models,/v1/embeddings,/v1/responses - Anthropic API Compatible:
/v1/messages - Streaming Support: Full SSE streaming for both OpenAI and Anthropic formats
- Anthropic Routing: Uses native
/v1/messageswhen the model supports it, otherwise routes via/responsesor/chat/completions - Auto Authentication: GitHub Device Flow OAuth with automatic token refresh
- Usage Monitoring: Built-in
/usageendpoint for quota tracking - Models Cache: 5-minute cache for
/v1/modelsand Anthropic model capability lookups
# Build from source (requires Go 1.26+)
go build -o copilot2api .
# Start the proxy
./copilot2apiFirst run will prompt GitHub Device Flow authentication:
🔐 GitHub Authentication Required
Please visit: https://github.com/login/device
Enter code: XXXX-XXXX
Waiting for authorization...
✅ Authentication successful!
Server starts on http://127.0.0.1:7777 by default.
- Does not implement API key validation — any request is accepted
- Do not expose publicly — it becomes an open proxy consuming your Copilot quota
- Credentials are stored in
~/.config/copilot2api/credentials.json
Add to ~/.claude/settings.json:
{
"env": {
"ANTHROPIC_BASE_URL": "http://127.0.0.1:7777",
"ANTHROPIC_API_KEY": "dummy",
"ANTHROPIC_MODEL": "claude-opus-4.6",
"ANTHROPIC_SMALL_FAST_MODEL": "claude-haiku-4.5",
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1"
},
"permissions": {
"deny": [
"WebSearch"
]
}
}copilot2api supports Claude 1M context models. When Claude Code sends the anthropic-beta: context-1m-... header, the proxy automatically appends -1m to the model ID (e.g. claude-opus-4.6 → claude-opus-4.6-1m) so Copilot routes to the 1M variant.
To use it, select the 1M model variant in Claude Code via the /model command (e.g. Opus (1M)). Without this, Claude Code defaults to the standard 200K context window.
Add to ~/.codex/config.toml:
model = "gpt-5.3-codex"
model_provider = "copilot2api"
model_reasoning_effort = "high"
web_search = "disabled"
[model_providers.copilot2api]
name = "copilot2api"
base_url = "http://127.0.0.1:7777/v1"
wire_api = "responses"
api_key = "dummy"# OpenAI chat completion
curl http://localhost:7777/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"gpt-5.3-codex","messages":[{"role":"user","content":"Hello!"}]}'
# Anthropic message
curl http://localhost:7777/v1/messages \
-H "Content-Type: application/json" \
-H "x-api-key: dummy" \
-d '{"model":"claude-sonnet-4.6","messages":[{"role":"user","content":"Hello!"}],"max_tokens":100}'
# List models
curl http://localhost:7777/v1/models
# Check usage/quota
curl http://localhost:7777/usageUsage with SDKs
import openai
client = openai.OpenAI(
api_key="dummy",
base_url="http://127.0.0.1:7777/v1"
)
response = client.chat.completions.create(
model="gpt-5.3-codex",
messages=[{"role": "user", "content": "Hello!"}]
)import anthropic
client = anthropic.Anthropic(
api_key="dummy",
base_url="http://127.0.0.1:7777"
)
message = client.messages.create(
model="claude-sonnet-4.6",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello!"}]
)| Endpoint | Method | Description |
|---|---|---|
/v1/chat/completions |
POST | OpenAI Chat Completions (streaming & non-streaming) |
/v1/responses |
POST | OpenAI Responses API |
/v1/models |
GET | List available models (5min cache) |
/v1/embeddings |
POST | Generate embeddings (string or array input) |
/v1/messages |
POST | Anthropic Messages API (streaming & non-streaming) |
/usage |
GET | Copilot usage and quota info |
./copilot2api [options]
-host string Server host (default "127.0.0.1")
-port int Server port (default 7777)
-token-dir string Token storage directory (default ~/.config/copilot2api)
-debug Enable debug logging
-version Show version and exit
Environment variables are used as defaults when flags are not provided:
| Variable | Description | Default |
|---|---|---|
COPILOT2API_HOST |
Server host | 127.0.0.1 |
COPILOT2API_PORT |
Server port | 7777 |
COPILOT2API_TOKEN_DIR |
Token storage directory | ~/.config/copilot2api |
COPILOT2API_DEBUG |
Enable debug logging (true/false, 1/0) |
false |
CLI flags take precedence over environment variables.
docker run -it -p 7777:7777 \
-v ~/.config/copilot2api:/root/.config/copilot2api \
ghcr.io/whtsky/copilot2apiThe Docker image defaults to COPILOT2API_HOST=0.0.0.0 so port forwarding works out of the box. The volume mount persists your GitHub credentials across container restarts. First run will prompt Device Flow authentication.
To use a custom port:
docker run -it -p 8080:8080 \
-v ~/.config/copilot2api:/root/.config/copilot2api \
-e COPILOT2API_PORT=8080 \
ghcr.io/whtsky/copilot2api
⚠️ The Docker image listens on all interfaces by default. Only publish the port to127.0.0.1(e.g.-p 127.0.0.1:7777:7777) unless you know what you're doing.
- Authenticates with GitHub via Device Flow OAuth
- Exchanges GitHub token for Copilot API token (auto-refreshes)
- Proxies OpenAI-format requests directly to Copilot API
- Routes Anthropic Messages requests by model capabilities (native
/v1/messages, translated/responses, or translated/chat/completions) - Automatically detects API endpoint from token (Individual/Business/Enterprise)
go test ./... # Run tests
go build -o copilot2api . # BuildMIT