FreeLLMAPI KeyStatus Options: Understanding API Key Health States

FreeLLMAPI uses five distinct status values—healthy, rate_limited, invalid, error, and unknown—to track the operational state of every stored API key.

Each upstream provider key in FreeLLMAPI (the open-source LLM routing gateway by tashfeenahmed) carries a KeyStatus field that controls whether it receives traffic. The enum is defined centrally in shared/types.ts and drives routing decisions across the health-checking, rate-limiting, and request-dispatching subsystems.

The Five Key Status Values

healthy

A key marked healthy has passed validation and is actively used for routing requests. This is the desired steady-state for production workloads.

The health-check service in server/src/services/health.ts promotes keys to this status after successful credential verification against the upstream provider.

rate_limited

When an upstream returns HTTP 429, the affected key transitions to rate_limited. It is temporarily excluded from the routing pool until a cooldown period expires.

This protects the overall system from hammering providers that have already signaled capacity constraints. The rate-limiting logic at server/src/services/ratelimit.ts#L450 filters these keys before dispatch.

invalid

Keys that fail definitive credential checks (bad API key, revoked credentials, etc.) are marked invalid. These keys are permanently sidelined and will not be retried.

This state prevents wasted requests on known-bad credentials. The health probe captures the specific error and stores it in last_health_error for audit purposes.

error

Error indicates a non-recoverable transport or protocol failure—timeouts, TLS failures, malformed responses—that does not imply the key itself is bad.

The key leaves the active pool but remains in the database for diagnostics. Operators can inspect the stored error details to distinguish transient infrastructure issues from credential problems.

unknown

Freshly added or never-checked keys start as unknown. They are eligible for routing (tentatively) but will be probed immediately to resolve to healthy or invalid.

This default state allows quick onboarding: keys become usable instantly while background validation determines their true health.

How Status Drives System Behavior

The SQLite api_keys table stores status as a string column. Three core services consult it:

Service File Role
Health checker server/src/services/health.ts Periodically probes keys and updates status based on validation results
Rate limiter server/src/services/ratelimit.ts Excludes rate_limited keys from the candidate pool
Router server/src/services/router.ts Selects only healthy or unknown keys for request dispatch

Working with KeyStatus in Code

Type Definition

// shared/types.ts (L252)
type KeyStatus = 'healthy' | 'rate_limited' | 'invalid' | 'error' | 'unknown';

The type is exported from the shared package and consumed by both server and client code.

Querying Usable Keys

import type { KeyStatus } from '@freellmapi/shared/types.js';

function getUsableKeys(platform: string): Promise<Array<{id: number; key: string; status: KeyStatus}>> {
  const stmt = db.prepare(`
    SELECT id, encrypted_key AS key, status
    FROM api_keys
    WHERE platform = ? AND enabled = 1
      AND status IN ('healthy', 'unknown')
  `);
  return Promise.resolve(stmt.all(platform));
}

This pattern appears in server/src/services/media.ts and the router: only healthy and unknown keys are considered for live traffic.

Updating Status After Probes

async function setKeyStatus(
  keyId: number,
  newStatus: KeyStatus,
  errorMsg: string | null = null
): Promise<void> {
  db.prepare(`
    UPDATE api_keys
    SET status = ?,
        last_health_error = ?,
        last_checked_at = datetime('now')
    WHERE id = ?
  `).run(newStatus, errorMsg, keyId);
}

The health service calls this pattern at server/src/services/health.ts#L113 after each validation attempt.

Handling Rate-Limit Responses

async function handleRateLimit(keyId: number): Promise<void> {
  await setKeyStatus(keyId, 'rate_limited', '429 Too Many Requests');
}

This triggers the temporary exclusion pathway enforced by server/src/services/ratelimit.ts.

Key Lifecycle Flow

  1. Creation → Key inserted with status unknown
  2. First request or background probe → Health checker validates credentials
  3. Validation succeeds → Status becomes healthy; key enters rotation
  4. Validation fails definitively → Status becomes invalid; key ignored
  5. Runtime 429 response → Status becomes rate_limited; key suspended for cooldown
  6. Cooldown expires → Health checker re-probes; may return to healthy
  7. Transport failure → Status becomes error; key quarantined for review

Summary

  • Five statuses: healthy, rate_limited, invalid, error, and unknown form the complete state machine for API key health in FreeLLMAPI.
  • Defined in shared/types.ts and stored in the SQLite api_keys table.
  • Routing eligibility: Only healthy and unknown keys receive traffic; rate_limited keys are temporarily blocked; invalid and error keys are excluded.
  • Automatic recovery: rate_limited keys return to healthy after cooldown; unknown keys resolve on first probe.
  • Audit trail: last_health_error captures diagnostic details for invalid, error, and rate_limited transitions.

Frequently Asked Questions

How does FreeLLMAPI handle keys that hit rate limits?

When an upstream provider returns HTTP 429, the key is immediately marked rate_limited in the database. The rate-limiting service at server/src/services/ratelimit.ts filters these keys from the routing pool until a configurable cooldown expires, at which point the health checker revalidates the key.

Can I manually set a key's status?

Yes. The status column is a standard string field in the api_keys table. You can issue direct SQL updates or call internal service methods like setKeyStatus() shown above. Changes take effect on the next routing decision—there is no caching layer that would delay status updates.

What happens to keys with error status?

error indicates a non-recoverable transport or protocol problem rather than a credential failure. These keys are excluded from routing but retained in the database. Operators should inspect last_health_error and last_checked_at to determine whether the issue was transient infrastructure failure or requires manual intervention.

Why does unknown status still allow routing?

unknown is the default for newly added keys. The system permits tentative routing to avoid startup delays, while simultaneously triggering a background health probe. This design balances availability (keys work immediately) with safety (bad keys are quickly demoted to invalid).

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →