Expose Codex Desktop's capabilities as standard OpenAI / Anthropic / Gemini APIs, seamlessly connecting any AI client.
Quick Start • Features • Models • Client Setup • Configuration • Acknowledgements
简体中文 | English
Disclaimer: This project is independently developed and maintained by a single person — built to scratch my own itch. I have my own account pipeline and am not short on tokens; this project exists because I needed it, not to freeload off anyone.
I open-source and maintain this voluntarily. Features get added when I need them; bugs get fixed as soon as I find them. But I am under no obligation to serve any individual user's demands.
Think the code is garbage? Don't use it. Think you can do better? Open a PR and join as a contributor. The issue tracker is for bug reports and suggestions — not feature demands, update nagging, or unsolicited code reviews.
Codex Proxy is a lightweight local gateway that translates the Codex Desktop Responses API into multiple standard protocol endpoints — OpenAI /v1/chat/completions, Anthropic /v1/messages, Gemini, Codex /v1/responses passthrough, and an optional Ollama-compatible /api/chat bridge. Use Codex coding models directly in Cursor, Claude Code, Continue, or any compatible client.
Just a ChatGPT account (or a third-party API key provider) and this proxy — your own personal AI coding assistant gateway, running locally.
Download the installer from GitHub Releases:
| Platform | Installer |
|---|---|
| Windows | Codex Proxy Setup x.x.x.exe |
| macOS | Codex Proxy-x.x.x.dmg |
| Linux | Codex Proxy-x.x.x.AppImage |
Open the app, log in with your ChatGPT account. Dashboard at http://localhost:8080.
mkdir codex-proxy && cd codex-proxy
curl -O https://raw.githubusercontent.com/icebear0828/codex-proxy/master/docker-compose.yml
curl -O https://raw.githubusercontent.com/icebear0828/codex-proxy/master/.env.example
cp .env.example .env
docker compose up -d
# Open http://localhost:8080 to log inData persists in
data/. Cross-container access: use host LAN IP (e.g.192.168.x.x:8080), notlocalhost. Uncomment Watchtower indocker-compose.ymlfor auto-updates. To enable the Ollama-compatible bridge in Docker, see Ollama Bridge configuration.
git clone https://github.com/icebear0828/codex-proxy.git
cd codex-proxy
npm install # Backend dependencies
cd web && npm install && cd .. # Frontend dependencies
npm run dev # Dev mode (hot reload)
# Or: npm run build && npm start # Production modeRequires Rust toolchain (for TLS native addon):
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh cd native && npm install && npm run build && cd ..Docker / desktop app ship pre-built addons — no manual compilation needed.
After logging in, open the dashboard at http://localhost:8080 and find your API Key in the API Configuration section:
# Replace your-api-key with the key shown in the dashboard
curl http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-api-key" \
-d '{"model":"gpt-5.4","messages":[{"role":"user","content":"Hello!"}],"stream":true}'If you see streaming AI text, the setup is working. If you get 401, double-check the API Key.
- Compatible with
/v1/chat/completions(OpenAI),/v1/messages(Anthropic), Gemini, and/v1/responses(Codex passthrough) - Optional built-in Ollama-compatible bridge, defaulting to
http://127.0.0.1:11434 - SSE streaming, works with all OpenAI / Anthropic SDKs and clients
- Automatic bidirectional translation between all protocols and Codex Responses API
- Structured Outputs —
response_format(json_object/json_schema) and GeminiresponseMimeType - Function Calling — native
function_call/tool_callsacross all protocols - Third-party API keys — supports OpenAI / Anthropic / Gemini / OpenRouter / custom OpenAI-compatible providers, routed by model.
- OAuth PKCE login — one-click browser auth
- Multi-account rotation —
least_used,round_robin, andstickystrategies - Plan Routing — accounts on different plans (free/plus/team/business) auto-route to their supported models
- Auto token refresh — JWT renewed before expiry with exponential backoff
- Passive quota collection — updates account quota from upstream response headers and WebSocket rate-limit events;
quota.refresh_interval_minutesonly controls local usage snapshots, and0disables that timer. - Ban detection — upstream 403 auto-marks banned; 401 token invalidation auto-expires and switches account
- API key provider pool — manage third-party API keys, model lists, import/export, and enable/disable state from the dashboard.
- Web dashboard — account management, usage stats, batch operations; dashboard login gate for remote access
- Per-account proxy routing — different upstream proxies per account
- Four assignment modes — Global Default / Direct / Auto / Specific proxy
- Health checks — scheduled + manual, reports exit IP and latency
- Auto-mark unreachable — unreachable proxies excluded from rotation
- Rust Native TLS — built-in reqwest + rustls native addon, TLS fingerprint matches real Codex Desktop exactly (pinned dependency versions)
- Desktop header replication —
originator,User-Agent,x-openai-internal-codex-residency,x-codex-turn-state,x-client-request-idheaders sent per real client behavior - Cookie persistence — automatic Cloudflare cookie capture and replay
- Fingerprint auto-update — polls Codex Desktop update feed, auto-syncs
app_versionandbuild_number
Codex Proxy
┌──────────────────────────────────────────────────────────┐
│ │
│ Client (Cursor / Claude Code / Continue / SDK / ...) │
│ │ │
│ POST /v1/chat/completions (OpenAI) │
│ POST /v1/messages (Anthropic) │
│ POST /v1/responses (Codex passthrough) │
│ POST /gemini/* (Gemini) │
│ │ │
│ ▼ │
│ ┌──────────┐ ┌───────────────┐ ┌──────────────┐ │
│ │ Routes │──▶│ Translation │──▶│ Proxy │ │
│ │ (Hono) │ │ Multi→Codex │ │ Native TLS │ │
│ └──────────┘ └───────────────┘ └──────┬───────┘ │
│ ▲ │ │
│ │ ┌───────────────┐ │ │
│ └──────────│ Translation │◀─────────┘ │
│ │ Codex→Multi │ SSE stream │
│ └───────────────┘ │
│ │
│ ┌──────────┐ ┌───────────────┐ ┌──────────────────┐ │
│ │ Auth │ │ Fingerprint │ │ Model Store │ │
│ │OAuth/API │ │ Rust (rustls) │ │ Static + Dynamic │ │
│ │ API Keys │ │ Headers/UA │ │ Plan Routing │ │
│ └──────────┘ └───────────────┘ └──────────────────┘ │
│ │
└──────────────────────────────────────────────────────────┘
│
Rust Native Addon (napi-rs)
reqwest 0.12.28 + rustls 0.23.36
(TLS fingerprint = real Codex Desktop)
│
┌──────┴──────┐
▼ ▼
chatgpt.com 3rd-party providers
/backend-api/codex (3rd-party API)
| Model ID | Reasoning | Current context | Max context | Max output | Output | Description |
|---|---|---|---|---|---|---|
gpt-5.5 |
low / medium / high / xhigh | 272,000 | 272,000 | 128,000 | text | Frontier model for complex coding, research, and real-world work |
gpt-5.4 |
low / medium / high / xhigh | 272,000 | 1,000,000 | 128,000 | text | Strong model for everyday coding (default) |
gpt-5.4-mini |
low / medium / high / xhigh | 400,000 | — | 128,000 | text | GPT-5.4 lightweight model |
gpt-5.3-codex |
low / medium / high / xhigh | 400,000 | — | 128,000 | text | GPT-5.3 coding-optimized model |
gpt-5.2 |
low / medium / high / xhigh | 400,000 | — | 128,000 | text | Professional work & long-running agents |
gpt-5-codex |
low / medium / high | 400,000 | — | 128,000 | text | GPT-5 coding-optimized model |
gpt-5-codex-mini |
medium / high | — | — | — | text | Lightweight Codex / CLI coding model |
gpt-oss-120b |
low / medium / high | 131,072 | — | — | text | Open-source 120B model |
gpt-oss-20b |
low / medium / high | 131,072 | — | — | text | Open-source 20B model |
gpt-image-2 |
— | — | — | — | image | Image-generation tool backend, invoked via image_generation |
Suffixes: Append
-fastto any chat model for Fast mode,-high/-lowfor reasoning effort. E.g.gpt-5.4-fast,gpt-5.4-high-fast. The image model (gpt-image-2) does not take suffixes.Plan Routing: Accounts on different plans auto-route to the models returned for that account by the Codex backend. Do not treat old Plus-only notes as fixed model access rules. Models are dynamically fetched and auto-synced; if a model appears in the Dashboard or
/v1/models/catalog, it can be used as the requestmodel.Dashboard model picker ≠ config file: Changing the model in the Dashboard only affects the UI display and API examples — it does not modify
model.defaultinconfig/default.yamlordata/local.yaml. The actual model used is determined by themodelfield in each client request (Cursor, Claude Code, etc.). Themodel.defaultconfig is only a fallback when the client omits the model field.Max token note: the table follows the current
config/models.yamland Codex runtime/v1/models/catalogmetadata.—means the current catalog does not return that field, not that the model is unavailable. Runtime data fetched from the Codex backend overrides static values and preservescontextWindow,maxContextWindow,maxOutputTokens, andtruncationPolicyLimit. Request fields such ascontext_window,max_context_window,truncation_policy, andmax_output_tokensare not usable switches; forwarding them to the native Codex API returns400 Unsupported parameter.
Image generation rides on /v1/responses via the built-in image_generation tool; the backend is always gpt-image-2.
Prerequisite: a ChatGPT Plus or higher account (free accounts have the tool silently stripped by upstream, and the model falls back to replying with an SVG snippet).
curl -N http://localhost:8080/v1/responses \
-H "Authorization: Bearer $PROXY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.5",
"stream": true,
"input": [{"role":"user","content":"Draw a red circle on white background."}],
"tools": [{"type":"image_generation","size":"3840x2160"}]
}'Tunable fields: size (1024×1024 / 1024×1536 / 1536×1024 / 2048×2048 / 2048×3072 / 3072×2048 / 3840×2160 (4K UHD) / auto; longest edge ≤ 3840 px, pixel budget ≈ 8 MP), output_format (png / jpeg / webp), output_compression (jpeg / webp only), background (auto / opaque), moderation (auto / low), partial_images (0–3). Upstream forces model = gpt-image-2 and rejects n, input_image, mask, input_fidelity, style, response_format. See API.md for the full matrix.
In the stream, the image_generation_call item's result field is a base64-encoded image; revised_prompt contains the final prompt used by the model.
Edit mode (with a reference image): include {"type":"input_image","image_url":"data:image/png;base64,..."} in the user message content array.
The
/v1/chat/completionscompatibility path accepts theimage_generationtool so OpenAI clients do not fail schema validation, but image payloads are only exposed reliably through/v1/responsesasimage_generation_call.result. Use/v1/responseswhen you need the image bytes.
Get your API Key from the dashboard (
http://localhost:8080). Use a concrete model ID (defaultgpt-5.4) or any model ID as the model name.
export ANTHROPIC_BASE_URL=http://localhost:8080
export ANTHROPIC_API_KEY=your-api-key
# Switch model: export ANTHROPIC_MODEL=gpt-5.4 / gpt-5.4-fast / gpt-5.4-mini ...
claudeCopy env vars from the Anthropic SDK Setup card in the dashboard (includes Opus / Sonnet / Haiku tier model config).
Recommended models: Opus →
gpt-5.4, Sonnet →gpt-5.3-codex, Haiku →gpt-5.4-mini.
~/.codex/config.toml:
[model_providers.proxy_codex]
name = "Codex Proxy"
base_url = "http://localhost:8080/v1"
wire_api = "responses"
# Inline the API Key (recommended for local single-user setups)
[model_providers.proxy_codex.http_headers]
Authorization = "Bearer your-api-key"
[profiles.default]
model = "gpt-5.4"
model_provider = "proxy_codex"💡 To keep the key out of the config file (shared machine / open-source repo), drop the
http_headersblock and useenv_key = "PROXY_API_KEY"instead, thenexport PROXY_API_KEY=your-api-key && codex.
- Enable Developer Mode: Click menu Help → Troubleshooting → Enable Developer Mode.
- Configure Third-Party Inference: Click the new Developer menu → Configure Third-Party Inference....
- Fill in details:
- Endpoint:
http://127.0.0.1:8080 - API Key: your-api-key
- Model:
claude-opus-4-7/claude-sonnet-4-6/claude-haiku-4-5
- Endpoint:
Alternatively, edit the config file (usually a JSON file in
%APPDATA%\Claude-3p\configLibrary\on Windows, or~/Library/Application Support/Claude-3p/configLibrary/on Mac), adding the following fields:{ "disableDeploymentModeChooser": true, "inferenceProvider": "gateway", "inferenceGatewayBaseUrl": "http://127.0.0.1:8080", "inferenceGatewayApiKey": "your-api-key", "inferenceGatewayAuthScheme": "bearer", "inferenceModels": [ "claude-opus-4-7", "claude-sonnet-4-6", "claude-haiku-4-5" ] }
Built-in Claude-shaped model names map to Codex models. Put custom mappings in data/local.yaml; do not edit config/models.yaml:
model:
aliases:
claude-opus-4-7: gpt-5.5
claude-sonnet-4-6: gpt-5.4
claude-haiku-4-5: gpt-5.3-codex
my-openai: openai:gpt-4o
my-deepseek: deepseek-chatThe left side is the model name used by the client; the right side is the real upstream model. The target can be a Codex model ID, a provider-prefixed model such as openai:gpt-4o / anthropic:claude-sonnet-4-5 / gemini:gemini-2.5-pro, or a model already bound to a custom provider via model_routing such as deepseek-chat. Aliases appear in /v1/models; direct provider requests rewrite the outgoing model to the mapped target.
💡 Troubleshooting (Windows): If Claude Desktop shows
ERR_CONNECTION_REFUSEDwhen using127.0.0.1(andmust use httpswhen usinglocalhost), it means Node.js is only binding to IPv6 by default. Go to the Codex Proxy dashboard settings, change Host to127.0.0.1, or addserver: { host: "127.0.0.1" }todata/local.yamland restart the proxy.💡 LAN Usage Tip: Claude Desktop strictly validates the endpoint and only allows
https://or exactlyhttp://127.0.0.1. If your proxy is on another machine in the LAN (e.g.192.168.x.x), you cannot use it directly via HTTP. Workarounds:
- SSH Tunnel (Easiest): Run
ssh -L 8080:127.0.0.1:8080 user@192.168.x.xon your client machine, then usehttp://127.0.0.1:8080in Claude.- Reverse Proxy: Setup Caddy or Nginx with a valid HTTPS certificate for your LAN IP.
The official client shares configuration with the CLI. Restart the app after editing.
~/.codex/config.toml:
[model_providers.proxy_codex]
name = "Codex Proxy"
base_url = "http://localhost:8080/v1"
wire_api = "responses"
[model_providers.proxy_codex.http_headers]
Authorization = "Bearer your-api-key"
[profiles.default]
model = "gpt-5.4"
model_provider = "proxy_codex"💡 Why not
env_key? macOS/Windows GUI apps do not inherit env vars from your shell rc files —export PROXY_API_KEY=...in your terminal is invisible to the GUI process and Codex Desktop will fail withMissing environment variable. InliningAuthorizationviahttp_headersavoidslaunchctl setenv/ LaunchAgent gymnastics. Switch back toenv_key = "PROXY_API_KEY"only when you need the key out of the config file.
⚠️ When logged in via "ChatGPT account", existing sessions might bypass this config and hit the official upstream directly. New sessions started after[model_providers.proxy_codex]is wired up +profiles.default.model_provider = "proxy_codex"will route through the proxy.
Open Claude extension settings → API Configuration:
- API Provider: Anthropic
- Base URL:
http://localhost:8080 - API Key: your API key
- Settings → Models → OpenAI API
- Base URL:
http://localhost:8080/v1 - API Key: your API key
- Add model
gpt-5.4
- Settings → AI Provider → OpenAI Compatible
- API Base URL:
http://localhost:8080/v1 - API Key: your API key
- Model:
gpt-5.4
- Cline sidebar → gear icon
- API Provider: OpenAI Compatible
- Base URL:
http://localhost:8080/v1 - API Key: your API key
- Model ID:
gpt-5.4
~/.continue/config.json:
{
"models": [{
"title": "Codex",
"provider": "openai",
"model": "gpt-5.4",
"apiBase": "http://localhost:8080/v1",
"apiKey": "your-api-key"
}]
}aider --openai-api-base http://localhost:8080/v1 \
--openai-api-key your-api-key \
--model openai/gpt-5.4- Settings → Model Services → Add
- Type: OpenAI
- API URL:
http://localhost:8080/v1 - API Key: your API key
- Add model
gpt-5.4
Enable it in Dashboard → Settings → Ollama Bridge, then use the default Ollama base URL:
| Setting | Value |
|---|---|
| Base URL | http://localhost:11434 |
| API Key | Not required; the bridge uses the Codex Proxy key internally |
| Model | gpt-5.4 (or any model ID) |
curl http://localhost:11434/api/tags
curl http://localhost:11434/api/chat \
-H "Content-Type: application/json" \
-d '{"model":"gpt-5.4","messages":[{"role":"user","content":"Hello!"}],"stream":true}'The Ollama API has no authentication. The bridge listens on
127.0.0.1by default; do not expose it to the public internet or untrusted LANs.
| Setting | Value |
|---|---|
| Base URL | http://localhost:8080/v1 |
| API Key | from dashboard |
| Model | gpt-5.4 (or any model ID) |
SDK examples (Python / Node.js)
Python
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8080/v1", api_key="your-api-key")
for chunk in client.chat.completions.create(
model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}], stream=True
):
print(chunk.choices[0].delta.content or "", end="")Node.js
import OpenAI from "openai";
const client = new OpenAI({ baseURL: "http://localhost:8080/v1", apiKey: "your-api-key" });
const stream = await client.chat.completions.create({
model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content || "");
}All configuration in config/default.yaml:
| Section | Key Settings | Description |
|---|---|---|
server |
host, port, proxy_api_key |
Listen address and API key |
api |
base_url, timeout_seconds |
Upstream API URL and timeout |
client |
app_version, build_number, chromium_version |
Codex Desktop version to impersonate |
model |
default, default_reasoning_effort, default_service_tier, aliases, custom_models, inject_desktop_context |
Default model, reasoning config, aliases, and custom catalog entries |
auth |
rotation_strategy, rate_limit_backoff_seconds |
Rotation strategy and rate limit backoff |
tls |
proxy_url, force_http11 |
TLS proxy and HTTP version |
quota |
refresh_interval_minutes, warning_thresholds, skip_exhausted |
Usage snapshots, threshold config, exhausted-account skipping |
session |
ttl_minutes, cleanup_interval_minutes |
Dashboard session management |
ollama |
enabled, host, port, version, disable_vision |
Ollama-compatible bridge |
model.aliases maps client-facing model names to the real upstream model. This is useful when Claude Desktop / Cursor / Continue only lets you pick certain model IDs, or when you want shorter local names.
You can also manage aliases in Dashboard → Settings → Model Aliases. Saving writes to data/local.yaml and hot-reloads the backend, so you do not need to edit config/default.yaml.
model:
aliases:
claude-opus-4-7: gpt-5.5
sonnet-local: gpt-5.4
openai-fast: openai:gpt-4o
deepseek-local: deepseek-chat
providers:
custom:
deepseek:
api_key: "sk-..."
base_url: "https://api.deepseek.com/v1"
models: ["deepseek-chat"]
model_routing:
deepseek-chat: deepseekAlias resolution runs before model_routing and built-in Claude/Gemini auto-routing. Aliases targeting Codex models still work with Codex suffixes such as -fast / -high; aliases targeting third-party providers rewrite the outgoing direct request model field to the mapped target.
If you need to add fully custom Codex-compatible model IDs to the catalog, configure model.custom_models in data/local.yaml. A string entry uses default text/medium metadata; an object entry can define display name, reasoning efforts, context, and output limits:
model:
custom_models:
- local-simple
- id: local-rich
display_name: Local Rich
description: Local rich model
supported_reasoning_efforts: [low, high]
default_reasoning_effort: high
input_modalities: [text, image]
output_modalities: [text]
context_window: 12345
max_context_window: 23456
max_output_tokens: 3456When quota.skip_exhausted: true, the account pool skips accounts whose cached quota is already exhausted before session affinity / preferredEntryId is applied. A long conversation therefore cannot force routing back to a cached-exhausted account.
The skip condition is currently rate_limit.limit_reached === true, secondary_rate_limit.limit_reached === true, or code_review_rate_limit.limit_reached === true in cached quota. If used_percent is merely near 100, for example 99%, but upstream has not set limit_reached, the proxy may still use that account. Once upstream returns 429, the account is marked rate_limited, enters backoff, and the request is retried with another available account. Secondary and code-review windows are removed from cache after their own reset_at passes, so an account is not skipped forever on stale quota data.
ollama:
enabled: false # true = start the built-in Ollama-compatible listener
host: 127.0.0.1 # localhost-only by default
port: 11434 # Ollama default port
version: "0.18.3" # value returned by /api/version
disable_vision: false # true = do not advertise vision in /api/showSupported Ollama endpoints:
| Endpoint | Method | Description |
|---|---|---|
http://localhost:11434/api/version |
GET | Ollama version probe |
http://localhost:11434/api/tags |
GET | Model list |
http://localhost:11434/api/show |
POST | Model metadata |
http://localhost:11434/api/chat |
POST | Chat completions with streaming NDJSON |
http://localhost:11434/v1/* |
Any | OpenAI /v1 passthrough |
For Docker deployments that need host access to 11434:
- Set
ollama.enabled: trueandollama.host: 0.0.0.0in the Dashboard ordata/local.yaml. - Uncomment the
127.0.0.1:${OLLAMA_BRIDGE_PORT:-11434}:11434port mapping indocker-compose.yml. - Keep the host binding on
127.0.0.1unless you intentionally want to expose an unauthenticated Ollama API.
Browser CORS access is limited to loopback origins such as localhost, 127.x.x.x, and ::1; non-local web origins are not allowed to read bridge responses. The bridge injects the configured Codex Proxy API key for /v1/* passthrough requests, so exposing it beyond localhost effectively grants unauthenticated access to the main proxy API.
The source/Docker default config listens on :: (IPv6 unspecified, usually still reachable from localhost). Electron passes 127.0.0.1 at startup unless data/local.yaml explicitly overrides server.host. To force localhost-only binding:
server:
host: "127.0.0.1"To allow LAN access, set server.host: "0.0.0.0" in data/local.yaml and use a strong proxy API key.
server:
proxy_api_key: "pwd" # clients use Authorization: Bearer pwd
# proxy_api_key: null # no global key; logged-in accounts still have account-level codex-proxy-xxxx keysOn first startup, if data/local.yaml is missing, Codex Proxy creates it with server.proxy_api_key: pwd. The active key is shown in the dashboard API Configuration section.
| Variable | Overrides |
|---|---|
PORT |
server.port |
CODEX_PLATFORM |
client.platform |
CODEX_ARCH |
client.arch |
HTTPS_PROXY |
tls.proxy_url |
OLLAMA_BRIDGE_ENABLED |
ollama.enabled |
OLLAMA_BRIDGE_HOST |
ollama.host |
OLLAMA_BRIDGE_PORT |
ollama.port |
OLLAMA_BRIDGE_VERSION |
ollama.version |
OLLAMA_BRIDGE_DISABLE_VISION |
ollama.disable_vision |
Click to expand main endpoint list
Protocol Endpoints
| Endpoint | Method | Description |
|---|---|---|
/v1/chat/completions |
POST | OpenAI format chat completions |
/v1/responses |
POST | Codex Responses API passthrough |
/v1/responses/compact |
POST | Codex compact response proxy |
/v1/messages |
POST | Anthropic format chat completions |
/v1/models |
GET | List available models |
/v1/models/catalog |
GET | Full model catalog for the dashboard |
/v1/models/:modelId/info |
GET | Reasoning and metadata for one model |
/v1beta/models |
GET | Gemini-format model list |
/v1beta/models/:modelAction |
POST | Gemini generateContent / streamGenerateContent |
:11434/api/chat |
POST | Ollama-compatible chat completions (requires Ollama Bridge) |
Auth & Accounts
| Endpoint | Method | Description |
|---|---|---|
/auth/login |
GET | OAuth login entry |
/auth/accounts |
GET | Account list (?quota=true / ?quota=fresh) |
/auth/accounts |
POST | Add single account (token or refreshToken) |
/auth/accounts/import |
POST | Bulk import accounts |
/auth/accounts/export |
GET | Export accounts (?format=minimal for compact) |
/auth/accounts/batch-delete |
POST | Batch delete accounts |
/auth/accounts/batch-status |
POST | Batch update account status |
/auth/accounts/health-check |
POST | Batch account health check |
/auth/accounts/:id/refresh |
POST | Refresh and probe one account |
/auth/accounts/:id/quota |
GET | Actively query one account quota |
/auth/accounts/:id/cookies |
GET/POST/DELETE | Manage account Cloudflare cookies |
/auth/quota/warnings |
GET | Current quota warning state |
Third-Party API Keys
| Endpoint | Method | Description |
|---|---|---|
/auth/api-keys/catalog |
GET | Built-in providers and suggested model catalog |
/auth/api-keys |
GET/POST | List / add API keys |
/auth/api-keys/models |
POST | Fetch models from a custom OpenAI-compatible provider |
/auth/api-keys/export |
GET | Export API key config |
/auth/api-keys/import |
POST | Import API key config |
/auth/api-keys/batch-delete |
POST | Batch delete API keys |
/auth/api-keys/:id |
DELETE | Delete one API key |
/auth/api-keys/:id/label |
PATCH | Update API key label |
/auth/api-keys/:id/status |
PATCH | Enable or disable an API key |
Account Import/Export Examples
# Export all accounts (full format with tokens)
curl -s http://localhost:8080/auth/accounts/export \
-H "Authorization: Bearer your-api-key" > backup.json
# Export minimal format (refreshToken + label only, safe to share)
curl -s "http://localhost:8080/auth/accounts/export?format=minimal" \
-H "Authorization: Bearer your-api-key" > backup-minimal.json
# Bulk import (token, refreshToken, or both)
curl -X POST http://localhost:8080/auth/accounts/import \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-api-key" \
-d '{
"accounts": [
{ "token": "eyJhbGciOi..." },
{ "refreshToken": "v1.abc..." },
{ "refreshToken": "v1.def...", "label": "Backup" }
]
}'
# Returns: { "added": 2, "updated": 1, "failed": 0, "errors": [] }
# One-step backup restore (export file → import to another instance)
curl -X POST http://localhost:8080/auth/accounts/import \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-api-key" \
-d @backup.jsonAdmin
| Endpoint | Method | Description |
|---|---|---|
/admin/rotation-settings |
GET/POST | Rotation strategy config |
/admin/quota-settings |
GET/POST | Quota refresh & warning config |
/admin/ollama-settings |
GET/POST | Ollama Bridge config |
/admin/ollama-status |
GET | Ollama Bridge runtime status |
/admin/refresh-models |
POST | Trigger manual model list refresh |
/admin/usage-stats/summary |
GET | Usage stats summary |
/admin/usage-stats/history |
GET | Usage time series |
/admin/logs |
GET | Request log list |
/admin/logs/state |
GET/POST | Log capture settings |
/admin/update-status |
GET | Self-update status |
/admin/check-update |
POST | Check for updates |
/admin/apply-update |
POST | Apply self-update |
/health |
GET | Health check |
Proxy Pool
| Endpoint | Method | Description |
|---|---|---|
/api/proxies |
GET/POST | List / add proxies |
/api/proxies/:id |
PUT/DELETE | Update / remove proxy |
/api/proxies/:id/check |
POST | Health check single proxy |
/api/proxies/check-all |
POST | Health check all proxies |
/api/proxies/assign |
POST | Assign proxy to account |
/api/proxies/assignments |
GET | View account proxy assignments |
/api/proxies/assign-bulk |
POST | Bulk assign proxies |
/api/proxies/assign-rule |
POST | Rule-based proxy assignment |
/api/proxies/export |
GET | Export proxy pool YAML |
/api/proxies/import |
POST | Import proxy pool YAML |
- Node.js 18+ (20+ recommended)
- Rust — required for source builds (compiles TLS native addon); Docker / desktop app ship pre-built
- ChatGPT account — free account is sufficient
- Docker (optional)
- Codex API is stream-only.
stream: falsecauses the proxy to stream internally and return assembled JSON. - This project relies on Codex Desktop's public API. Upstream updates are auto-detected and fingerprints auto-synced.
- Windows source builds need Rust toolchain for the TLS native addon. Docker deployment has it pre-built.
Codex Proxy is primarily maintained by one person, but it has been improved by a lot of community help. Special thanks to these contributors who submitted code, documentation, fixes, or PRs:
@SsuJojo · @TutuchanXD · @kanweiwei · @et2010 · @d-demand-priv · @hangox · @jarvisluk · @jeasonstudio · @JPClaw12 · @lezi-fun · @lookvincent · @pocper1 · @woai66 · @xsShuang · @yuwei5380
Thanks as well to everyone who opened Issues with bug reproductions, logs, compatibility reports, and feature suggestions. Those reports directly shaped account rotation, proxy compatibility, the Dashboard, Ollama Bridge, model compatibility, and error observability.
Non-Commercial license:
- Allowed: Personal learning, research, self-hosted deployment
- Prohibited: Any commercial use including selling, reselling, paid proxy services, or commercial product integration
Not affiliated with OpenAI. Users assume all risks and must comply with OpenAI's Terms of Service.


