Every AI lab hands out a free tier. Individually each one is a toy — a few thousand requests a day, a quota you hit by lunchtime. FreeLLMAPI stacks 34 of them behind a single OpenAI-compatible endpoint with routing and failover, so your client points at http://localhost:3001/v1 and stops caring which provider answered.

Google, Groq, Cerebras, Mistral, OpenRouter, Cloudflare, Cohere, Z.ai, NVIDIA, HuggingFace and more — plus a custom provider for any OpenAI-compatible endpoint, which is how you fold in Ollama, llama.cpp, or vLLM alongside them. MIT licensed, local-first, keys encrypted at rest in SQLite.

Docker

  git clone https://github.com/tashfeenahmed/freellmapi.git
cd freellmapi
printf "ENCRYPTION_KEY=%s\nPORT=3001\n" "$(openssl rand -hex 32)" > .env
docker compose up -d
docker compose logs -f freellmapi
  

There’s also a one-liner installer (curl -fsSL https://freellmapi.co/install.sh | bash) and desktop builds for macOS and Windows, where the dashboard signs itself in and there’s no password to set.

Compose binds the port to 127.0.0.1 on purpose. To reach it from another machine on a trusted LAN:

  HOST_BIND=0.0.0.0 docker compose up -d
  

Podman

The image runs the same way, with two differences that will bite you if you skip them.

  podman run -d \
  --name freellmapi \
  -p 127.0.0.1:3001:3001 \
  -e ENCRYPTION_KEY="$(openssl rand -hex 32)" \
  -v freellmapi_data:/app/data:Z \
  ghcr.io/tashfeenahmed/freellmapi:latest
  

ENCRYPTION_KEY is not optional. The image runs in production mode, and without a 64-character hex key the server exits immediately — the container is “running” while the port answers nothing:

  [server] Failed to start:
  ENCRYPTION_KEY is required in production for API key encryption.
  

Generate one with openssl rand -hex 32, or node -e "console.log(require('crypto').randomBytes(32).toString('hex'))". Keep it: the stored keys are AES-256-GCM, so changing it later doesn’t re-encrypt them — there’s a rotate-encryption-key script for that. If you’d rather not put the key in your shell history, export it first and pass the bare name:

  export ENCRYPTION_KEY="…"
podman run -d --name freellmapi -p 127.0.0.1:3001:3001 \
  -e ENCRYPTION_KEY -v freellmapi_data:/app/data:Z \
  ghcr.io/tashfeenahmed/freellmapi:latest
  

:Z on the volume relabels it for SELinux. On Fedora, RHEL, and derivatives, leaving it off gives you permission errors on the database that look like the app is broken.

Note the 127.0.0.1: prefix on the published port: plain -p 3001:3001 binds every interface, and this service holds your provider keys and answers requests without authentication on the API side. Bind it to localhost unless you specifically want it on the LAN.

When port 3001 doesn’t answer

In order, cheapest first:

  podman logs freellmapi      # nine times out of ten the answer is here
podman port freellmapi      # expect 3001/tcp -> 127.0.0.1:3001
podman ps -a                # is it actually still up, or did it exit?
  

Two specific messages worth recognising:

  • ENCRYPTION_KEY is required in production — see above.
  • “A setup code is required to create the first account from a remote device.” You’re opening the dashboard from a different machine than the one running the container. The code is printed in the server log, so podman logs freellmapi and paste it into the form. Opening http://localhost:3001 on the host itself skips this.

Forgotten a password later? The one-time reset code goes to the log too, and lasts 15 minutes — there’s no email to send it to.

Starting it at boot

The command in most guides still works but is deprecated in current Podman:

  podman generate systemd --name freellmapi --files --new
mv container-freellmapi.service ~/.config/systemd/user/
systemctl --user enable --now container-freellmapi.service
  

The supported way now is a Quadlet unit — ~/.config/containers/systemd/freellmapi.container:

  [Container]
ContainerName=freellmapi
Image=ghcr.io/tashfeenahmed/freellmapi:latest
PublishPort=127.0.0.1:3001:3001
Volume=freellmapi_data:/app/data:Z
EnvironmentFile=%h/.config/freellmapi.env

[Service]
Restart=always

[Install]
WantedBy=default.target
  
  systemctl --user daemon-reload
systemctl --user start freellmapi
loginctl enable-linger "$USER"     # so it survives you logging out
  

Put ENCRYPTION_KEY=… in ~/.config/freellmapi.env with chmod 600.

Pointing your tools at it

Add provider keys on the Keys page, then take the unified key from that page’s header. Anything that accepts an OpenAI base URL works — Claude Code, Codex CLI, Cline, Continue, Aider, opencode, Cursor — and there are generators for the config files:

  npx freellmapi setup-claude --url http://localhost:3001 --api-key <unified-key>
  

“So is it actually free?”

You don’t need every provider — one key is enough to start, and the obvious first choices are Google AI Studio and Groq, whose free tiers are generous and take two minutes to get. Nothing is billed, because you’re using each lab’s own free allowance. The custom provider also lets you point at a local model that needs no key at all.

What it costs you instead is honesty about the limits, and the project states them plainly:

  • No frontier models, variable latency, no SLA. The good models hit their daily caps, so the endpoint gets noticeably less capable late in the day and resets at UTC midnight.
  • It’s for personal experimentation, not production. Free tiers exist for prototyping. If you ship something real, put a paid API behind it.
  • Each provider’s terms still apply to you when the traffic is proxied. The repository documents a provider-by-provider review; read it before you route anything commercial through it.

Next

The other extreme — a model small enough to ship inside the app → Needle

Last updated 07 Sep 2026, 00:00 UTC. history