An OpenAI-compatible local proxy that routes LLM requests across multiple free model providers with automatic failover, rate-limit avoidance, and configurable rotation.
Designed to work with OpenClaw as a custom provider, but compatible with any OpenAI-compatible client.
OpenClaw Gateway Any OpenAI Client
↓ ↓
HTTP POST /v1/chat/completions
↓
[Free Models Proxy] (localhost:4242)
↓ round-robin + 429 failover
├── Cloudflare Workers AI (fast, high daily limit)
├── Z.AI Direct API (1000 req/day)
└── Ofox.ai Proxy (free, rate-limited)
- Multi-provider rotation — distribute requests across providers in round-robin
- 429 auto-fallback — on rate-limit, immediately retry with next provider
- Consecutive 429 suppression — N consecutive 429s triggers an extended cooldown per provider
- Per-window call limits — max calls per time window; if exhausted, provider is marked for cooldown
- Streaming support — SSE streaming with model-alias passthrough (every chunk's
modelfield uses the alias) - Health dashboard —
/healthendpoint shows real-time status of all providers - No dependencies — built on Node.js 18+ native
fetchandhttpmodules - Model-alias mapping — proxy receives an alias name, forwards to the real model ID per provider
- 0% spend — only uses free-tier endpoints; no API key costs
- Node.js 18+ (for built-in
fetchandAbortController) - API keys / tokens from at least one of the supported free providers
# 1. Clone the repo
git clone https://github.com/lykeion-dev/free-models-proxy.git
cd free-models-proxy
# 2. Edit config.json with your provider credentials
# 3. Set environment variables
export CF_API_KEY="your-cloudflare-api-token"
export ZAI_API_KEY="your-zai-api-key"
export OFOX_API_KEY="your-ofox-api-key" # optional
# 4. Start
node server.js# 5. Test it
curl http://localhost:4242/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer change-me-to-a-random-secret" \
-d '{"model":"glm-4.7-flash","messages":[{"role":"user","content":"Hello"}],"max_tokens":50,"stream":false}'Edit config.json. Structure:
{
"port": 4242,
"apiKey": "set-a-secret-for-the-proxy-auth",
"models": {
"your-model-alias": {
"providers": [
{
"name": "cloudflare",
"baseUrl": "https://api.cloudflare.com/client/v4/accounts/YOUR_ACCOUNT_ID/ai/v1",
"apiKeyEnv": "CF_API_KEY",
"modelId": "@cf/zai-org/glm-4.7-flash",
"cooldownMs": 30000,
"maxConcurrent": 3,
"consecutive429Limit": 2,
"consecutive429CooldownMs": 300000,
"maxCallsPerWindow": 60,
"callWindowMs": 60000,
"callLimitCooldownMs": 60000
}
]
}
}
}| Field | Type | Description |
|---|---|---|
port |
number | Proxy listen port |
apiKey |
string | Bearer token required by clients to access the proxy |
name |
string | Human-readable label for the provider (logging) |
baseUrl |
string | Provider's OpenAI-compatible base URL (without /chat/completions) |
apiKeyEnv |
string | Environment variable name containing the API key for this provider |
modelId |
string | Actual model ID to send to this provider |
cooldownMs |
number | Standard cooldown duration after a single 429 (ms) |
maxConcurrent |
number | Max concurrent requests to this provider |
consecutive429Limit |
number | Consecutive 429s before extended cooldown (0 = disabled) |
consecutive429CooldownMs |
number | Extended cooldown duration when limit is hit (ms) |
maxCallsPerWindow |
number | Max calls in the sliding time window (0 = disabled) |
callWindowMs |
number | Sliding window duration (ms) |
callLimitCooldownMs |
number | Cooldown when call limit is hit (ms) |
You can mix different models from different providers under the same alias. Example:
"coding-mix": {
"providers": [
{
"name": "cloudflare-qwen",
"baseUrl": "https://api.cloudflare.com/client/v4/accounts/YOUR_ACCOUNT_ID/ai/v1",
"apiKeyEnv": "CF_API_KEY",
"modelId": "@cf/qwen/qwen3-30b-a3b-fp8",
"cooldownMs": 30000,
"maxConcurrent": 3
},
{
"name": "zai",
"baseUrl": "https://api.z.ai/api/paas/v4",
"apiKeyEnv": "ZAI_API_KEY",
"modelId": "glm-4.7-flash",
"cooldownMs": 30000,
"maxConcurrent": 2
}
]
}Each provider in the list is independent — different API endpoints, different rate limits, different model IDs. The proxy rotates between them and falls back on failure.
- Round-robin — requests are distributed evenly across providers
- 429 fallback — on rate limit, immediately retry with the next available provider
- Consecutive 429 suppression — if a provider returns
consecutive429Limit429s in a row, it enters extended cooldown (consecutive429CooldownMs) - Call window limits — if
maxCallsPerWindowcalls have been made withincallWindowMs, the provider is cooled forcallLimitCooldownMs - All providers exhausted — returns HTTP 503 with available aliases
Register as a custom provider in openclaw.json:
{
"models": {
"providers": {
"free-models": {
"baseUrl": "http://localhost:4242/v1",
"apiKey": "change-me-to-a-random-secret",
"api": "openai-completions",
"models": [
{
"id": "glm-4.7-flash",
"name": "GLM-4.7 Flash (Free, Auto-rotated)",
"reasoning": true,
"contextWindow": 131072,
"maxTokens": 8192
}
]
}
}
}
}Then reference models as freemodels/glm-4.7-flash.
| Method | Path | Auth | Description |
|---|---|---|---|
POST |
/v1/chat/completions |
Bearer | OpenAI-compatible chat completion (stream + non-stream) |
GET |
/v1/models |
Bearer | List available model aliases |
GET |
/health |
No | Provider health status with stats |
| Provider | Base URL | Daily Limit | Notes |
|---|---|---|---|
| Cloudflare Workers AI | https://api.cloudflare.com/client/v4/accounts/YOUR_ID/ai/v1 |
10,000 neurons/day | Fast, reliable. Get your account ID from the Cloudflare dashboard. |
| Z.AI (General API) | https://api.z.ai/api/paas/v4 |
1,000 req/day | 3 req/min limit. Sign up at z.ai for an API key. |
| Ofox.ai | https://api.ofox.ai/v1 |
Unknown | OpenRouter-compatible proxy. Has :free suffix models. |
MIT