npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

llm-key-validator

v1.0.0

Published

Validate API keys for OpenAI, Anthropic, Google Gemini, Groq, Mistral, OpenRouter, Cohere, DeepSeek, Together, Fireworks, xAI, and Perplexity. Zero dependencies. CLI + library.

Readme

llm-key-validator

npm version CI license: MIT node

Validate API keys for OpenAI, Anthropic, Google Gemini, Groq, Mistral, OpenRouter, Cohere, DeepSeek, Together, Fireworks, xAI, and Perplexity — as a CLI or a TypeScript library.

  • Zero runtime dependencies (uses native fetch, Node ≥ 20)
  • Modular: add a new provider in ~15 lines
  • Library + CLI + GitHub Action from one install
  • Distinguishes invalid keys from rate limits, timeouts, and network errors
  • Env-var aware (OPENAI_API_KEY, ANTHROPIC_API_KEY, etc.)
  • Tree-shakable: bundle only the providers you import (~500 B per provider; 2 KB for all 12)
  • Works in Node, Bun, Deno, Cloudflare Workers, and browsers
  • --offline mode for instant format-only validation (no network call)
  • scan subcommand for .env file audits

Who is this for?

  • BYOK SaaS builders (Cursor/Cline/OpenWebUI/LibreChat clones) — one library replaces 12 per-provider error-mapping branches
  • Developers with many AI keyslkv --env tells you which still work
  • DevOps / platform teams — CI/CD pre-flight and cron monitoring; clean exit codes
  • On-call debugging — instantly distinguishes "key revoked" from "provider down"

Not the right fit for: server-side runtime auth (handle 401 at request time), org/scope introspection, cost/usage analytics.

Install

# global CLI (installs both `lkv` and `llm-key-validator` commands)
npm i -g llm-key-validator

# or one-shot without installing
npx llm-key-validator <key>

# as a library
npm i llm-key-validator

After global install, use the short alias lkv everywhere — both commands are interchangeable.

CLI

# auto-detect provider from key prefix
lkv sk-ant-...

# force a provider
lkv <key> --provider openai

# read keys from stdin (preferred — avoids shell history)
echo "$OPENAI_API_KEY" | lkv --stdin

# bulk
lkv <key1> <key2> <key3>

# validate every <PROVIDER>_API_KEY found in the environment
lkv --env

# provider env-var fallback: read OPENAI_API_KEY from env
lkv --provider openai

# JSON output for scripting
lkv <key> --json

# list all supported providers (and their env vars)
lkv --list-providers

# offline mode — format-only check, no network call
lkv --offline sk-ant-...

# scan a .env file (.env and .env.local are checked by default)
lkv scan
lkv scan .env.production
lkv scan --check-revoked   # hide VALIDs, show only failures

Exit codes: 0 all valid, 1 usage error, 2 ≥ 1 invalid, 3 unexpected error.

Library

Default (eager — all 12 providers loaded)

import { validateKey, validateKeys } from "llm-key-validator";

const result = await validateKey("sk-ant-...");
if (result.valid) {
  console.log("OK —", result.provider, "in", result.latencyMs, "ms");
} else {
  console.log("Failed:", result.reason, result.message);
}

// Bulk
const results = await validateKeys([key1, key2]);

Tree-shakable (only pay for what you use)

For browser bundles, edge workers, or library consumers that only care about one or two providers, import from /core + /providers:

import { validateKey } from "llm-key-validator/core";
import { openai, anthropic } from "llm-key-validator/providers";

await validateKey(key, { providers: [openai, anthropic] });

The core entry has zero side effects; bundlers will drop every provider you don't import. Typical bundle sizes:

| Entry | Size (min+gz) | |---|---| | llm-key-validator (eager, 12 providers) | ~5 KB | | llm-key-validator/core (no providers) | ~2 KB | | llm-key-validator/providers (single import) | ~0.5 KB per provider |

Options

await validateKey(key, {
  provider: "anthropic",  // force, skip auto-detect
  providers: [openai],    // scope detection to this list (tree-shake friendly)
  offline: true,          // skip network call; format-only check
  timeoutMs: 5000,        // default 10_000
  retries: 1,             // default 1 (5xx / network errors)
  fetch: customFetch,     // injectable
  signal: abortSignal,
});

Browser, Bun, Cloudflare Workers, Deno

The library is pure fetch + AbortController — no Node-specific globals. See examples/ for a browser HTML demo, a Cloudflare Worker, and a Bun script.

Result shape

type ValidationResult =
  | { valid: true;  provider: string; latencyMs: number; metadata?: Record<string, unknown> }
  | { valid: false; provider: string; reason: ValidationFailureReason; status?: number; message: string; latencyMs: number };

type ValidationFailureReason =
  | "invalid_key"      // 401 / 403
  | "rate_limited"     // 429 — key is likely valid but throttled
  | "server_error"     // 5xx after retries
  | "network_error"    // DNS / connection refused
  | "timeout"
  | "unknown_provider"
  | "malformed_key";

Supported providers

| Provider | Env var | Key prefix | Endpoint | |---|---|---|---| | OpenAI | OPENAI_API_KEY | sk-... | GET /v1/models | | Anthropic | ANTHROPIC_API_KEY | sk-ant-... | GET /v1/models | | Google Gemini | GOOGLE_API_KEY | AIza... | GET /v1beta/models?key= | | Groq | GROQ_API_KEY | gsk_... | GET /openai/v1/models | | Mistral | MISTRAL_API_KEY | 32 alphanumeric | GET /v1/models | | OpenRouter | OPENROUTER_API_KEY | sk-or-... | GET /v1/auth/key | | Cohere | COHERE_API_KEY | 40 alphanumeric | GET /v1/models | | DeepSeek | DEEPSEEK_API_KEY | sk-<32 hex> | GET /models | | Together | TOGETHER_API_KEY | 64 hex or tgp_v1_... | GET /v1/models | | Fireworks | FIREWORKS_API_KEY | fw_... | GET /inference/v1/models | | xAI (Grok) | XAI_API_KEY | xai-... | GET /v1/models | | Perplexity | PERPLEXITY_API_KEY | pplx-... | POST /chat/completions (max_tokens: 1) |

Note: most providers expose a /models listing that costs nothing. Perplexity has no such endpoint — a single 1-token chat call is the smallest probe and may incur a tiny charge.

Auto-detect caveat: Mistral and Cohere keys are opaque alphanumeric strings with no distinguishing prefix. They are tried last during auto-detect to minimize false positives, but for any 32- or 40-character alphanumeric input, prefer --provider mistral or --provider cohere to be explicit.

Adding a new provider

// my-provider.ts
import { registerProvider, type Provider } from "llm-key-validator";

const myProvider: Provider = {
  name: "myprovider",
  displayName: "My Provider",
  detect: (key) => key.startsWith("mp_"),
  validate: async (key, { http, signal }) => {
    const res = await http("https://api.myprovider.com/v1/me", {
      headers: { Authorization: `Bearer ${key}` },
      signal,
    });
    return { status: res.status, ok: res.ok };
  },
};

registerProvider(myProvider);

GitHub Action

Validate every *_API_KEY secret in your repo on every PR:

# .github/workflows/check-keys.yml
name: Check keys
on: [pull_request, workflow_dispatch]

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: jishnu-mohan/llm-key-validator@v1
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        with:
          args: "--env --json"

The default args (--env --json) validates every standard *_API_KEY env var exposed to the job. Fails the build (exit 1) if any key is invalid.

Security

  • Validation calls hit the live provider APIs — only validate keys you own
  • Some endpoints count toward rate limits even when not billed
  • Prefer --stdin over passing keys on argv (shell history)
  • Keys are never logged in full; CLI output masks to first 7 and last 4 characters

See SECURITY.md for the disclosure process.

License

MIT