codex2api-reverse-proxy

v2026.09.25

Codex2API is a production-ready reverse proxy that converts Codex account pools into OpenAI/Anthropic-compatible API gateways with account management, scheduling, and admin dashboard.

GitHub
Install command
npx skhub add reason-machines/codex2api-reverse-proxy
Markdown
SKILL.md

Codex2API Reverse Proxy

Skill by ara.so — Codex Skills collection.

Codex2API is a Go + Gin + React production gateway that transforms a pool of Codex accounts into observable, schedulable OpenAI/Anthropic-compatible API endpoints. It manages Refresh Token/Access Token lifecycles, health scoring, dynamic concurrency, rate-limit recovery, usage tracking, and admin operations through a built-in dashboard.

What It Does

  • Unified Gateway: Exposes /v1/chat/completions, /v1/responses, /v1/messages, /v1/images/generations, /v1/images/edits, and /v1/models endpoints
  • Account Pool Management: Handles Refresh Tokens and Access Tokens with automatic health scoring and cooldown recovery
  • Dynamic Scheduling: Selects accounts based on health tier, concurrency limits, rate limits, and recent usage
  • Admin Dashboard: React/Vite UI for account import, API key management, proxy pools, image studio, prompt filtering, usage analytics
  • Flexible Storage: Production mode (PostgreSQL + Redis) or lightweight mode (SQLite + in-memory cache)

Installation

Standard Production Deployment (PostgreSQL + Redis)

git clone https://github.com/james-6-23/codex2api.git
cd codex2api
cp .env.example .env
# Edit .env with your DATABASE_* and REDIS_* settings
docker compose pull
docker compose up -d
docker compose logs -f codex2api

Lightweight SQLite Deployment

git clone https://github.com/james-6-23/codex2api.git
cd codex2api
cp .env.sqlite.example .env
# Edit .env if needed
docker compose -f docker-compose.sqlite.yml pull
docker compose -f docker-compose.sqlite.yml up -d
docker compose -f docker-compose.sqlite.yml logs -f codex2api

Local Development

cp .env.example .env
# Start PostgreSQL and Redis containers or configure local instances
cd frontend && npm ci && npm run build && cd ..
go run .

Frontend dev server:

cd frontend && npm ci && npm run dev
# Frontend runs at http://localhost:5173/admin/

Configuration

Environment Variables (.env)

Server:

CODEX_PORT=8080
ADMIN_SECRET=your-secure-admin-password
TZ=Asia/Shanghai

PostgreSQL Mode:

DATABASE_DRIVER=postgres
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_USER=codex2api
DATABASE_PASSWORD=secure-db-password
DATABASE_NAME=codex2api
DATABASE_SSLMODE=disable

CACHE_DRIVER=redis
REDIS_ADDR=localhost:6379
REDIS_PASSWORD=secure-redis-password
REDIS_DB=0

SQLite Mode:

DATABASE_DRIVER=sqlite
DATABASE_PATH=/data/codex2api.db
CACHE_DRIVER=memory

Redis TLS (Aiven, Upstash, etc.):

# Prefer rediss:// URL format
REDIS_ADDR=rediss://default:password@host:port/0

# OR for host:port format
REDIS_ADDR=host:port
REDIS_TLS=true
REDIS_INSECURE_SKIP_VERIFY=false  # Set true only for self-signed certs
REDIS_USERNAME=default
REDIS_PASSWORD=your-password

Runtime Settings (Database)

After first startup, configure via admin dashboard at /admin/settings:

  • MaxConcurrency: Global concurrent request limit
  • GlobalRPM: Global requests per minute
  • TestModel: Model for account health checks
  • TestConcurrency: Test request concurrency
  • ProxyURL: Global proxy (e.g., http://proxy:port)
  • PgMaxConns: PostgreSQL connection pool size
  • RedisPoolSize: Redis connection pool size
  • Auto-cleanup settings for logs and usage records

API Usage

OpenAI Chat Completions

package main

import (
	"context"
	"fmt"
	"os"

	"github.com/sashabaranov/go-openai"
)

func main() {
	config := openai.DefaultConfig(os.Getenv("CODEX2API_KEY"))
	config.BaseURL = "http://localhost:8080/v1"
	client := openai.NewClientWithConfig(config)

	resp, err := client.CreateChatCompletion(
		context.Background(),
		openai.ChatCompletionRequest{
			Model: "claude-code",
			Messages: []openai.ChatCompletionMessage{
				{
					Role:    openai.ChatMessageRoleUser,
					Content: "Explain how Codex2API account scheduling works",
				},
			},
		},
	)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	fmt.Println(resp.Choices[0].Message.Content)
}

Anthropic Messages (Compatible Endpoint)

import os
import anthropic

client = anthropic.Anthropic(
    api_key=os.environ.get("CODEX2API_KEY"),
    base_url="http://localhost:8080/v1"
)

message = client.messages.create(
    model="claude-code",
    max_tokens=1024,
    messages=[
        {"role": "user", "content": "Explain dynamic concurrency in Codex2API"}
    ]
)

print(message.content[0].text)

Native Codex Responses

curl -X POST http://localhost:8080/v1/responses \
  -H "Authorization: Bearer $CODEX2API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-code",
    "messages": [
      {
        "role": "user",
        "content": "Write a hello world in Go"
      }
    ],
    "stream": false
  }'

Image Generation

curl -X POST http://localhost:8080/v1/images/generations \
  -H "Authorization: Bearer $CODEX2API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "A serene mountain landscape at sunset",
    "model": "gemini-2.0-flash-exp-image",
    "n": 1,
    "size": "1024x1024"
  }'

Image Editing

curl -X POST http://localhost:8080/v1/images/edits \
  -H "Authorization: Bearer $CODEX2API_KEY" \
  -F "image=@original.png" \
  -F "prompt=Add a rainbow in the sky" \
  -F "model=gemini-2.0-flash-exp-image" \
  -F "n=1"

List Models

curl http://localhost:8080/v1/models \
  -H "Authorization: Bearer $CODEX2API_KEY"

Account Management API

Upload Refresh Tokens

curl -X POST http://localhost:8080/api/admin/accounts/upload \
  -H "X-Admin-Key: $ADMIN_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "tokens": [
      "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
      "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
    ]
  }'

Upload Access Tokens

curl -X POST http://localhost:8080/api/admin/accounts/upload \
  -H "X-Admin-Key: $ADMIN_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "tokens": [
      "sess-abc123...",
      "sess-def456..."
    ],
    "type": "access_token"
  }'

Test Account Health

# Test all accounts
curl -X POST http://localhost:8080/api/admin/accounts/test \
  -H "X-Admin-Key: $ADMIN_SECRET"

# Test specific account
curl -X POST http://localhost:8080/api/admin/accounts/test \
  -H "X-Admin-Key: $ADMIN_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"account_id": "550e8400-e29b-41d4-a716-446655440000"}'

List Accounts

curl http://localhost:8080/api/admin/accounts \
  -H "X-Admin-Key: $ADMIN_SECRET"

Delete Account

curl -X DELETE http://localhost:8080/api/admin/accounts/550e8400-e29b-41d4-a716-446655440000 \
  -H "X-Admin-Key: $ADMIN_SECRET"

API Key Management

Create API Key

curl -X POST http://localhost:8080/api/admin/apikeys \
  -H "X-Admin-Key: $ADMIN_SECRET" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Production Client",
    "key": "sk-custom-key-123",
    "max_rpm": 100,
    "max_tpm": 50000
  }'

Response:

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "name": "Production Client",
  "key": "sk-custom-key-123",
  "max_rpm": 100,
  "max_tpm": 50000,
  "enabled": true,
  "created_at": "2026-05-16T10:30:00Z"
}

List API Keys

curl http://localhost:8080/api/admin/apikeys \
  -H "X-Admin-Key: $ADMIN_SECRET"

Disable/Enable API Key

curl -X PATCH http://localhost:8080/api/admin/apikeys/550e8400-e29b-41d4-a716-446655440000 \
  -H "X-Admin-Key: $ADMIN_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"enabled": false}'

Common Patterns

Account Scheduler Logic

The scheduler selects accounts based on:

  1. Health Tier: Active (healthy) > Cooldown (recovering) > Inactive (failed)
  2. Concurrency: Current concurrent requests < account's max concurrency
  3. Rate Limits: RPM (requests per minute) and TPM (tokens per minute) not exceeded
  4. Score: Weighted by success rate, recent failures, and last success time
  5. Cooldown Recovery: Accounts in cooldown automatically transition to Active after configured interval
// Scheduler picks account with highest score among eligible candidates
// Example internal scoring (simplified):
score := (successRate * 0.5) + 
         (1.0 - recentFailureRate * 0.3) + 
         (timeSinceLastSuccess * 0.2)

Health Check Workflow

# Accounts are tested with TestModel (configured in settings)
# Default: claude-code
# Test sends minimal completion request and validates response

# Health states:
# - Active: Last test succeeded, ready for requests
# - Cooldown: Recent failure, waiting for recovery
# - Inactive: Multiple consecutive failures, excluded from scheduling

Proxy Configuration

Set per-account proxy:

curl -X PATCH http://localhost:8080/api/admin/accounts/550e8400-e29b-41d4-a716-446655440000 \
  -H "X-Admin-Key: $ADMIN_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"proxy_url": "http://proxy.example.com:8080"}'

Global proxy via settings page or environment:

# .env
PROXY_URL=http://global-proxy:8080

Streaming Responses

import os
import anthropic

client = anthropic.Anthropic(
    api_key=os.environ.get("CODEX2API_KEY"),
    base_url="http://localhost:8080/v1"
)

with client.messages.stream(
    model="claude-code",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Count to 10"}]
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)

Prompt Filter (Block/Warn/Modify)

Configure in admin dashboard under "Prompt Filter":

{
  "enabled": true,
  "rules": [
    {
      "pattern": "(?i)nuclear",
      "action": "block",
      "message": "Content violates policy"
    },
    {
      "pattern": "(?i)medical advice",
      "action": "warn",
      "message": "Consider consulting a professional"
    }
  ]
}

Docker Commands

Standard Mode

# Start
docker compose up -d

# View logs
docker compose logs -f codex2api

# Restart
docker compose restart codex2api

# Stop
docker compose down

# Update to latest
docker compose pull && docker compose up -d

SQLite Mode

# Start
docker compose -f docker-compose.sqlite.yml up -d

# Logs
docker compose -f docker-compose.sqlite.yml logs -f codex2api

# Update
docker compose -f docker-compose.sqlite.yml pull
docker compose -f docker-compose.sqlite.yml up -d

Backup and Restore

PostgreSQL:

# Backup
docker exec codex2api-postgres pg_dump -U codex2api codex2api > backup_$(date +%Y%m%d_%H%M%S).sql

# Restore
docker exec -i codex2api-postgres psql -U codex2api codex2api < backup_20260516_103000.sql

SQLite:

# Backup (requires running container)
docker exec codex2api sqlite3 /data/codex2api.db ".backup /data/backup_$(date +%Y%m%d_%H%M%S).db"

# Or copy from host (if /data is mounted)
cp /path/to/data/codex2api.db /path/to/backup/codex2api_$(date +%Y%m%d_%H%M%S).db

Troubleshooting

No Healthy Accounts Available

Symptom: API returns 503 or "No available account"

Solutions:

  1. Check account health in admin dashboard
  2. Run account tests: POST /api/admin/accounts/test
  3. Verify Refresh Tokens are valid (not expired)
  4. Check cooldown settings and wait for recovery
  5. Review logs for authentication failures
# Check account status
curl http://localhost:8080/api/admin/accounts \
  -H "X-Admin-Key: $ADMIN_SECRET" | jq '.[] | {id, email, status, health_tier}'

# Force test all accounts
curl -X POST http://localhost:8080/api/admin/accounts/test \
  -H "X-Admin-Key: $ADMIN_SECRET"

Database Connection Failed

PostgreSQL:

# Test connection
docker exec codex2api-postgres pg_isready -U codex2api

# Check logs
docker logs codex2api-postgres

# Verify .env settings match docker-compose.yml
grep DATABASE_ .env

SQLite:

# Check file permissions
docker exec codex2api ls -la /data/codex2api.db

# Verify mount point
docker inspect codex2api | jq '.[0].Mounts'

Redis Connection Failed

# Test Redis
docker exec codex2api-redis redis-cli ping

# Check authentication
docker exec codex2api-redis redis-cli -a "$REDIS_PASSWORD" ping

# For TLS issues with cloud Redis
# Ensure REDIS_ADDR uses rediss:// or REDIS_TLS=true
# Check REDIS_INSECURE_SKIP_VERIFY if using self-signed certs

Rate Limit Exceeded

Symptom: 429 Too Many Requests

Solutions:

  1. Check API key limits in admin dashboard
  2. Increase max_rpm or max_tpm for the key
  3. Review global GlobalRPM setting
  4. Add more accounts to the pool
  5. Verify account-level rate limits
# Update API key limits
curl -X PATCH http://localhost:8080/api/admin/apikeys/YOUR_KEY_ID \
  -H "X-Admin-Key: $ADMIN_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"max_rpm": 200, "max_tpm": 100000}'

High Concurrency Blocking

Symptom: Requests queue or timeout during high load

Solutions:

  1. Increase MaxConcurrency in settings
  2. Add more healthy accounts
  3. Adjust per-account concurrency limits
  4. Scale horizontally (multiple Codex2API instances with shared PostgreSQL/Redis)
# Check current concurrency
curl http://localhost:8080/api/admin/stats \
  -H "X-Admin-Key: $ADMIN_SECRET" | jq '.current_concurrency'

Image Generation Fails

Common issues:

  1. Model doesn't support images (only gemini-*-image models work)
  2. Image file format not supported (use PNG/JPEG)
  3. File size exceeds limits
  4. No accounts with image capability
# Verify image-capable models
curl http://localhost:8080/v1/models \
  -H "Authorization: Bearer $CODEX2API_KEY" | jq '.data[] | select(.id | contains("image"))'

Admin Dashboard 401 Unauthorized

Symptom: Login fails or /api/admin/* returns 401

Solutions:

  1. Verify ADMIN_SECRET in .env matches login password
  2. Check X-Admin-Key header in requests
  3. Restart after changing ADMIN_SECRET
# Check current admin secret source
docker exec codex2api env | grep ADMIN_SECRET

# Restart to apply .env changes
docker compose restart codex2api

Memory or CPU Usage High

PostgreSQL mode:

# Reduce connection pool size in settings
# Default PgMaxConns: 25, RedisPoolSize: 10

# Monitor resource usage
docker stats codex2api codex2api-postgres codex2api-redis

SQLite mode:

# SQLite is single-threaded; for high concurrency use PostgreSQL
# Check database file size
docker exec codex2api du -h /data/codex2api.db

# Run VACUUM to reclaim space
docker exec codex2api sqlite3 /data/codex2api.db "VACUUM;"

Advanced Configuration

Custom Test Model

# Update via settings API
curl -X PATCH http://localhost:8080/api/admin/settings \
  -H "X-Admin-Key: $ADMIN_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"test_model": "claude-sonnet-4"}'

Auto-Cleanup Policies

Configure in admin settings:

  • AutoCleanupEnabled: Enable automatic cleanup
  • LogRetentionDays: Keep request logs for N days (default 30)
  • UsageRetentionDays: Keep usage records for N days (default 90)

Horizontal Scaling

Multiple Codex2API instances can share PostgreSQL + Redis:

# docker-compose.yml
services:
  codex2api-1:
    image: ghcr.io/james-6-23/codex2api:latest
    environment:
      - DATABASE_HOST=postgres
      - REDIS_ADDR=redis:6379
  codex2api-2:
    image: ghcr.io/james-6-23/codex2api:latest
    environment:
      - DATABASE_HOST=postgres
      - REDIS_ADDR=redis:6379

Use a load balancer (nginx, Caddy, Traefik) to distribute requests.

Health Check Endpoint

curl http://localhost:8080/health

Response:

{
  "status": "healthy",
  "database": "ok",
  "cache": "ok",
  "timestamp": "2026-05-16T10:30:00Z"
}

Use in Kubernetes liveness/readiness probes or Docker healthchecks.

Resources

Discovery
Tags

No tags published for this skill.

Version
Latest version metadata

Version

v2026.09.25

Published

Sep 25, 2026

Category

Uncategorized

License

NOASSERTION

Source path

skills/codex2api-reverse-proxy

Default branch

main

Latest commit

901a4e3

Tree SHA

faa2fa4