Skip to content

Repository files navigation

KeyPulse

Stop guessing which of your AI API keys still works.

KeyPulse pools every free-tier and paid AI key you own behind one local OpenAI-compatible endpoint, measures all of them continuously, and routes each request to whichever key is healthiest right now — failing over the instant one rate-limits.

A desktop app. Runs entirely on your machine. No account, no proxy service, no telemetry.

License: MIT Built with Tauri v2 Rust core Vue 3 No telemetry


Why KeyPulse exists

Collecting free AI inference keys is easy. Groq, Cerebras, SambaNova, Together, Mistral, Gemini, OpenRouter, and a new one every other week on Hacker News. Living with them is the hard part.

Free tiers are unstable by design. A key that answered in 300 ms this morning is rate-limited by lunch, silently degraded by dinner, and revoked on Friday. So you end up doing the same three things by hand, forever:

  • Switching keys. Comment out one base_url, paste a different key, restart the agent, hope. Repeat when that one dies.
  • Guessing. Which provider is actually fastest for you, on your connection, at this hour? Nobody's public benchmark answers that. Yours would, if you had one.
  • Finding out too late. A new provider launches with a generous free tier and you hear about it three weeks after the thread scrolled off the front page.

KeyPulse turns all three into background processes.

It probes every key on a schedule with one small chat completion, and from that single call records time-to-first-token, total latency, tokens per second, and rolling jitter. It scores each key against the others. Then it exposes http://127.0.0.1:8787/v1 — plain OpenAI API — and sends each request to the best candidate, moving to the next one on a 429, an auth failure, a timeout or a 5xx, with exponential backoff that honours Retry-After. Your agent, your editor plugin, your script: one base URL, changed once, and never again.

A second tab watches GitHub, Hacker News and any RSS feed you pin for new free and trial inference offers, so the next Groq shows up as a card with an Add to my providers button instead of a thread you missed.

Nothing leaves your machine. Keys are sealed with AES-256-GCM under a master key in your OS keychain. State is a SQLite file in your app data directory. There is no KeyPulse account, no hosted control plane, and no telemetry — the only network calls are to the providers you added and the discovery sources you enabled.


What it does

One endpoint, every key behind it

http://127.0.0.1:8787/v1 speaks the OpenAI API, so any SDK, CLI or editor plugin can point at it unchanged.

curl http://127.0.0.1:8787/v1/chat/completions \
  -H 'content-type: application/json' \
  -d '{"model":"keypulse-auto","messages":[{"role":"user","content":"hi"}]}'

keypulse-auto (or auto, or an empty model) means you choose — KeyPulse picks per the current strategy and each key's own default model. Name a model explicitly and routing narrows to the keys that list it. Non-OpenAI providers are translated in both directions, streaming included: a chat stream is re-encoded frame by frame, not buffered.

Endpoint Purpose
POST /v1/chat/completions OpenAI-compatible, streaming or not
POST /v1/embeddings OpenAI-compatible (OpenAI-shaped keys only)
GET /v1/models every model across your routable keys, plus keypulse-auto
GET /health how many keys are routable and which is next
GET / a one-glance status page for the gateway itself

Health data that is actually yours

A background scheduler probes every enabled key on your interval with one small chat completion. From that single call it records time-to-first-token, total latency, tokens/sec, and rolling jitter (standard deviation over the recent window) — deliberately not a large-payload download test, which measures your network rather than the provider.

Failures are classified as rate limit, auth, timeout, server, network, bad request or bad response. A 429 puts the key in cooldown so the gateway stops choosing it. Quota comes from the provider's rate-limit headers when it sends them and from figures you enter when it doesn't, with a time-to-exhaustion estimate based on your observed usage.

Nine routing strategies, with a live preview

Best composite score · lowest latency · fastest TTFT · highest throughput · lowest error rate · round robin · weighted round robin · strict priority · random.

The composite score weights success rate .4, latency .3, throughput .2 and jitter .1 by default — all four adjustable. Whatever you pick, Settings shows the exact candidate order the strategy produces right now, before a single request depends on it.

On-demand benchmarking

The Benchmark card is separate and manual. Run on every key, or the gauge button on one row, fires several requests at that key on demand — the same small prompt, repeated. Use it to answer "which of these is fastest for me?" without waiting for the probe schedule.

Provider Radar

Discovery runs on a schedule or on demand across:

  • GitHub — the documented search API, over rotating queries about free LLM API lists.
  • Hacker News — the Algolia search API.
  • Pinned sources — RSS/Atom feeds, GitHub markdown lists and plain pages you add. Five community feeds are seeded on first run and can be deleted.
  • Web search — optional, only if you store a Brave or Tavily key.

Findings pass a relevance gate (it has to look like an AI/LLM API and like a free or trial offer) and become cards: name, website, docs, base URL, whether it looks OpenAI-compatible, free-tier details, pricing notes, models, context length, capabilities, first-seen, last-verified, source link. Cards are de-duplicated against each other and against the keys you already have. Each one has a one-click Add to my providers → that opens the Tab 1 dialog pre-filled — you only paste the key.

On politeness: only official APIs, public feeds and documented endpoints. A pinned page is fetched only after its host's robots.txt says a generic agent may; if it says no, the source is reported as skipped rather than worked around. One source failing never fails a run. Findings are leads, not facts — verify a free tier on the provider's own pricing page before you rely on it.


Why it's different

There are excellent tools in this space. KeyPulse overlaps with them and is not trying to replace all of them.

LiteLLM is the more complete production gateway — more providers, more routing knobs, team/spend management, Kubernetes-native. If you are putting a gateway in front of a product, use LiteLLM. Helicone is an observability platform first: it is the better answer to "what did my app send last Tuesday." OpenRouter is the least work of all — one key, hundreds of models, someone else's problem.

KeyPulse is for the case none of them target: you, on your own laptop, with a drawer full of free-tier keys and no infrastructure. Three things follow from that.

1. Zero infrastructure. No Docker, no Postgres, no Redis, no YAML, no account. Install the app, paste a key, done. State is one SQLite file.

2. Your keys never leave the machine. They are sealed under your OS keychain and used only for calls you initiated. There is no vendor in the request path because there is no vendor.

3. It tells you about providers you don't have yet. Every other tool manages the keys you already own. Provider Radar is how you get the next one.

And because the probes run from your connection, the numbers are about your actual situation — not a status page in another region.

Comparison

Checked against the linked official docs on 2026-07-30. Corrections by PR are welcome; if something below is wrong or out of date, open an issue and it gets fixed.

KeyPulse LiteLLM Helicone OpenRouter
Shape Desktop app Self-hosted server (Docker / K8s) Hosted service, also self-hostable Hosted service
Works with no account anywhere Self-hosted only ❌ — account + credits
Infrastructure to run it None (one SQLite file) Postgres + Redis for production Postgres · ClickHouse · Minio None (it's hosted)
Keys stay on your machine ✅ OS keychain ✅ your infra ✅ self-hosted Encrypted server-side (BYOK)
OpenAI-compatible endpoint ✅ local
What gets ranked your individual API keys model deployments in config.yaml providers / models provider endpoints for a model
Latency-aware routing ✅ 9 strategies latency-based-routing, cost, usage, least-busy Routing across 100+ models sort: "latency" / "throughput", preferred_max_latency
Active health probing ✅ scheduled, per key background_health_checks (300 s default) Deprioritizes providers with outages in the last 30 s
Per-key TTFT + jitter history Latency tracking in dashboards Rolling 5-min latency/throughput percentiles, per provider
Automatic failover on 429 ✅ backoff + Retry-After ✅ cooldowns + fallbacks
Discovers new free-tier providers
Fee on top of provider cost None None 0% markup on gateway credits No token markup; ~5% on credit purchases, and 5% on BYOK above 1M req/mo
GUI included ✅ the whole product Admin UI
License MIT MIT (separate licence for enterprise/) Apache-2.0 Proprietary service

means we could not find it documented on the official pages linked in the header row. It may well exist; absence here is absence of evidence, not a claim about the product.

One distinction the table can't show: OpenRouter's latency and throughput percentiles are measured across its own fleet, and LiteLLM's health checks run from wherever you deployed it. KeyPulse's numbers are measured from the machine you are sitting at, per key, which is the only vantage point that answers "will this be fast for me right now."


Screenshots


Quick start

Under two minutes from clone to a running gateway.

Prerequisites

  • Rust 1.82 or newer (rustup recommended)
  • Node.js 20.19+ or 22.12+ and npm
  • Platform build dependencies for Tauri v2:
    • WindowsWebView2 runtime (preinstalled on Windows 11) and the MSVC build tools
    • macOS — Xcode Command Line Tools (xcode-select --install)
    • Linuxwebkit2gtk-4.1, libayatana-appindicator3, librsvg2, patchelf (see the Tauri prerequisites for your distribution's exact package names)

Run it

git clone https://github.com/Matinj031/KeyPulse.git
cd KeyPulse
npm install
npm run app:dev      # Vite dev server + the Rust core, with hot reload

The first launch creates the database and a master key. On a machine with no usable keychain (a bare Linux container, say) KeyPulse falls back to a file-backed master key with restricted permissions and tells you so in Settings.

Prebuilt installers are not published yet — npm run app:build produces them into src-tauri/target/release/bundle/ if you want one now. Watch releases for the first tagged build.

Add your first key

  1. Add key, top right of Tab 1.
  2. Name it something you'll recognise — Groq — personal, not key 1. Several keys may share a name; they are still scored separately.
  3. Base URL: the API root, e.g. https://api.groq.com/openai/v1. Pasting the full .../v1/chat/completions also works.
  4. Wire format: OpenAI-compatible covers Groq, Together, OpenRouter, Cerebras, SambaNova, DeepInfra, Fireworks, Mistral and most others. Pick Anthropic or Gemini for those two.
  5. Authentication: Bearer for almost everything; Custom header for providers that want x-api-key or similar.
  6. API key: pasted once, encrypted before it touches disk. On a later edit, leaving the field blank keeps the stored key.
  7. Custom headers (optional): Name: value, one per line — sent on probes, model listing and every proxied request for this key. For gateways that want a tenant id, a beta flag, or a particular User-Agent. ⚠️ These are stored and exported in the clear, so the key itself belongs in the field above.
  8. Models: one per line, or hit Fetch from provider once the key is saved to pull them from the provider's own /models. The probe model is the one health checks call — pick the cheapest, fastest one.
  9. Capabilities: the gateway refuses to route a request a key cannot serve, so tick only what the key actually supports.
  10. Save. The first probe runs on the next cycle, or immediately via Probe all now.

Point a client at it

Enable the proxy in Settings, then change one line:

from openai import OpenAI

client = OpenAI(base_url="http://127.0.0.1:8787/v1", api_key="unused")
client.chat.completions.create(
    model="keypulse-auto",                      # or a specific model id
    messages=[{"role": "user", "content": "hi"}],
)

The listener binds to loopback only, so it is not reachable from the network. That is not sufficient on a shared or multi-user machine — any local process can reach a loopback port — so Require a token in Settings adds a bearer token the client must present. Turn it on if you are not the only user of the box.


Architecture

src/                        Vue 3 + TypeScript frontend
  views/                    GatewayView · RadarView · SettingsView
  components/               provider table, charts, dialogs, ui/ primitives
  stores/                   Pinia: health · discovery · settings · toast
  lib/                      typed invoke() wrappers, shared types, formatters

src-tauri/src/              Rust core
  commands.rs               the Tauri command surface the frontend calls
  proxy.rs                  local OpenAI-compatible endpoint + failover loop
  health.rs                 probe scheduler, error classification, quota parsing
  discovery.rs              the radar's sources, relevance gate and de-duplication
  scoring.rs                one key's samples → score, status, series
  routing.rs                a health snapshot → an ordered candidate list
  adapters/                 per-provider wire formats (openai · anthropic · gemini)
  db.rs / crypto.rs         SQLite storage and secret sealing
  state.rs                  shared app state, event emission, proxy handle

Data flows one way. The Rust core owns all state; the frontend reads snapshots through invoke and receives pushes on three events (keypulse://health, keypulse://failover, keypulse://discovery), so both tabs stay live without polling.

scoring and routing are pure functions over plain structs — no clock, no network, no database. That is what makes the decision logic testable without mocks, and it is why the routing preview in Settings can show you the real candidate order instead of an approximation of it.

The adapter is the one abstraction worth investing in. Everything above it speaks the OpenAI chat-completions shape; an adapter is the only thing that knows a provider's real wire format. Supporting a new non-OpenAI API means one new file implementing eight small methods, plus a variant in AdapterKind. There is no repository layer, no DI container and no DDD scaffolding — db.rs is a thin typed wrapper over SQL, and the modules that need it take a &Db.

Why this stack
Choice Reason
Tauri v2 over Electron API keys belong in the OS keychain, not in a Chromium profile. A real systems language for the network layer, and no bundled browser to keep patched.
Rust core The gateway is a long-lived server doing concurrent I/O with a hard requirement that one provider's failure never takes the app down. tokio + reqwest + typed errors make that boring.
axum for the gateway Same async runtime as the rest of the core, and streaming bodies are a first-class type — a chat stream is re-encoded frame by frame, not buffered.
sqlx + SQLite Probe history is time-series data that outlives the process. SQLite is a file with no daemon, and every query here is parameterised — key material and user text never reach a SQL string.
keyring + AES-256-GCM The keychain holds one master key; each API key is sealed under it. A stolen database file is useless on its own.
Vue 3 + Composition API Two dense dashboards with a lot of live state. <script setup> with strict TypeScript keeps components short, and Pinia setup stores read like the functions they are.
Tailwind v4 + shadcn-vue (reka-ui) Unstyled, accessible primitives we own the markup of, rather than fighting a component library's opinions about a data-dense table.
vee-validate + Zod One schema states the provider rules, and the same object types the form and the invoke payload.
uPlot + vue-echarts uPlot draws a hundred sparklines without breaking a sweat; ECharts is the better tool for the interactive historical charts. Different jobs, different libraries.
GSAP Small, deliberate transitions on state change — a key going red should be noticeable without being a distraction.

Configuration

Everything is in Settings inside the app; there is no config file to edit. Values are clamped by the core, so a typo cannot put the app in a bad state.

Health checks, gateway, notifications, discovery, backup

Health checks

Setting Default Range
Probe interval 90 s floor of 30 s; a warning below 60 s
Probe timeout 20 s 5–120 s
History retention 7 days 1 hour – 90 days
Rolling window 20 samples 3–200
Rate-limit cooldown 120 s 10–3600 s
Benchmark runs 5 1–20
Score weights success .4, latency .3, throughput .2, jitter .1 any non-negative set

Local gateway

Setting Default Range
Enabled off
Port 8787 ≥ 1024
Routing strategy best score 9 strategies
Max attempts per request 3 1–10
Request timeout 300 s 10–3600 s
Backoff base 250 ms 50–5000 ms, doubling, capped at 8 s
Require a token off

Notifications — on by default, for keys going down and for quota warnings at 15% remaining (1–90%).

Discovery — off by default; interval 12 h (1 h – 7 days); GitHub, Hacker News and pinned sources individually switchable; optional Brave or Tavily key.

Backup — export your configuration as JSON, with or without the keys. With keys, every key in the bundle is sealed under a passphrase you choose (PBKDF2-HMAC-SHA256 + AES-256-GCM, at least 8 characters); lose the passphrase and the keys in that file are gone. Exports are also written to an exports/ folder in the data directory and offered back as import sources, so the usual restore needs no file picker. Import merges by default — a provider you already have is skipped rather than duplicated — or, in replace mode, deletes every provider and pinned source first. Replace asks for confirmation; export first if you might want it back.

Files and environment

Database, master-key fallback, exports %APPDATA%\dev.keypulse.app\ (Windows) · ~/Library/Application Support/dev.keypulse.app/ (macOS) · ~/.local/share/dev.keypulse.app/ (Linux)
API keys OS keychain (Keychain / Credential Manager / Secret Service)
KEYPULSE_LOG log filter, e.g. KEYPULSE_LOG=keypulse=debug

Security

  • API keys are sealed with AES-256-GCM under a master key held in the OS keychain. Where no keychain is available, the master key falls back to a file with restricted permissions and the app says so in Settings.
  • Keys are masked to their last four characters everywhere in the UI. The plaintext is decrypted only for a request you asked for, and revealing one is an explicit action.
  • Raw keys are never written to logs. Provider error messages sometimes echo a key back; those strings are redacted before they are stored or logged.
  • The gateway binds to 127.0.0.1 only. Enable the bearer token if other people or untrusted processes share the machine.
  • Custom request headers are stored and exported in the clear — only api_key is encrypted. Put secrets in the key field, not in a header.
  • Exported bundles contain no plaintext keys unless you ask for keys and supply a passphrase.

Found something? Open a regular issue for anything non-sensitive. For a suspected vulnerability, please report it privately via GitHub security advisories rather than a public issue.


Playing fair with free tiers

KeyPulse is built to be a good citizen of the free tiers it watches, and it is your responsibility to keep it that way.

  • Probing costs the provider something. Each health check is one small chat completion, but multiplied by your key count and divided by your interval it adds up. The interval has a hard floor of 30 seconds and warns below 60; on a free tier, minutes are usually more appropriate than seconds. Providers with tight daily caps should get a longer interval, and you can stop probing a key by disabling it.
  • Read the terms of the keys you add. Some free tiers prohibit sharing a key across users, automated load distribution, or use in a service that resells access. Pointing the local gateway at your own tools is not the same thing as pooling keys for other people, and only you can tell which side of a given provider's terms you are on.
  • Rate limits are signals, not obstacles. A 429 puts a key in cooldown; that is the intended behaviour, not something to tune away. KeyPulse never retries a key faster than the Retry-After it was given.
  • The radar does not crawl. Documented APIs, public feeds and pages you pinned yourself. It checks robots.txt before fetching a pinned page and reports a source as skipped when it is disallowed. Please don't add sources whose terms forbid automated access.

Development

npm install                     # frontend deps
npm run app:dev                 # the real thing: Rust core + Vue, hot-reloaded
npm run dev                     # frontend only — Tauri APIs are absent, so most of the app is inert
Command What it does
npm run app:dev Run the full desktop app in dev mode
npm run app:build Build the installer for your platform into src-tauri/target/release/bundle/
npm run typecheck vue-tsc --noEmit
npm run build Typecheck, then build the frontend bundle
cd src-tauri && cargo test The Rust test suite
cd src-tauri && cargo clippy --all-targets Lints, including test code
cd src-tauri && cargo fmt Format

Tests. 120 tests, green at the time of writing, over scoring, routing, probe error classification, crypto round-trips, the command surface, discovery parsing and de-duplication, all three adapters, and the proxy's failover behaviour — retryable classification, attempt clamping, backoff doubling and its cap, Retry-After, and SSE frames that arrive split across chunks. Because scoring and routing are pure functions, those tests are plain tables of input and expected output with no mocks in sight.

There is no CI yet — see the roadmap. Until there is, please run cargo test, cargo clippy --all-targets and npm run build before opening a pull request.


Roadmap

Nothing below is a dated promise. It is the order in which things would land, and an honest list of what today's 0.1.0 does not do.

In 0.1.0

  • Local OpenAI-compatible gateway with 9 routing strategies and failover
  • Scheduled health probing — TTFT, latency, throughput, jitter, error taxonomy
  • Quota tracking from rate-limit headers, with time-to-exhaustion estimates
  • On-demand benchmarking and historical charts
  • Provider Radar — GitHub, Hacker News, pinned feeds, optional search APIs
  • openai, anthropic and gemini wire formats
  • Keychain-sealed storage, per-provider custom headers, passphrase-sealed backups

Next

  • CI on pushfmt, clippy, cargo test, vue-tsc
  • Tagged releases with prebuilt installers for Windows, macOS and Linux, so running KeyPulse does not require a Rust toolchain
  • Screenshots, GIFs and an architecture diagram — every image slot in this README is still a placeholder
  • A native Anthropic /v1/messages endpoint on the gateway, so clients that speak that shape rather than the OpenAI one can route through it too
  • More non-OpenAI wire formats — AWS Bedrock and Vertex AI are the obvious next adapters; anything with an OpenAI-compatible endpoint already works today
  • Tool-call and vision passthrough for the non-OpenAI adapters. Today the Anthropic and Gemini adapters translate text only, so a request carrying tools or an image is routed only to keys whose wire format already speaks it. The gateway detects the need and filters the candidate pool rather than silently dropping the field — but the translation itself is not written yet
  • Rate-limit-aware pacing — spread gateway traffic to stay under a published limit, instead of reacting after the 429 has already been spent
  • Headless mode — run the gateway without the window open

Ideas, not yet designed

  • Per-model scoring instead of per-key, for providers whose models fail independently
  • Price-aware routing for paid keys, alongside the latency-aware strategies
  • A metrics endpoint for people who already run Prometheus or Grafana

Deliberately not planned: a hosted version, telemetry of any kind, and any feature whose purpose is sharing one key between several people.


Contributing

Contributions are welcome, including the unglamorous ones. The repository is young — one commit, no releases — which is the best possible time to shape it.

Why it is a pleasant codebase to work in

  • The decision logic is pure. scoring.rs and routing.rs take plain structs and return plain structs. No clock, no network, no database, so a change you make is a test you can write in a minute.
  • One abstraction, on purpose. No repository layer, no DI container, no DDD. If you are looking for where something happens, it is in the file named after it.
  • The tests are real. 120 of them, including a socket-level test that pins header precedence, because a provider once rejected requests over a User-Agent.
  • MIT, no CLA. Your commit is yours.

Good first contributions

There is no issue tracker with labels on it yet, so here is the honest list of self-contained gaps:

# Gap Where
1 A new provider adapter src-tauri/src/adapters/
2 A new routing strategy routing.rs — a pure function and a table test
3 The CI workflow this repo does not have .github/workflows/
4 CONTRIBUTING.md, issue and PR templates, a code of conduct repo root, .github/
5 Screenshots and GIFs for the placeholders above docs/assets/
6 A keyboard-navigation and ARIA pass on the provider table src/components/
7 A discovery source worth pinning that the seed list misses discovery.rs
8 macOS and Linux verification everything was developed and checked on Windows
Adding a provider adapter, end to end

This is the extension point the architecture was built around. A provider that speaks the OpenAI chat-completions shape needs no code at all — add it in the UI with its base URL and auth scheme. A provider with its own wire format needs one file:

  1. Add a variant to AdapterKind in models.rs and wire it into as_str.

  2. Create adapters/yourprovider.rs and implement Adapter:

    Method Answers
    chat_url Where does a completion go?
    translate_request OpenAI-shaped body → the provider's body
    translate_response The provider's body → an OpenAI envelope
    decode_event One SSE data: line → a StreamEvent
    stream_is_openai_compatible Can the stream be passed through untouched?
    extra_headers Constant headers the API requires
    models_url Is there a model list to fetch?
    parse_models That list → model ids

    The last four have defaults; the first four do not.

  3. Return it from for_kind in adapters/mod.rs.

  4. The shared tests in that file — every adapter translates a minimal request, every adapter produces a valid envelope, no adapter mistakes junk for content — will pick your variant up automatically. Add the cases specific to your provider next to them.

Do not touch auth. adapters::apply_headers is the single choke point: adapter constants first, the user's custom headers next, authentication last and never overridable.

House rules
  • Never log, print or commit a raw key. Provider errors can echo one back; health::redact() exists for that reason. Test fixtures use obviously fake keys.
  • Keep the probe-interval floor. 30 seconds is a hard minimum and is not a knob to loosen. Probing costs the provider something.
  • Discovery uses documented APIs, feeds and pages the user pinned. If robots.txt or a source's terms say no, the answer is to report it as skipped, not to route around it.
  • Pure functions stay pure. If scoring or routing needs the clock or the network, the design is wrong — pass the value in.
  • No new layers of indirection unless it removes more code than it adds.
  • Commit messages follow the existing history: feat:, fix:, docs:.

FAQ

Is this a hosted service or a proxy I sign up for?

No. KeyPulse is a desktop application. The gateway is a server that runs on your machine, bound to 127.0.0.1, and requests go straight from your computer to the provider. There is no account, no KeyPulse backend, and nothing to sign up for.

Do my keys leave my machine?

Only to the provider the key belongs to. They are stored sealed with AES-256-GCM under a master key in your OS keychain, masked to their last four characters in the UI, and never written to logs. See Security.

Is this for pooling free keys with other people?

No, and please don't. It routes your keys to your tools. Several free tiers explicitly forbid sharing a key across users or redistributing access, and the gateway listens on loopback precisely so it is not a service other people can reach. See Playing fair with free tiers.

Which clients can point at it?

Anything that lets you set a custom OpenAI-compatible base URL and an API key — the OpenAI SDKs in any language, Cursor, Continue, Aider, LangChain, LlamaIndex, curl. Use http://127.0.0.1:8787/v1, and any key value at all unless you turned the bearer token on. Clients hard-wired to a provider's native API shape — the Anthropic Messages format, for instance — are not covered yet; that endpoint is on the roadmap.

Which providers are supported?

Every provider that exposes an OpenAI-compatible /v1/chat/completions — Groq, Together, OpenRouter, Cerebras, DeepSeek, local llama.cpp or Ollama endpoints, and so on — plus Anthropic and Google Gemini in their own formats. You add providers yourself by base URL and auth scheme, so a new OpenAI-compatible service needs no release from us.

Do I have to run the gateway?

No. It is off by default. The health monitor, benchmarking, quota tracking and radar all work on their own — plenty of people will want the dashboard and nothing else.

Nothing is answering on port 8787.

Two likely reasons, in order. The gateway is disabled by default, so check the toggle in Settings. If it is on and GET /health answers but completions return 503 no_routable_provider, the gateway is fine and no key is currently healthy — the Gateway tab will say which and why.

Does it need Docker, Postgres or Redis?

No. One binary and one SQLite file.

Does it work with paid keys?

Yes. Nothing in the gateway or the health monitor is specific to free tiers; free tiers are simply where the flakiness that motivated it lives.

Is there an installer?

Not yet — there are no tagged releases, so today you build it with npm run app:build. Publishing signed installers is the top item on the roadmap.

What does probing actually cost me?

One small chat completion per enabled key per interval, and the tokens/sec figure is measured from that same call rather than from a separate large download. At the default 90-second interval that is 40 tiny requests per key per hour, which is why the interval has a floor and why free tiers with hard daily caps deserve a longer one.


License

MIT — Copyright (c) 2026 KeyPulse contributors.

Use it, fork it, ship it. There is no hosted tier to upsell you to and no server to switch off: your keys and your probe history are files on your own disk.


If KeyPulse saves you one "why is this key 401-ing" afternoon, a star helps the next person find it.

Report a bug · Suggest a provider · Read the architecture

About

Desktop control room for your AI API keys: encrypted key store, background health probes, 9 routing strategies, and a local OpenAI-compatible failover proxy. Tauri v2 + Vue 3.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages