← Blog

Secure API Key Storage for LLM Apps

2026-09-22 · 5 min read · SubToAPI Team

The short answer

Secure API key storage for LLM apps means three things: never ship keys to the client, keep them out of version control, and load them at runtime from a secret manager or environment variable rather than hardcoding them. If your app calls an LLM provider directly from a browser or mobile client, you've already lost — any user can open dev tools and steal the key. The fix is a server-side layer between your users and the LLM provider, with the actual key living only in server memory or a managed secrets store.

This matters more for LLM apps than for typical API integrations because LLM keys are usually billed by usage with no hard spending cap by default. A leaked OpenAI, Anthropic, or Claude key doesn't just expose data — it can run up a bill in hours if someone scripts against it. The rest of this article covers where to store keys, how to scope them, and how to structure your app so a leak is inconvenient instead of catastrophic.

Where keys should never live

Where keys should live

1. Environment variables (baseline)

For small projects and prototypes, environment variables loaded from a .env file (excluded via .gitignore) are the minimum bar:

# .env — never committed
LLM_API_KEY=sk-live-xxxxxxxxxxxxxxxx
import 'dotenv/config';
const apiKey = process.env.LLM_API_KEY;

This keeps the key out of source control but doesn't solve rotation, auditing, or per-environment scoping. Treat it as a starting point, not a production strategy.

2. Managed secret stores (production baseline)

For anything running in production, use a dedicated secrets manager: AWS Secrets Manager, Google Secret Manager, HashiCorp Vault, or your hosting platform's built-in secrets (Vercel, Railway, Fly.io all have one). These give you:

A typical pattern is to fetch the secret at boot time and hold it in memory, never writing it to disk or logs:

import { SecretManagerServiceClient } from '@google-cloud/secret-manager';

const client = new SecretManagerServiceClient();
async function getApiKey() {
  const [version] = await client.accessSecretVersion({
    name: 'projects/my-project/secrets/llm-api-key/versions/latest',
  });
  return version.payload.data.toString();
}

3. A server-side proxy in front of the LLM provider

Even with a secrets manager, you still need an architecture where the key never crosses the network to an untrusted client. The standard shape is:

Client → Your backend (holds the key) → LLM provider

Your backend authenticates the client with its own session or API key, then makes the LLM call server-side using the provider key from your secret store. This also gives you a single choke point for rate limiting, logging, and cost control — instead of every client-facing service needing direct access to the raw LLM key.

This is exactly the gap SubToAPI fills if you're building on Claude. Instead of distributing your raw Anthropic access to every service or teammate, you generate scoped sub_live_... application keys from a dashboard, each tied to a project, with its own usage metadata. Revoking one doesn't touch the others, and the underlying Claude access never has to be shared directly. See /docs/quickstart for the setup, or /pricing for plan details.

Scoping and rotation, not just storage

Storage is half the problem. The other half is limiting blast radius when a key does leak.

If you're managing keys across multiple LLM providers or multiple Claude sub-accounts, doing this manually gets tedious fast. A dashboard that issues application-level keys per project — with streaming, tool use, and usage metadata built in — removes most of the manual bookkeeping. That's the core of what SubToAPI does; see /docs/messages and /docs/streaming for how requests are structured once keys are scoped this way.

A minimal checklist

None of this is exotic — it's the same discipline you'd apply to database credentials or payment API keys. The reason it gets skipped for LLM apps is usually speed: prototypes call the provider directly because it's faster to ship, and the shortcut never gets removed. Building the proxy layer in from day one, even a thin one, avoids that trap entirely.

questions

Do I need a secrets manager for a small side project, or is a .env file enough? A .env file kept out of version control is fine for local development and prototypes. Once the app has real users or handles billed API usage, move to a managed secrets store — the cost is minimal and it closes the biggest leak vector: accidental commits and shared dev machines.

Is it safe to call an LLM API directly from a mobile or browser app if I obfuscate the key? No. Obfuscation and minification don't prevent extraction — any key present in a shipped bundle can be pulled out with basic tooling. Always route LLM calls through a server you control.

How often should LLM API keys be rotated? Quarterly is a reasonable default for most applications, with immediate rotation after any suspected leak, employee offboarding, or unusual usage spike in your logs. Scoping keys per service makes rotation lower-risk since you're not breaking every integration at once.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →