freelm — free LLM API for Python & Node.js (OpenAI-compatible, auto-failover)
freelm turns the free tiers of Google Gemini, Groq, OpenRouter, Cloudflare Workers AI, Z.ai, Cohere, Mistral,
NVIDIA NIM and Kilo into one OpenAI-compatible LLM — in your Python or TypeScript code, or as a local /v1 endpoint for any tool.
A free-only, lightweight alternative to LiteLLM or an OpenRouter account: it rotates your keys, falls back across
providers on rate limits, outages and retired models, races a slow provider against the next one (a hung provider
costs ~6 s, not a 60 s failure — measured), and
discovers which models are free today. A library, not a server to babysit: your own free keys go straight to each
provider — no relay, dashboard or admin port, one runtime dependency in Python and none in Node — and the CLI even
works with no keys at all.
pip install freelm # Python >= 3.9 · npm install freelm (Node >= 20, zero dependencies)
import freelm
llm = freelm.FreeLLM.from_env() # reads GEMINI_API_KEY, GROQ_API_KEY, OPENROUTER_API_KEY, ...
print(llm.text("Explain failover in one sentence."))
Start in 60 seconds
Zero keys? pip install freelm && freelm chat "hello" already works: with no keys configured, the CLI falls
back to keyless public endpoints (Kilo Gateway free routes, OVHcloud AI Endpoints
anonymous tier) and says so. Their limits are low and free routes may log prompts, so for real use:
-
Get one free key (no credit card): Google AI Studio is the most generous; add Groq and OpenRouter for failover.
-
export GEMINI_API_KEY=...(and any others — see providers). -
Check what works:
freelm doctorsends one tiny request per key and tells you exactly what's wrong and how to fix it:$ freelm doctor openrouter sk-or-...a1b2 FAIL key rejected (401): User not found. — get a new free key: https://openrouter.ai/keys google AIzaSy...x9yz OK gemini-3.5-flash-lite · 1131 ms groq gsk_ab...cd12 FAIL key rejected (401): Invalid API Key — get a new free key: https://console.groq.com/keys 1 of 3 key(s) working — ready: google
-
Call it from code (above), the terminal (
freelm chat "hi"), or any OpenAI-compatible tool (freelm serve, below).
Use free LLMs in any OpenAI-compatible tool
freelm serve runs the same router as a local OpenAI-compatible API — streaming, tool calls and failover included:
freelm serve # → http://127.0.0.1:4000/v1 (npx freelm serve works too)
Then point your tool at http://127.0.0.1:4000/v1 with any API key and model auto (or chat:fast, large,
tools, or a concrete id):
| Tool | Setting |
|---|---|
| OpenAI SDK (Python / Node) | OpenAI(base_url="http://127.0.0.1:4000/v1", api_key="freelm") |
| LangChain | ChatOpenAI(base_url="http://127.0.0.1:4000/v1", api_key="freelm", model="auto") |
| LlamaIndex | OpenAILike(api_base="http://127.0.0.1:4000/v1", api_key="freelm", model="auto", is_chat_model=True) |
| Vercel AI SDK | createOpenAICompatible({ name: "freelm", baseURL: "http://127.0.0.1:4000/v1" })("auto") |
| Continue (VS Code / JetBrains) | model with provider: openai, apiBase: http://127.0.0.1:4000/v1, model: auto |
| Cline / Roo Code | API provider "OpenAI Compatible", base URL http://127.0.0.1:4000/v1, model auto |
| OpenCode | opencode.json → "provider": {"freelm": {"npm": "@ai-sdk/openai-compatible", "options": {"baseURL": "http://127.0.0.1:4000/v1"}, "models": {"auto": {}}}} (docs) |
| OpenClaw | models.providers.freelm: baseUrl: "http://127.0.0.1:4000/v1", api: "openai-completions", models: [{ id: "auto" }], then allow freelm/auto in agents.defaults.models (docs) |
| Hermes Agent | hermes model → Custom endpoint → base URL http://127.0.0.1:4000/v1, model auto (needs a long-context model: pin one with large or a concrete id) |
| Aider | aider --openai-api-base http://127.0.0.1:4000/v1 --openai-api-key freelm --model openai/auto |
| Open WebUI | Admin → Connections → OpenAI API: http://host.docker.internal:4000/v1 |
| n8n | OpenAI credential → Base URL http://127.0.0.1:4000/v1 |
Or with Docker (image built on each release):
docker run --rm -p 4000:4000 -e GEMINI_API_KEY=... -e FREELM_SERVER_KEY=change-me ghcr.io/shihabshahrier/freelm
GET /v1/models lists the aliases plus every discovered free model; GET /health shows key state. A model id no
free provider offers (a tool's default gpt-4o) is tried, then served by auto (--no-fallback to disable). Bound
to localhost by default — if you expose it (--host 0.0.0.0, Docker on Linux), set --api-key so nobody else spends
your quota. In code: freelm.serve() (Python) / serve() (JS).
Python
from freelm import FreeLLM, GoogleAIStudio, Groq, OpenRouter
llm = FreeLLM(
[GoogleAIStudio("AIza..."), Groq("gsk_..."), OpenRouter("sk-or-...")], # or FreeLLM.from_env()
strategy="smart", # smart (default) | priority | round_robin | quota_aware | latency
)
r = llm.chat([{"role": "user", "content": "Write a haiku about failover."}], model="chat:fast")
print(r.text, "via", r.provider, r.model)
for chunk in llm.stream("Count to five."): # token streaming, failover before the first token
print(chunk, end="", flush=True)
r = llm.chat(msgs, model="chat:tools", tools=[...], tool_choice="auto") # function calling
r.tool_calls
llm.chat(msgs, response_format={"type": "json_object"}) # JSON mode
Async is symmetric (AsyncFreeLLM, await llm.chat(...), async for c in llm.astream(...)). For raw OpenAI
chat.completion.chunk objects — tool-call deltas, finish reasons, usage — use stream_chunks() /
astream_chunks().
Drop-in for the OpenAI SDK — change one import:
# from openai import OpenAI
from freelm.compat import OpenAI
client = OpenAI() # api_key / base_url / ... are accepted and ignored; keys come from the environment
r = client.chat.completions.create(model="auto", messages=[{"role": "user", "content": "hi"}])
print(r.choices[0].message.content)
Responses behave like the SDK's (r.choices[0].message.tool_calls[0].function.arguments, model_dump(),
stream=True chunks, with blocks, client.models.list()).
JavaScript / TypeScript
import { FreeLLM } from "freelm";
const llm = FreeLLM.fromEnv();
console.log(await llm.text("Explain failover in one sentence."));
for await (const chunk of llm.stream("Count to five.", { signal: AbortSignal.timeout(30_000) })) process.stdout.write(chunk);
Same engine, same providers, zero dependencies; ESM + CommonJS. No node: imports in the library, so it also
bundles for edge functions, Workers and browsers (disk cache/persistence switch on only where Node built-ins exist).
See the
JS README.
Free providers
| Provider | Get a free key | Environment variable | Notes |
|---|---|---|---|
| Google AI Studio (Gemini) | aistudio.google.com/apikey | GEMINI_API_KEY |
Most generous free tier; per-model limits. Pro models have no free quota. |
| Groq | console.groq.com/keys | GROQ_API_KEY |
Very fast; per model 30 req/min, 1K req/day. Not xAI's "Grok" (that one is paid). |
| OpenRouter | openrouter.ai/keys | OPENROUTER_API_KEY |
:free models + the openrouter/free router only (guarded); 20 req/min, 50 req/day (1000/day after $10 of lifetime credit). |
| Cloudflare Workers AI (beta) | dash.cloudflare.com | CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID |
10,000 Neurons/day free on every account (a few hundred chats); Llama 4 Scout, gpt-oss, Qwen 3.8, Gemma 4, GLM-4.7-Flash. Token needs Workers AI permission. |
| Z.ai (GLM) (beta) | z.ai | ZAI_API_KEY |
GLM-4.7-Flash, GLM-4.5-Flash and GLM-4.6V-Flash (vision) are free; other GLM models are paid, so they're blocked (guarded). |
| Cohere (beta) | dashboard.cohere.com | COHERE_API_KEY |
Command A / A+ on a free trial key: 20 req/min per model, 1,000 calls/month, non-commercial use. |
| Kilo Gateway | app.kilo.ai (optional) | KILO_API_KEY |
Free routes (:free, kilo-auto/free) work without a key (~200 req/hour per IP); a free account lifts that. Free-only guard on. |
| OVHcloud AI Endpoints | none — anonymous | — | Keyless, 2 req/min per IP per model; last-resort fallback. (An OVH key is pay-as-you-go, so freelm doesn't use one.) |
| Cerebras | cloud.cerebras.ai | CEREBRAS_API_KEY |
⚠️ No longer permanently free (trial credits, card required) — supported if you already have a key. |
| Mistral | console.mistral.ai | MISTRAL_API_KEY |
"Experiment" plan; low requests/minute. |
| NVIDIA NIM | build.nvidia.com | NVIDIA_API_KEY |
Free against build credits. |
Beta = added in 0.5.0 and not yet verified with live keys — freelm doctor tells you if yours works.
Several keys per provider: comma-separate them or use FREELM_<PROVIDER>_KEYS. The library never uses the
keyless endpoints unless asked — FreeLLM.from_env(keyless=True) (always, as last resorts), keyless="auto"
(only when no keys are set — the CLI's default) or FREELM_KEYLESS=1|auto|0. Any other OpenAI-compatible endpoint
works too: Provider("key", name="myhost", base_url="https://.../v1", models=[ModelSpec("model-id", ("chat",))]).
Free tiers change constantly — models get retired, limits move, keys expire. freelm is built for that: it discovers
models live (OpenRouter, Groq, Mistral, Kilo, OVHcloud, Cerebras), benches retired or overloaded models instead of retrying them,
and freelm doctor tells you what works right now. The built-in fallback model lists were re-verified on 2026-10-09.
How it stays up
- A slow or hung provider doesn't hold the call. When an attempt is still running after a few seconds (3× that provider's usual latency, clamped; ~3 s for a stream's first token, ~6 s for a whole answer when it's unknown), the next provider starts in parallel and the first answer wins — the slow one is cancelled and remembered.
- Smart routing (default): providers are ranked by measured latency, so a fast one serves first and a slow one drops back; an unknown provider counts as typical so it gets tried, and old measurements expire after 10 minutes.
- Every failure fails over. 429 → rotate the key (or just bench the model, where quotas are per-model: Gemini, Groq, Z.ai, Cohere; a key whose daily or monthly quota is spent rests instead of being retried every minute); 5xx / timeouts → circuit breaker + backoff; 401/402 → disable that key; a retired model (404/410) → bench it for an hour; one provider rejecting a request (unsupported parameter, moderation) → try the next provider. Only a request that several providers reject is reported as your bug.
- Breadth-first failover: the best model of every provider is tried before any provider's second model, so a provider full of throttled or dead models can't stall the call.
- Quota guard: per-key requests/minute bucket + daily counter; keys predicted to be exhausted are skipped.
- Errors that explain themselves: when everything fails,
NoProvidersAvailablesays why per provider (openrouter: disabled (auth:401 — key invalid/expired?) | google: cooling down ~30s ...).
Choosing models
| Alias | Meaning |
|---|---|
auto / chat |
best available chat model (non-thinking models first) |
chat:fast · chat:large · chat:small |
by size/speed |
chat:tools · vision |
only models that support function calling / images |
reasoning |
prefer thinking models |
vendor/model-id |
exactly this model, routed only to providers that list it |
["vendor/id", "chat:fast"] |
an ordered per-call fallback chain |
Bias selection with prefer=["gemini-2.5-flash", "gpt-oss"] (exact id or substring), order a static list with
ModelSpec(priority=), or rank providers with priority= (lower = first).
Thinking models (gemini-2.5-flash, gpt-oss, ...) can spend a small
max_tokensbudget on hidden reasoning and return empty text — give them ≥ 128 tokens or useauto, which prefers non-thinking models.
Configuration
FreeLLM(...) option |
Default | |
|---|---|---|
strategy |
"smart" |
smart (priority tiers, then fastest measured) · priority · round_robin · quota_aware · latency |
hedge |
True |
race a slow attempt against the next provider: True = adaptive delay, seconds = fixed, False = one at a time |
max_attempts |
12 |
cap on tries across providers/keys/models per call |
timeout |
60 |
seconds; also the overall deadline for one call |
wait / max_wait |
False / 20 |
sleep until a key frees up instead of failing |
on_event |
— | callback for attempt / hedge / success / error / wait / discovery events (keys masked) |
persist |
False (FREELM_PERSIST=1) |
keep quota/cooldown/disabled-key state across restarts (~/.cache/freelm/state.json, 0600, key hashes only) |
Provider options: tier, priority, prefer, models, rpm/rpd, discover, free_only (OpenRouter defaults
to True: paid ids raise ConfigError instead of billing you), cache_ttl. llm.health() shows per-key state;
llm.reset_keys() gives every key a fresh start.
CLI
freelm chat "explain failover in one line" --model chat:fast --stream
freelm doctor [--json] # live-check every key, with fixes
freelm serve [--port 4000] [--api-key KEY] [--cors]
freelm models --provider groq # live model list
freelm health # this process's key state
When can freelm cost money?
Free-only by default and by guard: OpenRouter, Kilo and Z.ai paid models are blocked unless you pass
free_only=False; Google is free unless you pick tier="tier1" (billing enabled); NIM consumes free build credits
and Cerebras trial credits (requests fail when they run out — nothing is billed through freelm); Groq and Mistral
free accounts only have free models; OVHcloud is used anonymously only. Your account's plan decides the rest: Cohere
bills only production (non-trial) keys, and Cloudflare only on the Workers Paid plan (past the daily free Neurons —
the Free plan just stops).
How freelm compares
Most free-LLM tools of 2026 are servers you run — many with a dashboard, most aimed at coding agents. freelm is the library for your own code, with the server optional:
| Project | What it is | How freelm differs |
|---|---|---|
| OmniRoute, 9Router, FreeLLMAPI, free-claude-code | Self-hosted gateways (dashboards, desktop apps) that pool free providers, mostly for coding agents | pip/npm install and call it from your code; nothing to host, no admin port or database to secure. freelm serve gives a local /v1 endpoint when you want one |
| freellmpool | Python library + proxy over free providers | freelm also ships a zero-dependency TypeScript package with the same behaviour (Node, Workers, browsers) |
| LiteLLM | SDK + proxy for 100+ providers, paid and free | Better for paid production traffic. freelm is free-only, one dependency, with per-key quota state, free-model discovery and failover tuned to how free tiers break |
| any-llm, aisuite, llm | Unified interfaces across providers | They unify the API; freelm also pools keys, tracks free quotas and fails over when a free tier gives out |
| OpenRouter | One hosted aggregator | One of freelm's pools — when its free models are throttled, freelm moves to Gemini, Groq, Cloudflare, Z.ai, Mistral or NIM directly |
| gpt4free | Reverse-engineered chat endpoints | freelm only uses providers' official free tiers with your own keys |
| LangChain / LlamaIndex | Orchestration frameworks | Use freelm under them via freelm serve or the OpenAI-compatible shim |
FAQ
What is the best free LLM API in 2026?
Google AI Studio (Gemini) has the most generous free tier; Groq is the fastest; OpenRouter has the widest choice of
:free models. Each has its own limits and outages, so the most reliable option is to use several at once —
freelm pools them behind one OpenAI-compatible call and fails over automatically.
Is there a free alternative to the OpenAI API?
Yes. freelm.compat.OpenAI is a drop-in for the OpenAI SDK, and freelm serve exposes a local
/v1/chat/completions endpoint, both backed by free tiers (Gemini, Groq, OpenRouter, Cloudflare Workers AI, Z.ai,
Cohere, Mistral, NVIDIA NIM)
with streaming, tool calling and automatic failover.
How do I use free LLMs in OpenCode, OpenClaw, Hermes, Cline, Continue, Open WebUI or n8n?
Run freelm serve and set the tool's OpenAI base URL to http://127.0.0.1:4000/v1 with model auto — see the
table above. Tools that call the endpoint from their own servers need
a public URL (for example a tunnel) plus --api-key.
How do I use free LLMs in Python or JavaScript?
pip install freelm or npm install freelm, set one free key (e.g. GEMINI_API_KEY), then
freelm.FreeLLM.from_env().text("...") / await FreeLLM.fromEnv().text("...").
Can I use an LLM API with no API key at all?
Yes, for trying things out: freelm chat "hello" (or npx freelm chat "hello") works with no keys by using
documented keyless endpoints — Kilo Gateway's free routes (~200 requests/hour per IP) and OVHcloud AI Endpoints'
anonymous tier. Limits are low and free routes may log prompts; one free Gemini or Groq key is far more capable.
Why did my free model stop working?
Free model ids are retired constantly (NVIDIA retired its Llama 3.x endpoints on 2026-08-26; Groq retired Llama 3.3 70B
on 2026-08-16). freelm discovers current models live, benches retired ones on the first 404/410 and fails over, so
your code keeps working; freelm doctor shows which keys and models work today.
How fast is failover?
An error (429, 5xx, bad key, retired model) fails over immediately — the cost is one extra round trip, usually well
under a second. A provider that hangs or answers slowly is raced: after a few seconds the next provider starts in
parallel and the first answer wins (measured: a hung provider ahead of a healthy one answers in ~6 s, a stalled
stream in ~3 s). After that, smart routing sends calls to the fast provider first, so later calls don't wait at all.
The numbers come from a reproducible simulation: benchmarks/.
A host that won't accept a connection fails within 10 s.
Is there a lightweight LiteLLM alternative?
For free tiers, yes: freelm is one dependency in Python (httpx) and zero in Node, with no proxy to run. LiteLLM is
the better tool for paid production traffic across 100+ providers, budgets and teams; freelm is narrower — free-only,
with per-key quota and model state, free-model discovery and failover tuned to how free tiers break.
Is there a free OpenRouter alternative?
Use the free tiers at the source: Google AI Studio, Groq, Cloudflare Workers AI, Mistral, NVIDIA NIM, Z.ai and Cohere all serve real models at no cost. freelm calls them directly with your own keys and keeps OpenRouter as one more pool, so when OpenRouter's free models are throttled the call moves on instead of failing.
How do I add LLM fallback or failover in Python or Node.js?
FreeLLM.from_env() (or FreeLLM.fromEnv()) already falls back across every provider you have a key for: errors move
on in one round trip, slow providers are raced, and model=["vendor/id", "chat:fast"] gives an explicit per-call
fallback chain. No try/except needed.
How do I rotate API keys across free LLM tiers?
Comma-separate several keys per provider (GROQ_API_KEY=key1,key2) or pass a list: freelm paces each key, rotates on
429, cools or disables keys individually and, with persist=True, remembers quota state across restarts.
What does this free-tier error mean?
Each common one has a guide with the exact error body, the causes, the fix on the provider itself and what freelm does about it: Gemini 429 RESOURCE_EXHAUSTED, Gemini "User location is not supported", Gemini "API key not valid", Gemini 503 overloaded, OpenRouter free-models-per-day, OpenRouter "No endpoints found that support tool use", Groq rate limits, Mistral 429, Z.ai 1302 and empty replies from thinking models — all in the error reference.
How do I avoid free-tier rate limits (429)?
Add more providers and keys: freelm paces each key, rotates on 429, benches per-model quotas (Gemini, Groq) and
spreads load with strategy="quota_aware".
Is freelm really free? Does it see my prompts?
freelm is MIT-licensed and runs in your process (or on your machine with freelm serve); requests go straight from
you to the providers whose keys you configure. It has no telemetry. Usage limits are those of each provider's free
tier.
Links
Changelog · Contributing · Security · Website & docs · PyPI · npm
MIT © Shahriar Labs
Metadata
Release files for freelm 0.5.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| freelm-0.5.2.tar.gz | 99.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| freelm-0.5.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 182.2 kB
Release files / freelm-0.5.2.tar.gz
| Download URL | freelm-0.5.2.tar.gz |
|---|---|
| Size | 99.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b0c6ad484fb14223d56c3378750f93e6ef2864fd2f8521ca6f18552412a6c1f0
|
|
BLAKE2b-256 checksum How to use checksums |
81376b2ce7d45129b901dd2f0c02c5fff33f1fe2f6244d5eff25518fc27030f9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / freelm-0.5.2-py3-none-any.whl
| Download URL | freelm-0.5.2-py3-none-any.whl |
|---|---|
| Size | 82.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
442f7231b61f7b8ad6daeada879a637afc2c0f6341023a88e68285d4e4ced785
|
|
BLAKE2b-256 checksum How to use checksums |
76cc6a9ff5528c9a2cb4992df64f57104eb37ea6d2e3479474296b61a3326084
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|