How to Manage API Keys Without Losing Your Mind
Managing API keys well comes down to five habits: never hardcode them, store them in a secrets manager or environment variables, scope each key to the minimum access it needs, rotate them on a schedule (not just after an incident), and revoke unused keys immediately. Everything else — dashboards, naming conventions, audit logs — exists to make those five habits easier to follow consistently across a team.
Most API key problems aren't caused by weak keys or bad encryption. They're caused by process failures: a key committed to a public repo, a contractor's key that's still active six months after the contract ended, a single shared key used by three services so nobody can rotate it without breaking something. This guide covers how to avoid those failures in practice, whether you're managing keys for your own app or for a team consuming a third-party service.
Store keys outside your code
The first rule is the simplest to state and the easiest to violate under deadline pressure: API keys never go in source code, never in commit history, and never in client-side JavaScript that ships to browsers.
Instead:
- Use environment variables for local development and CI.
- Use a secrets manager (AWS Secrets Manager, HashiCorp Vault, Doppler, or your cloud provider's equivalent) for staging and production.
- Add
.envand any credential files to.gitignorebefore you write the first line that uses them, not after.
# .env (never committed)
SUBTOAPI_KEY=sub_live_xxxxxxxxxxxxxxxxxxxx
const apiKey = process.env.SUBTOAPI_KEY;
const response = await fetch("https://api.subtoapi.app/v1/messages", {
method: "POST",
headers: {
"Authorization": `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "claude-sonnet-4",
messages: [{ role: "user", content: "Summarize this ticket." }],
}),
});
If a key ever does end up in a git history by accident, treat it as compromised: revoke it and issue a new one. Rewriting history doesn't undo the exposure — anyone who cloned the repo before the fix already has it.
Scope keys to what they actually need
A single API key with full account access is a liability the moment more than one service or person uses it. Whenever the platform supports it, create separate keys per environment and per purpose:
- One key for local development
- One key for staging/CI
- One key per production service
- Separate keys for internal tools versus customer-facing integrations
This matters for two reasons. First, if one key leaks, the blast radius is limited to whatever that key can do — not your entire account. Second, scoped keys make debugging easier: when something breaks, you know exactly which key (and therefore which service) is responsible from the request logs.
SubToAPI generates sub_live_... keys per application, so you can issue one key for your production backend, another for a staging environment, and another for an internal automation script, all under the same account without sharing credentials between them.
Rotate keys on a schedule, not just after a leak
Rotation is the habit most teams skip because nothing forces it. A practical rotation policy looks like this:
- Set a default lifetime for keys (90 days is a common baseline for anything customer-facing).
- Generate the new key before revoking the old one, so there's a window where both work.
- Update the key everywhere it's used — environment variables, CI secrets, deployed services.
- Confirm the new key is live in production logs.
- Revoke the old key.
Skipping step 2 is the most common mistake: revoking the old key immediately causes an outage the moment a deploy or cron job runs with the stale credential still cached.
# Verify the new key works before revoking the old one
curl https://api.subtoapi.app/v1/messages \
-H "Authorization: Bearer $NEW_SUBTOAPI_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"claude-sonnet-4","messages":[{"role":"user","content":"ping"}]}'
Revoke unused keys immediately
Every API key that exists but isn't actively used is pure risk with no benefit. Common sources of orphaned keys:
- A contractor or former employee's personal key
- A key generated for a one-off script that's still active a year later
- A key baked into a demo or proof-of-concept that got forgotten
Review active keys quarterly at minimum. If a key hasn't made a request in 30–60 days, that's a strong signal to revoke it and see if anything breaks — if it does, you now know about a dependency you'd otherwise have missed.
Give keys names people actually understand
"key-3" and "prod-key-final-v2" tell you nothing six months from now. Name keys by what they're for and where they run: backend-prod-eu, ci-pipeline, support-tool-internal. When you're staring at a usage dashboard trying to figure out which key is generating unexpected traffic, a clear name saves you from grepping through every service's config.
Manage keys per team member, not per team
Shared keys across a team are convenient until someone leaves, a key needs rotating, or you need to know who made a specific request. Issue individual keys or application-level keys tied to a person or service, and use team/seat-based access controls if your provider supports them, so removing one person's access doesn't require rotating a key everyone else depends on.
SubToAPI's Team plan supports per-seat access so each team member or application gets its own key under a shared billing account, and usage metadata is attached to each key so you can see exactly which key is driving cost and volume. If you're consolidating access to Claude across a team, the quickstart walks through generating your first key and making a request in a few minutes, and the Messages API docs cover request formatting in more depth.
A minimal checklist
- Keys live in environment variables or a secrets manager — never in code or client-side JS
- Each environment and service gets its own key
- Keys are named descriptively
- Rotation happens on a schedule with overlap, not reactively
- Unused keys are found and revoked at least quarterly
- Team access is per-person or per-app, not one shared credential
questions
How often should I rotate API keys? For anything customer-facing or handling sensitive data, 90 days is a reasonable default. For low-risk internal tools, 6–12 months is often acceptable. Always rotate immediately if a key is exposed, regardless of schedule.
Should every environment have its own API key? Yes. Separate keys for development, staging, and production limit the damage if one leaks and make it obvious which environment generated a given request when you're debugging.
What's the safest way to store an API key in a project? Environment variables for local work, a secrets manager for deployed environments, and never in source code, config files committed to git, or client-side JavaScript.