---
title: "09 — API Keys & Management: Issuing, Scoping, Rotating"
uid: api-keys-management
tags: ["api-keys", "security", "rotation", "management", "roadmap:api-design", "scoping"]
excerpt: "The key itself is trivial; the discipline around it is the work. An API key is a long-lived secret that identifies a caller — and management means generation, least-privilege scoping, hashing at rest, and rotation without downtime."
date: 2026-08-13T03:28:33+0000
source: https://www.aveshina.my.id/en/blog/api-keys-management
---

API keys look like the simplest part of an API — a string, a header, done — which is exactly why the roadmap gives them their own major node. The model that finally clicked: **an API key is a long-lived secret that identifies a caller, and "management" is the discipline that keeps that secret from becoming a liability — strong generation, scoping to the minimum permissions, hashing at rest, and rotation without service interruption.** [1][2] The key itself is trivial; everything around it is the actual work.

The thread connecting the sub-nodes is that a key without management is a liability waiting to happen. An unscoped key that lives forever in plaintext and can't be rotated is the classic breach shape. The practices below exist to make a leaked key low-damage and recoverable.

## What an API key is (and isn't)

An **API key** is a unique string a caller presents to identify themselves to an API — usually in a header (X-API-Key) or a query parameter [1]. Conceptually it's close to a long-lived token: the server receives it, looks it up (or hashes it and looks up the hash), and maps it to a caller and a set of permissions. The simplest way of thinking is "a username and password rolled into one string, for machines."

The distinctions I had to keep straight: a key identifies *a calling program*, not a human user — it's the right tool for server-to-server or script access, not for login flows (those want OAuth/OIDC from the previous posts). And keys are conventionally **bearer secrets** — anyone who holds the key can use it — which is why everything below is about limiting the blast radius if one leaks.

## Key generation: entropy is the only metric

**Generation** is about producing keys that resist brute force [3]. Two properties matter: **length** and **randomness** (entropy). A 32+ byte cryptographically random key, base64- or hex-encoded, is the floor. Predictable keys (key-1, key-2, timestamps, hashes of the company name) are the kind of thing that gets an API scraped by a script in an afternoon. The rule: use your language's cryptographically secure RNG, not a regular one, and never derive a key from anything guessable.

The other generation discipline is **uniqueness** — each issued key must be globally unique so a lookup can't collide. A long enough random key makes collisions astronomically unlikely, but storing keys with a unique prefix or ID (separate from the secret itself) makes management and logging easier without exposing the secret.

## Hashing at rest and the prefix trick

A rule I now treat as non-negotiable: **never store API keys in plaintext.** Store a hash (SHA-256 is fine; you don't need a password hash because keys are high-entropy) and compare hashes on each request [2]. If your database leaks, the hashes are useless to an attacker. The downside is you can't show a caller their key again after creation — which is correct behavior; you show it once and tell them to store it.

A common pattern that solves the "I can't recognize my own keys" problem is the **prefix + secret** format (sk_live_abc123...). The prefix is non-secret and identifies the key in your UI and logs; the secret part is what's hashed. Stripe, GitHub, and others use this shape. It lets a customer find a leaked key in their code by its prefix without the prefix itself being enough to authenticate.

## Scopes and permissions: least privilege

This is the single most important management practice. **Scopes** are labels attached to a key (or token) that declare what it's allowed to do [4]. Instead of one all-powerful key per caller, you issue scoped credentials:

```
key: sk_live_orders_read_...
scopes: [ orders:read ]     // can fetch orders, nothing else

key: sk_live_admin_...
scopes: [ orders:read, orders:write, users:read ]
```

The principle at work is **least privilege**: a key should be able to do only what its specific job requires, and nothing more [4]. A reporting script that only reads orders gets an orders:read key, not a full-access one. The payoff is blast-radius reduction — when (not if) a key leaks, the damage is bounded by its scopes. A read-only leak is annoying; a full-access leak is an incident. Scopes turn a catastrophic event into a contained one.

The discipline is to make scopes granular enough to be useful (resource:action pairs like orders:write, users:read) and to resist the temptation to hand out * scopes except for truly trusted internal callers. Scopes come from OAuth 2.0 originally, but they apply identically to plain API key systems.

## Rotation: replacing a key without downtime

**Rotation** is replacing an existing key with a new one — on a schedule or after a suspected compromise — without breaking the consumer's service [3]. The hardest part isn't generating the new key; it's doing the swap without a window where the old key is dead and the new one isn't deployed yet.

The mechanics I rely on:

- **Overlap window.** When rotating, issue the new key first, let the consumer deploy it, and only then revoke the old one. A scheduled rotation should always have a grace period measured in days, not seconds.
- **Two active keys.** Support at least two valid keys per caller during rotation. The consumer adds the new one, confirms it works, then you revoke the old.
- **Scheduled rotation.** Don't rely on "we'll rotate when something goes wrong." Set a cadence (90 days, 1 year) and automate reminders. Long-lived keys are debt.
- **Emergency rotation.** Have a documented process for "revoke this key now" so a suspected leak doesn't become an unprepared fire drill.

The model: rotation should be boring. If it's exciting, you haven't practiced it, and the first time you really need it will be the worst possible moment to learn.

## Management as a first-class concern

Stepping back, "API management" is the umbrella over all of this — the tools and practices for governing keys across their whole lifecycle [1][2]. That includes the things above plus: a way for consumers to mint and revoke their own keys (so support tickets don't bottleneck rotation), analytics on key usage (which keys are actually being used; which are stale and revocable), rate limits per key, and audit logs of when each key was used. The pattern I look for: a key should be **self-service to rotate, scoped by default, observable in use, and revocable instantly**. Any of those missing and the management story is incomplete.

## How I use this

When shipping an API that takes keys, I treat four things as table stakes: cryptographically random generation, hashing at rest (never plaintext), scopes from day one (even if there's only one scope initially — adding scoping later is painful), and a documented rotation path with an overlap window. When I issue a key for my own use against a third-party API, I scope it to the minimum the script needs and rotate it on a schedule rather than letting it live forever in a .env. And I treat any key in version control or logs as compromised-by-default — rotate immediately, because the cost of rotation is trivial next to the cost of a live leaked full-scope key.

## References

[1] AKeyless, "What is API Key Management?," 2024. [Online]. Available: [https://www.akeyless.io/secrets-management-glossary/api-key-management/](https://www.akeyless.io/secrets-management-glossary/api-key-management/)

[2] Infisical, "API Key Management - Definition and Best Practices," 2024. [Online]. Available: [https://infisical.com/blog/api-key-management](https://infisical.com/blog/api-key-management)

[3] CodeSignal, "API Key Generation Basics," 2024. [Online]. Available: [https://codesignal.com/learn/courses/api-key-authentication-security/lessons/api-key-generation-basics](https://codesignal.com/learn/courses/api-key-authentication-security/lessons/api-key-generation-basics)

[4] Auth0, "Permissions, Privileges, and Scopes," 2024. [Online]. Available: [https://auth0.com/blog/permissions-privileges-and-scopes/](https://auth0.com/blog/permissions-privileges-and-scopes/)

```quiz
Q: Where should an API key be stored on the server side?
- In plaintext, so it can be displayed to the customer later
- As a hash; the plaintext is shown once at creation and never stored
correct: 1
explain: Hash keys at rest. If the database leaks, hashes are useless to an attacker. Show the plaintext once at creation and never again.

Q: What is the principle behind scoping an API key to `orders:read` only?
- Least privilege — the key can do only what its job requires, limiting damage if it leaks
- Scopes make keys faster
correct: 0
explain: A scoped key bounds the blast radius of a leak. A read-only leaked key is annoying; a full-access leaked key is an incident.

Q: Why must key rotation include an overlap window where both old and new keys are valid?
- So the consumer can deploy the new key before the old one stops working, avoiding downtime
- Because old keys can never be revoked
correct: 0
explain: Issue the new key, let the consumer deploy it, then revoke the old. Without overlap, there's a gap where no key works.

Q: The `sk_live_abc123...` prefix format (prefix + secret) exists so that…
- the non-secret prefix identifies the key in UI/logs without the prefix alone being enough to authenticate
- the prefix doubles as a backup key
correct: 0
explain: The prefix is non-secret and recognizable; only the secret part is hashed and used to authenticate. This lets customers find a key by prefix without exposing the full secret.

Q: A good rotation practice is to treat a key found in version control or logs as…
- fine, since keys are meant to be shared
- compromised-by-default and rotate it immediately
correct: 1
explain: Any key in version control or logs should be considered leaked. Rotate immediately — the cost is trivial next to a live full-scope key being exploited.
```
