An API key is a credential — a long opaque string a client presents to a service to identify itself. Unlike a password, it isn’t typed by a human, so it can be longer and more random than a password ever could. The tool above generates API keys using the browser’s crypto.getRandomValues() API; the value is generated in your browser and never transmitted.
What “good” means for an API key
Three properties matter:
- Unguessable. The key must come from a cryptographically secure random number generator (CSPRNG).
Math.random()is not a CSPRNG — its output is predictable to an attacker who observes a few outputs. Browsercrypto.getRandomValues()is. Node.jscrypto.randomBytes()is. Anything else, check the docs. - Long enough. 128 bits of entropy is the minimum bar (2¹²⁸ keyspace), 256 is conservative. That’s 16–32 raw bytes, which encode to 22–43 characters in base64url or 32–64 in hex.
- Distinguishable from other keys. A common pattern: a short human-readable prefix that identifies the issuer or environment, then a random suffix.
sk_live_8K9F3p2N...andsk_test_4mP7Q1R...are immediately distinguishable as Stripe live vs test keys, which makes leak-detection easier.
A typical layout
prefix _ env _ randomness
sk _ live _ 9KdQ8pWnZ3F4mR7vT2cY1B6jN5xLs0gH
- Prefix. Identifies the key type or service. Stripe uses
sk_for secret,pk_for publishable. GitHub usesghp_for personal access tokens,ghs_for server tokens. - Environment.
livevstestis widely standard. - Randomness. 32 base64url characters = 192 bits of entropy. More than enough.
The tool above emits plain random keys (hex or base64url), a UUID v4, or a key with a fixed sk_live_ prefix. For your own prefix, generate a plain key and prepend it — the randomness is all in the generated part.
Encoding choices
| Encoding | 16 raw bytes becomes | Properties |
|---|---|---|
| Hex | 32 chars (a–f, 0–9) | Case-insensitive, no special chars, ~50% denser than binary |
| Base64 | 24 chars + padding | Includes +, /, = — needs URL-encoding in URLs |
| Base64url | 22 chars (no padding) | URL- and header-safe out of the box |
| ASCII alphanumeric | ~26 chars | Skips ambiguous chars (0/O, 1/l) — used by some “human-readable” key generators |
For machine-to-machine API keys, base64url is the typical choice — dense, URL-safe, no special characters. For keys that humans might type once (a setup wizard), the ambiguous-character-stripped alphanumeric form is friendlier. See URL encoding for why URL-safety matters in tokens.
What clients should do with an API key
- Send it in the
Authorizationheader, not the query string. Query strings end up in access logs, inRefererheaders when the page links elsewhere, and in browser history. - Use HTTPS-only. An API key over HTTP is a credential broadcast in cleartext to every network hop.
- Rotate periodically. A key that’s been live for two years has likely leaked somewhere — a misconfigured CI log, a checked-in
.env, a developer’s laptop that got resold. - Scope when possible. Multiple keys with limited scopes are safer than one master key. Stripe’s restricted keys, AWS IAM access keys with minimal policies — these are the patterns.
What servers should do with an API key
- Hash it before storing. A leaked database of plaintext API keys is a breach of every customer. Servers should store
sha256(key)and look up by hash, exactly the way they store password hashes (though typically without the deliberate slowness of bcrypt — keys have 128+ bits of entropy, so dictionary attacks are impossible). - Show the plaintext key once. When a user creates a key, show it; on every subsequent view, show only a prefix or last-4 characters. If the user loses the key, they regenerate. This is now standard across SaaS platforms.
- Constant-time comparison on lookup. When verifying a candidate key against the stored hash, compare in constant time to avoid timing attacks. Most modern frameworks have a
crypto.timingSafeEqualor equivalent. - Log key usage. Per-key request counts and last-seen timestamps make leak detection viable — a key that suddenly fires a million requests from a new IP is detectable in real time.
What an API key isn’t
- Not a password. It’s longer, machine-typed, and revocable independently of the user account.
- Not a session token. Sessions are short-lived, tied to a user-agent, and often bear additional context (logged-in-as, MFA status). API keys are generally long-lived and identify the application/integration.
- Not a JWT. A JWT carries claims and is verifiable without a server lookup. An API key is opaque — the server has to look it up. Both are valid; the choice depends on whether stateless verification is worth the JWT’s complexity. See JWT decoder.
- Not an OAuth token. OAuth flows produce short-lived access tokens via a delegation handshake. API keys are simpler but require the user to copy-paste secrets, which is more error-prone for end-user integrations.
Common pitfalls
- Generating keys with weak randomness. Especially in older Node.js code or browser code that defaults to
Math.random(). Usecrypto.randomBytesorcrypto.getRandomValues. - Embedding keys in client-side JavaScript. Anything in the browser is visible to the user. “Public” keys (like Stripe’s
pk_*) are designed for this; secret keys are not. - Checking keys into git. The single most common cause of leaked API keys. Use
.envfiles, secret managers, andgit-secrets-style scanners. - Reusing keys across environments. A test-environment key that accidentally points at prod is one of the worst possible misconfigurations.
For raw-randomness generators with different shapes, see UUID generator and password generator.