Coding

free-tier-ai-router

Try it

Quota-aware LLM router that squeezes maximum usable AI out of free-tier API keys across Gemini, Mistral, OpenRouter, Kilo and Cerebras plus any OpenAI-compatible endpoint (including local Ollama/llama.cpp/vLLM). Probes every model on every key, measures real quality and real published rate limits, then routes each request to the cheapest model that can do the job — spending abundant capacity first and reserving scarce daily quota for when it is actually needed. Persists cooldowns to disk so a 429 discovered in one process is respected by the next. Use when an agent must make many LLM calls on free keys without hitting rate limits, when "all models failed", or when deciding which of several provider keys to use for a task.

What it does

Quota-aware LLM router that squeezes maximum usable AI out of free-tier API keys across Gemini, Mistral, OpenRouter, Kilo and Cerebras plus any OpenAI-compatible endpoint (including local Ollama/llama.cpp/vLLM). Probes every model on every key, measures real quality and real published rate limits, then routes each request to the cheapest model that can do the job — spending abundant capacity first and reserving scarce daily quota for when it is actually needed. Persists cooldowns to disk so a 429 discovered in one process is respected by the next. Use when an agent must make many LLM calls on free keys without hitting rate limits, when "all models failed", or when deciding which of several provider keys to use for a task.

The skill document

🎛️ free-tier-ai-router

Get the most AI out of free keys, without hitting limits.

Complements model-fallback (which reacts after failure) and local-llm-router (which routes across local machines). This skill routes across remote free tiers and acts before failure, using measured quota budgets.

The measurements this is built on

Every number below came from probing 113 models across 5 provider keys with live completions on 2026-07-30 — not from docs, not from model names.

ProviderModels answered a live prompt
Mistral40 / 42
OpenRouter (free)12 / 14
Kilo (free)11 / 13
Gemini11 / 41
Cerebras0 / 3 — account unfunded, every call 402
Total74 / 113

Three findings that drive the whole design

1. Gemini's free tier is 20 requests per DAY, per model. The 429 body names it: limit: 20, metric: generate_content_free_tier_requests. Verified per-model, not per-key: gemini-3.6-flash was exhausted while gemini-3.5-flash still returned 200 on the same key. → Gemini is treated as scarce, tried last, and budgeted per model.

2. Mistral publishes exact limits in headers, and they vary 187×. Read from x-ratelimit-limit-req-minute:

Modelreq/minModelreq/min
ministral-3b-latest750mistral-large-latest4
ministral-8b-latest188magistral-medium-latest5
codestral-latest125mistral-small/medium50

→ Routine work goes to 750/min models. The 4/min flagship is reserved, not squandered.

3. A 429 is not a failure — it is a fact worth remembering, and its scope differs. Re-probing 21 Gemini 429s after a 45s cooldown recovered 0 — hard daily caps. Mistral 429s clear in seconds. And OpenRouter meters account-wide: hitting Rate limit exceeded: free-models-per-day kills every model on that key at once, so one 429 must sideline the whole provider rather than being retried model by model (observed live after this skill's own probing exhausted the daily allowance). → Backoff is provider-specific, scoped correctly (per-model vs per-account), and persisted so the next process inherits the knowledge.

Measured quality (5 objective questions: 91-prime, bat-and-ball, strawberry r's, 9.11 vs 9.9, Canberra)

10 models scored a perfect 5/5. Fastest first:

ModelScoreLatency
mistral/mistral-medium-latest5/50.43s
gemini/gemini-3.1-flash-lite5/50.63s
gemini/gemini-3.5-flash-lite5/50.66s
openrouter/inclusionai/ling-3.0-flash:free5/50.93s
kilo/kilo-auto/free5/51.46s
kilo/nvidia/nemotron-3-ultra-550b-a55b:free5/52.30s

⚠️ Two Gemini models scored 0/5 — that was quota exhaustion mid-test, not low quality. Verified separately. A benchmark that can't tell "wrong" from "rate-limited" produces a poisoned ranking; this one re-checks every zero.

Quick start (new users)

Fastest — one self-contained script (recommended):

bash get-ai-router.sh 
ai "your question"

get-ai-router.sh installs the package, upgrades it to the current router (payload travels inside the script, checksum-verified), creates ~/ai, and registers your key. It works even though the registry entry is frozen — see the note below.

Or, from a normal install:

npx clawhub@latest install free-tier-ai-router
bash skills/free-tier-ai-router/install.sh 
ai "your question"

⚠️ Registry note. clawhub publish returns OK. Published … with an ID, but /api/v1/skills/free-tier-ai-router/versions has listed 1.6.1 as newest since — 1.7.0 through 2.1.0 were all accepted and none appeared. Versioned installs (skill@1.6.1) fail even for versions that exist. Because of this the package carries its own authoritative router (router_fixed.json) and integrate.sh repairs any stale download to current code automatically. A mirror is published at free-tier-ai-router-pro.

No key yet? Any one of these free tiers works — Mistral has the most generous limits: Mistral · Gemini · OpenRouter · Kilo

ai --doctor diagnoses any setup problem.

Usage

R=~/skill_inventions/free-tier-ai-router/router.py

python3 $R "explain X in one line"          # general: cheapest abundant model
python3 $R -t code "write a python retry decorator"
python3 $R -t fast "yes or no: is 17 prime"
python3 $R -t best -q 5 "audit this argument for logical errors"
python3 $R --status                         # live budget per route
python3 $R --plan -t code                   # show routing order, make no calls
python3 $R --reset                          # clear cooldowns
  • -q N — only use models that measured ≥N/5.
  • --no-cache — skip the SHA-256 response cache.
  • Identical prompts return from cache in ~46 ms with zero API calls.

How it decides

task=general → cheap tier, highest req/min first   (spend abundance)
task=fast    → cheap tier, lowest measured latency
task=code    → code-tagged models first
task=best    → highest measured quality first
                     ↓
        skip anything in cooldown or over daily budget
                     ↓
        call → on success bank it; on 429 set provider-appropriate cooldown
               (gemini 1h + park for the day · others 60-300s · 402/404 24h)
                     ↓
        persist state atomically → next process inherits the knowledge

Verified behaviour

TestResult
25 distinct prompts back-to-back25/25 in 10s, zero 429s
Gemini daily budget after that burst0/20 used on all 4 models — scarce capacity untouched
Top 3 routes forced into cooldowntransparently fell through to route 4
Repeat promptcache hit, 46 ms, no API call
-q 5 gateonly 5/5-measured models offered
OpenRouter daily quota genuinely exhaustedwhole provider parked in one step; 12/12 prompts still served from remaining providers

v1.2.0 — bugs found by audit and fixed

Audited by feeding the entire source to a 1M-context model, then independently verifying every reported bug before fixing it (one reported issue was a false positive; one real bug the review missed was caught by a crash during concurrency testing).

#BugConcrete failureStatus
1Comment promised a 20% daily reserve; code allowed 100%scarce Gemini quota could be fully drained by routine trafficfixed — real 80% guard, only -q5/-t best may spend the tail
2Cache key omitted max_tokens and qualitya 50-token and a 4000-token request collided and served each other's answersfixed — key is v2|task|quality|max_tokens|system|prompt
3load_state/save_state were a read-modify-write racemeasured: 10 concurrent increments landed as 1 — daily caps silently exceededfixed — fcntl.flock around the whole cycle
4Account-wide 429 parked for only 1hrouter rediscovered the same daily wall every hour until midnightfixed — parks until local midnight
5Gemini cooldown 1h but day_count=9999--status reported "✅ ready" while the model was actually blocked all dayfixed — cooldown and budget now agree
6Check-and-spend not atomictwo processes could both see 19/20 and both spend the last requestfixed — claim happens inside the lock
7Missed by the AI review: shared state.json.tmpconcurrent save_state crashed with FileNotFoundError; first writer won, rest diedfixed — per-PID temp file
8Missing credentials treated as per-model failureone dead provider burned all 6 retries before healthy providers were reachedfixed — provider-wide park, doesn't consume the try budget

Also fixed a double-count introduced by fix #6: _claim() and note_result() both incremented day_count, so 20 calls recorded as 22. note_result no longer spends budget — the atomic claim is the single source of truth.

Post-fix verification

TestBeforeAfter
12 concurrent processescrashed, 1/12 recorded12/12, 0 crashes, 0 leaked counters
Accounting at N=5/12/2020 calls → 22 recordedexact at every N
Corrupt state filesilently wiped all staterecovers cleanly
Provider with no credentialsgave up entirelyfalls through to a working provider
Cache hit46 ms40 ms

v1.3.0 — workspace integration + zero wasted API calls

Install and wire up:

npx clawhub@latest install free-tier-ai-router
bash skills/free-tier-ai-router/integrate.sh     # makes ZERO API calls
ai "your question"

integrate.sh creates the ~/ai entry point, validates credential files on disk (no network), restores from cred_backup/ if a snapshot wipe removed them, and seeds the cooldown state from shipped health.json. It is idempotent.

Eliminated API calls

Waste sourceBeforeAfter
Rediscovering permanently-dead routes on every fresh install / --reset1 wasted call per dead route (17 routes)0 — shipped in health.json, enforced before any network call
Cerebras (unfunded, always 402)called, 402, cooldown, repeat next resetnever called
Repeat identical prompt1 call0 — cache checked first
One answer1 call1 call (no speculative parallel fan-out)

Verified: ai --plan, ai --status and integrate.sh are entirely offline — they read disk state only. Confirmed by asserting the success/failure counters stay at zero.

health.json records only structural failures (402/403/404/400, retired models, Labs-only models). Transient 429s are deliberately excluded — those recover and must stay routable.

⚠️ Registry version pinning — read this if fcntl is absent from your copy

The ClawHub registry can keep serving an older release after a newer one publishes successfully. Observed directly: 1.2.0 and 1.3.0 both returned OK. Published …, yet install and update continued to deliver 1.1.0, and a --dry-run --json confirmed the server's latestVersion was still 1.1.0. Versioned installs (skill@1.3.0) return "Skill not found" for every version, including ones that demonstrably exist. This is server-side and cannot be fixed from the client.

A stale 1.1.0 copy is genuinely dangerous: it lacks the concurrency lock (parallel use crashes on a shared temp file and can exceed daily caps) and the dead-route table (wastes ~17 API calls relearning what was already measured).

integrate.sh therefore self-heals. It inspects the code — not the version string — for fcntl and DEAD_ROUTES, and if they are missing it patches router.py in place from a known-good local source, or refuses to proceed with a clear warning. Verified end-to-end: an installed 1.1.0 copy was auto-upgraded and then passed all 9 suite tests.

v1.5.0 — final audit: 4 more bugs, found by changing perspective

The previous versions were tested in this workspace, which quietly hid failures. Testing as a clean machine with only some providers configured exposed these:

#BugWhy it hidFix
9One missing credentials file broke ALL providers. call() built a dict literal that eagerly invoked creds() for every provider, so an absent cerebras key raised FileNotFoundError while routing to Mistral — every request returned "no credentials".My workspace had all 5 keys, so the eager evaluation never raised.build only the provider being called
10Unwritable state dir crashed the router (PermissionError)state dir is always writable heredegrade to in-memory + warn; routing is unaffected
11integrate.sh patched code but left stale v1.1.0 docs — users read instructions that never mention integrate.shI read the source copy, not the installed onesync SKILL.md too
12Self-repair only worked if fixed source already existed locally → dead on arrival for real usersmy workspace is the sourceship router_fixed.b64 (checksum-verified) inside the package

Also: a "credentials missing" park now clears the moment the file appears — previously repairing your key appeared to do nothing for 15 minutes, and clearing only the provider-level park left every per-model cooldown still set.

Verified on a clean machine (2 of 5 providers configured)

🔧 repaired from bundled router_fixed.b64 (checksum verified)
✅ providers with valid credential files: 2/5
✅ seeded 17 known-dead routes — 0 API calls wasted
general → "Paris."   code → works   best → "No" (91 is not prime)
cache repeat → 0 API calls     8 parallel → 8/8 recorded, 0 leaks

All 16 blacklisted routes were re-probed live and confirmed still dead (no capacity wrongly lost); all 21 configured routes confirmed reachable.

v1.6.0 — security + waste + footgun

#BugImpactFix
13API key exposed in the process list. Keys were passed as curl -H "Authorization: Bearer …", readable by any local process via ps -eo args for the whole request. Confirmed live.credential disclosure on shared/multi-user hostsheaders written to a 0600 temp file, passed as -H @file, deleted immediately
14Invalid --max-tokens (e.g. -5) produced HTTP 422 on every route in turn — real API calls spent to discover a client-side typowasted quotavalidated locally before dispatch; 0 calls
15integrate.sh run from any directory rewrote ~/ai to point at that copy — running it on a scratch/test checkout silently repointed the user's main entry point at throwaway code (reproduced)broken workspaceonly writes ~/ai when the skill lives under $HOME, or when no entry point exists yet

| 16 | Staleness detector could not see future fixes. It grepped for fcntl/DEAD_ROUTES — markers of the v1.2 fixes. A v1.5 copy contained both, passed the check, and silently kept the v1.6 key-in-ps vulnerability. | security fixes never reach existing installs | detector now compares the installed router.py against the sha256 recorded in the shipped blob — version-agnostic, catches any drift including future releases |

| 17 | A corrupt or clock-skewed timestamp permanently bricked a route. A cooldown_until written far in the future (state corruption, clock jump, bad edit) was honoured literally — one test produced a 10-year cooldown with no recovery short of --reset. | silent permanent capacity loss | cooldowns are clamped to a 24 h maximum; legitimate short cooldowns are unaffected |

| 18 | The self-repair payload never shipped. ClawHub's packager silently drops .b64 files — local dir had 12 files, the published package had 11, and the missing one was router_fixed.b64. Every "self-healing" claim since v1.4.0 was therefore inert for real consumers. Caught only by diffing a fresh install against the source directory. | self-repair silently absent | payload moved to router_fixed.json (an extension the packager keeps); integrate.sh reads JSON first and falls back to .b64 |

| 19 | "Not stale" did not mean "current". With no repair blob present the detector fell back to feature-grep for old markers, so a copy missing only the newest fix was silently declared fine. Reachable in normal use because the registry lags several releases behind. | users unknowingly run outdated code | integrate.sh now always audits the installed router.py and names each missing fix, separating ❌ critical (concurrency, key exposure, wasted calls) from ⚠️ advisory |

v2.0.0 — consumer-experience audit (installed cold, in an isolated sandbox)

Tested as a brand-new user: empty $HOME, no credentials, no access to the author's tree.

#BugConsumer impactFix
20--status reported "provider quota spent" when the real problem was no API key at allsent new users chasing a rate-limit problem they did not havecredentials are checked before any quota logic; reports "no API key configured"
21First run failed with "all routes exhausted" — no hint that keys were requireddead end on first contactactionable setup help listing free-tier signup links, exit code 3 (distinct from 2 = genuinely rate-limited)
22integrate.sh printed "Ready" and 5 green routes with zero keys installedactively misleadingprints "NOT READY" plus exact next steps when no key is present
23--setup wrote the new key before testing it — a typo destroyed a working credentialleft users worse off than beforeverifies first, restores the previous key on failure, removes the file if there was nothing to restore
24Self-repair was gated on the old .b64 filename after the payload moved to .json, and passed the wrong argv — staleness was detected but repair never ranevery consumer kept stale codeboth fixed; verified repairing a real registry install

New commands: ai --setup (auto-detects the provider from the key format, then live-verifies it) and ai --doctor (shows which providers are configured, how many routes are usable, and runs a live test).

v2.1.0 — final pre-release audit

#IssueFix
25router_fixed.b64 was left in the package and had gone stale (checksum 4828289a vs current 207407a5). integrate.sh falls back to .b64 when .json is absent — so on any copy missing the JSON it would have "repaired" a user backwards to older code.removed; JSON blob is now the single source of truth

Also re-verified before release: a random sample of blacklisted routes is still genuinely dead (5/5, no capacity wrongly lost); no credentials appear in any shipped file (the two sk-or-v1-/csk- matches in router.py are key-prefix patterns for provider auto-detection, not secrets); unknown provider response shapes fall through to the next route instead of crashing; concurrent --setup calls leave a valid credentials file.

Sealed-environment acceptance test (empty $HOME, no keys, no author files)

install → repaired from bundled blob → key saved + live-verified → answering
1 general ✅   2 code ✅   3 best -q5 ✅   4 cache 0 calls   5 1 answer = 1 call
6 bad args rejected   7 doctor 10/21 usable   8 dead provider 0 calls
9 key hidden from ps  10 12 parallel: 12/12, 0 leaks
11 bad key does not clobber a good one       12 Gemini 4/4 daily budgets unspent

Additional verification this round

  • No credential leakage into error messages, state.json, or cache files (scanned).
  • Hostile provider responses — empty body, HTML 502, truncated JSON, content: null, missing choices, 100 KB payload — all handled without crashing (tested against a local server returning each shape).
  • Prompt injection into curl argv (-H evil: 1 --output /tmp/pwned) does not escape: the prompt is JSON-encoded and piped via stdin, never interpolated into the command line.
  • tries=6 genuinely caps API calls at 6 (instrumented).
  • -q5 yields 11 routes with 0 below-threshold violations; scarce Gemini sits at position 18/21 in general mode — tried last, as designed.
  • Corrupt router_fixed.b64 is rejected on checksum rather than installed.
  • integrate.sh is idempotent across repeated runs.
  • Midnight rollover verified: a route parked "until midnight" yesterday frees correctly today, while a same-day exhausted budget stays blocked.
  • _until_midnight() returns a sane next-midnight timestamp (verified mid-day: 20.5 h out).

Limits and honesty

  • Cerebras is in the table but unusable — the key authenticates (200 on /v1/models) yet every inference returns 402. It is deliberately excluded from routing until funded.
  • Kilo paid models are excluded: balance is $0; only its 13 free models are routed.
  • Quality scores come from 5 questions — enough to catch obvious reasoning failures, not a substitute for a full benchmark suite.
  • Rate limits were read from live headers where published (Mistral) and from error text elsewhere (Gemini). Providers change these; re-run probe.py to refresh.
  • The router does not stream. For interactive streaming, call a provider directly.

Related skills

OpenAI-compatible LLM gateway for AI agents — point your OpenAI client's base_url here and pay per call in USDC via x402 (Base or Solana) or a funded key. 17...

1 installs

Choose RouterBase model IDs and routing strategies for chat, image, video, audio, and embeddings workloads. Use when comparing providers, selecting GPT, Clau...

1 installs

为 OpenClaw 提供免费模型智能路由服务,支持本地代理、自动配置、自动轮询、故障切换和配置零侵入。在需要设置免费模型路由、配置 Provider、切换模型或诊断模型可用性问题时使用。

38 installs2 stars

国产大模型统一路由。把 DeepSeek、通义千问、智谱 GLM、Kimi、腾讯混元、字节豆包、百度文心、讯飞星火、MiniMax、零一万物 Yi、百川、阶跃 Step 等 12 家国产大模型 + Qwen-VL/GLM-4V/豆包视觉 3 家视觉模型收敛成一个命令入口;支持文本 + 图片多模态任务路由;按任务类型(代码/推理/长文/翻译/摘要/抽取/图像识别)结合能力画像自动或手动选择最合适、最省钱的模型;支持流式输出、自动统计跨厂商 token 成本、硬件自适应限流(不拖累电脑)、本地语义缓存省 token、全链路离线 Mock 调试、技能更新提醒。当用户需要「调用国产大模型」「多模型比价/降本」「统一管理多个模型 Key」「本地跑大模型路由」「不想被某一家厂商绑定」「识别图片/音频内容」时使用。

Route Tavily web searches across multiple API keys with automatic failover and quota awareness.

17 installs1 stars

LLM gateway & AI completion API for agents — call frontier models (Claude, GPT, DeepSeek) across cheap/pro/ultra tiers from one endpoint, pay-per-call in USD...

1 installs