P5 - Ultimate OpenClaw CLIProxy Setup (Free Multi API)
Step 0) Objectives
• Run CLIProxyAPI locally on VPS: 127.0.0.1:8317
• Add multiple Codex/ChatGPT accounts (device login) → proxy automatically rotates accounts
• OpenClaw points the openai-codex provider to CLIProxyAPI (instead of chatgpt.com)
• CLIProxyAPI runs as a service (systemd user), automatically starts on reboot
Step 1) Install Go 1.22.5
cd /tmp
wget https://go.dev/dl/go1.22.5.linux-amd64.tar.gz
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go1.22.5.linux-amd64.tar.gz
sudo ln -sf /usr/local/go/bin/go /usr/local/bin/go
sudo ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt
Check the version
which go
go version
Step 2) Clone CLIProxyAPI and build the binary
cd ~
rm -rf CLIProxyAPI
git clone https://github.com/router-for-me/CLIProxyAPI.git
cd ~/CLIProxyAPI
go build -o cli-proxy-api ./cmd/server
./cli-proxy-api -h
Step 3) Create the CLIProxyAPI configuration
Suggested way to create a proxy API key (create using Python):
python3 - <<'PY'
import secrets
print(secrets.token_urlsafe(32))
PY
Key: sMPIdzRqbikhhHZ04QD9DHhvwKo32SbinpkkvVNQ9Mo
nano ~/CLIProxyAPI/config.yaml
Content
host: "127.0.0.1"
port: 8317
tls:
enable: false
cert: ""
key: ""
remote-management:
allow-remote: false
secret-key: ""
disable-control-panel: true
panel-github-repository: "https://github.com/router-for-me/Cli-Proxy-API-Management-Center"
auth-dir: "/home/bao/.cli-proxy-api"
api-keys:
- "sMPIdzRqbikhhHZ04QD9DHhvwKo32SbinpkkvVNQ9Mo"
debug: true
usage-statistics-enabled: true
routing:
strategy: "round-robin"
Save file : Ctrl + O enter, Ctrl X exit
Note: must be in the correct format
Step 5) Add Codex/ChatGPT account Go to CLIProxyAPI (device login)
Enable Codex in Chatgpt settings for accounts
Setting => Security => Enable device code author...
Add account #1
cd ~/CLIProxyAPI
./cli-proxy-api -codex-device-login -config ./config.yaml -no-browser
It will print out:
• URL: https://auth.openai.com/codex/device
• CODE: XXXX-XXXXX
Open the URL on the local computer → enter code → login account #1.
Add account #2 (repeat)
Run the correct command again:
cd ~/CLIProxyAPI
./cli-proxy-api -codex-device-login -config ./config.yaml -no-browser
After success, the proxy will create the auth file at:
ls -la ~/.cli-proxy-api
# There will be 2 codex-*.json files
# Test model list
cd ~/CLIProxyAPI
nohup ./cli-proxy-api -config ./config.yaml > proxy.log 2>&1 &
curl -sS -H "Authorization: Bearer sMPIdzRqbikhhHZ04QD9DHhvwKo32SbinpkkvVNQ9Mo" http://127.0.0.1:8317/v1/models
Step 6) Point OpenClaw to CLIProxyAPI
OpenClaw is using the openai-codex provider. We edit the conf file
Use python to insert files
python3 - <<'PY'
import json
p = "/home/bao/.openclaw/openclaw.json"
obj = json.load(open(p))
obj.setdefault("models", {})
obj["models"]["mode"] = "merge"
providers = obj["models"].setdefault("providers", {})
providers["openai-codex"] = {
"baseUrl": "http://127.0.0.1:8317/v1",
"api": "openai-completions",
"headers": {
"Authorization": "Bearer sMPIdzRqbikhhHZ04QD9DHhvwKo32SbinpkkvVNQ9Mo"
},
"models": []
}
with open(p, "w") as f:
json.dump(obj, f, indent=2)
f.write("\n")
print("updated", p)
PY
Validate config
openclaw config validate --json
Should output {"valid":true,...}.
Restart the gateway to regenerate agents/main/agent/models.json
systemctl --user restart openclaw-gateway
Check
cat ~/.openclaw/agents/main/agent/models.json
Step 7) Restart OpenClaw Gateway
systemctl --user restart openclaw-gateway
Then test normal Telegram messaging.
Step 8) Run CLIProxyAPI as a service (run automatically after reboot)
Create service file:
sudo nano ~/.config/systemd/user/cliproxyapi.service
Content
[Unit]
Description=CLIProxyAPI (local OpenAI-compatible proxy)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
WorkingDirectory=%h/CLIProxyAPI
ExecStart=%h/CLIProxyAPI/cli-proxy-api -config %h/CLIProxyAPI/config.yaml
Restart=on-failure
RestartSec=2
NoNewPrivileges=true
PrivateTmp=true
[Install]
WantedBy=default.target
Enable + start:
systemctl --user daemon-reload
systemctl --user enable --now cliproxyapi.service
Log Follow:
journalctl --user -u cliproxyapi -f
Step 9) Option Note
routing.strategy: What is round-robin, and where is it located?
It's a strategy for selecting accounts/credentials when a proxy has multiple valid accounts for the same model.
• round-robin = alternating (A → B → A → B…)
• fill-first = use up the priority account first, then move on to the next account
It's located in the CLIProxyAPI configuration file:
nano ~/CLIProxyAPI/config.yaml
# Restart proxy
systemctl --user restart cliproxyapi
# Restart gateway
systemctl --user restart openclaw-gateway
Step 10) Enable usage quota checking
#0 Create secret key
Create a secret key (you can use py)
(python creates plaintext key)
python3 - <<'PY'
import secrets
print(secrets.token_urlsafe(32))
PY
Key: rDdnKJu4JYNMnlVp8WRr-7tGgXSBtzMib2c_KQqzs4I
nano ~/CLIProxyAPI/config.yaml
Then restart the service:
systemctl --user restart cliproxyapi
When the service starts, it will automatically hash the secret (bcrypt) and overwrite the secret key in the file.
Restart service:
systemctl --user restart cliproxyapi
#1 Create key management file (to avoid copying the key every time)
echo 'rDdnKJu4JYNMnlVp8WRr-7tGgXSBtzMib2c_KQqzs4I' > ~/.cli-proxy-api/.mgmt_key
chmod 600 ~/.cli-proxy-api/.mgmt_key
#2 Create main Python script:
Create folder
mkdir -p ~/bin
Run python command to write file
cat > ~/bin/cliproxy_stats.py <<'PY'
#!/usr/bin/env python3
import argparse
import json
from datetime import datetime, timezone
try:
from zoneinfo import ZoneInfo
except Exception:
ZoneInfo = None
from urllib.request import Request, urlopen
DEFAULT_URL = "http://127.0.0.1:8317/v0/management/usage"
DEFAULT_AUTHFILES_URL = "http://127.0.0.1:8317/v0/management/auth-files"
DEFAULT_KEY_FILE = "/home/bao/.cli-proxy-api/.mgmt_key"
def fetch_json(url: str, key: str) -> dict:
req = Request(url, headers={"X-Management-Key": key})
with urlopen(req, timeout=10) as resp:
body = resp.read().decode("utf-8", errors="replace")
return json.loads(body)
def iter_details(usage_json: dict):
apis = usage_json.get("usage", {}).get("apis", {})
for _api_key, api in apis.items():
for model, model_info in (api.get("models", {}) or {}).items():
for det in (model_info.get("details", []) or []):
yield model, det
def auth_status_map(auth_files_json: dict) -> dict:
out = {}
files = auth_files_json.get("files", []) or []
for f in files:
email = f.get("email") or f.get("account") or f.get("label")
if not email:
continue
out[email] = {
"status": f.get("status", ""),
"disabled": bool(f.get("disabled", False)),
}
return out
def print_table(rows, headers, limit=None):
if limit is not None:
rows = rows[-limit:]
widths = []
for i, h in enumerate(headers):
w = len(h)
for r in rows:
w = max(w, len(str(r[i])))
widths.append(w)
fmt = " ".join("{:<%d}" % w for w in widths)
print(fmt.format(*headers))
print(fmt.format(*["-" * w for w in widths]))
for r in rows:
print(fmt.format(*[str(x) for x in r]))
def cmd_recent(usage: dict, n: int):
rows = []
for model, det in iter_details(usage):
t = det.get("tokens", {}) or {}
rows.append([
det.get("timestamp", ""),
model,
det.get("source", ""),
det.get("latency_ms", ""),
t.get("input_tokens", 0) or 0,
t.get("output_tokens", 0) or 0,
t.get("cached_tokens", 0) or 0,
t.get("total_tokens", 0) or 0,
"Y" if det.get("failed") else "N",
])
rows.sort(key=lambda r: r[0])
headers = ["time", "model", "account", "lat_ms", "in_tok", "out_tok", "cached", "total", "fail"]
print_table(rows, headers, limit=n)
def cmd_today(usage: dict, authfiles: dict, tz_name: str = "Asia/Ho_Chi_Minh"):
tz = ZoneInfo(tz_name) if ZoneInfo else timezone.utc
today = datetime.now(tz).date().isoformat()
status = auth_status_map(authfiles)
agg = {}
for model, det in iter_details(usage):
ts = det.get("timestamp", "")
if not ts.startswith(today):
continue
acct = det.get("source", "unknown")
t = det.get("tokens", {}) or {}
a = agg.setdefault(acct, {"req": 0, "in": 0, "out": 0, "cached": 0, "total": 0, "fail": 0})
a["req"] += 1
a["in"] += int(t.get("input_tokens", 0) or 0)
a["out"] += int(t.get("output_tokens", 0) or 0)
a["cached"] += int(t.get("cached_tokens", 0) or 0)
a["total"] += int(t.get("total_tokens", 0) or 0)
a["fail"] += 1 if det.get("failed") else 0
print(f"date({tz_name}) = {today}")
accounts = sorted(status.keys() | agg.keys())
rows = []
for acct in accounts:
v = agg.get(acct, {"req": 0, "in": 0, "out": 0, "cached": 0, "total": 0, "fail": 0})
s = status.get(acct, {})
st = s.get("status", "")
dis = "Y" if s.get("disabled") else "N"
rows.append([acct, st, dis, v["req"], v["in"], v["out"], v["cached"], v["total"], v["fail"]])
headers = ["account", "status", "disabled", "req", "in_tok", "out_tok", "cached", "total", "fail"]
print_table(rows, headers)
def main():
ap = argparse.ArgumentParser(description="CLIProxyAPI management usage viewer")
ap.add_argument("--url", default=DEFAULT_URL)
ap.add_argument("--key-file", default=DEFAULT_KEY_FILE)
sub = ap.add_subparsers(dest="cmd", required=True)
p1 = sub.add_parser("recent", help="show last N requests")
p1.add_argument("-n", type=int, default=40)
p2 = sub.add_parser("today", help="show today's totals per account")
p2.add_argument("--tz", default="Asia/Ho_Chi_Minh")
args = ap.parse_args()
with open(args.key_file, "r", encoding="utf-8") as f:
key = f.read().strip()
usage = fetch_json(args.url, key)
authfiles = fetch_json(DEFAULT_AUTHFILES_URL, key)
if args.cmd == "recent":
cmd_recent(usage, args.n)
else:
cmd_today(usage, authfiles, tz_name=args.tz)
if __name__ == "__main__":
main()
PY
Permissions
chmod +x ~/bin/cliproxy_stats.py
#3 Create a short bash wrapper: ~/bin/cliproxy-stats
cat > ~/bin/cliproxy-stats << 'SH'
#!/usr/bin/env bash
set -euo pipefail
exec /home/bao/bin/cliproxy_stats.py "$@"
SH
Permissions
chmod +x ~/bin/cliproxy-stats
#4 Execute
• View today's total + which account is active/disabled:
~/bin/cliproxy-stats today
• View recent requests running on which account (to ensure it "fill-first" correctly):
~/bin/cliproxy-stats recent -n 30
In conclusion: If you want it to be faster, just copy and paste my steps and let the AI do it automatically.
See also related articles
10 Powerful AI Business Applications to Boost Growth in 2025
Discover 10 powerful AI business applications that can transform your operations, boost efficiency, and drive growth. Learn how to implement AI today.
Read MoreP10 – Uninstall OpenClaw Windows Fast
P10 – Uninstall OpenClaw Windows Fast https://youtu.be/1ljEMzohiSY 🚀 AI Tutorial – P10: Uninstall OpenClaw on Windows (Clean Removal & Fix Issues) If you’re facing issues with OpenClaw or simply want to remove it completely, performing a proper Uninstall OpenClaw Windows process is essential. A partial uninstall may leave behind background...
Read MoreP9 – Build Local AI Telegram Bot Fast (Ollama Guide)
P9 – Build Local AI Telegram Bot Fast (Ollama Guide) https://youtu.be/YuiLJDLIVr0 🚀 AI Tutorial – P9: Create a Local AI Telegram Bot with Ollama in Minutes Building a Local AI Telegram Bot is one of the fastest ways to bring AI into real-world usage. By combining Ollama with a simple...
Read More

