# FreeLLMAPI KeyStatus Options: Understanding API Key Health States

> Explore the five API key status options in FreeLLMAPI: healthy, rate_limited, invalid, error, and unknown. Understand API key health states effectively.

- Repository: [Tashfeen/freellmapi](https://github.com/tashfeenahmed/freellmapi)
- Tags: api-reference
- Published: 2026-08-28

---

**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`](https://github.com/tashfeenahmed/freellmapi/blob/main/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`](https://github.com/tashfeenahmed/freellmapi/blob/main/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`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/health.ts) | Periodically probes keys and updates status based on validation results |
| Rate limiter | [`server/src/services/ratelimit.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/ratelimit.ts) | Excludes `rate_limited` keys from the candidate pool |
| Router | [`server/src/services/router.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/router.ts) | Selects only `healthy` or `unknown` keys for request dispatch |

## Working with KeyStatus in Code

### Type Definition

```typescript
// 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

```typescript
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`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/media.ts) and the router: only `healthy` and `unknown` keys are considered for live traffic.

### Updating Status After Probes

```typescript
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

```typescript
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`](https://github.com/tashfeenahmed/freellmapi/blob/main/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`](https://github.com/tashfeenahmed/freellmapi/blob/main/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`](https://github.com/tashfeenahmed/freellmapi/blob/main/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`).