How Stripe Generates API Keys: Reverse Engineering the Pattern
Stripe's API key format has become the de facto standard for modern APIs. The sk_live_ prefix is instantly recognizable, and dozens of companies — from OpenAI to Resend — have adopted similar patterns. But what exactly is the structure behind a Stripe key? How much entropy does it contain? And how can you implement the same pattern for your own API?
This article reverse-engineers the Stripe API key format by analyzing publicly documented keys, examining the prefix taxonomy, calculating the entropy, and providing production-ready code to generate identical-style keys.
Anatomy of a Stripe API Key
A Stripe API key consists of three parts:
sk_live_EXAMPLE_DO_NOT_USE_1234567890abcd
|__|____|____________________________________________|
type env random portion (base62)
- Type prefix (
skorpk): identifies whether the key is a secret key or publishable key - Environment (
_live_or_test_): identifies the Stripe environment - Random portion: a cryptographically random string using base62 characters (A-Z, a-z, 0-9)
The Complete Prefix Taxonomy
Stripe uses a systematic prefix scheme that has expanded over the years:
| Prefix | Type | Visibility | Use Case |
|---|---|---|---|
sk_live_ | Secret key | Server only | Production API calls |
sk_test_ | Secret key | Server only | Test/sandbox API calls |
pk_live_ | Publishable key | Client safe | Stripe.js, mobile SDKs |
pk_test_ | Publishable key | Client safe | Test client integration |
rk_live_ | Restricted key | Server only | Limited-permission keys |
rk_test_ | Restricted key | Server only | Test restricted keys |
whsec_ | Webhook secret | Server only | Webhook signature verification |
This prefix system provides three immediate benefits:
- Accidental exposure detection: GitHub's secret scanning can identify a leaked
sk_live_key by pattern matching the prefix, then alert Stripe and the key owner within seconds - Environment safety: A production key cannot accidentally work in test mode, and vice versa, because the server validates the prefix against the current environment
- Permission clarity: Developers can instantly tell whether a key is safe to embed in client-side code (
pk_) or must be kept secret (sk_,rk_)
Entropy Analysis
The random portion of a Stripe key uses base62 encoding (uppercase letters, lowercase letters, and digits). Based on publicly documented Stripe keys, the random portion is typically 24-32 characters long.
For a 24-character base62 random portion:
- Alphabet size: 62
- Bits per character: log2(62) = 5.954
- Total entropy: 24 x 5.954 = 142.9 bits
For a 32-character base62 random portion:
- Total entropy: 32 x 5.954 = 190.5 bits
Both values exceed the 128-bit minimum for cryptographic security. At 143 bits, brute-forcing the key at one trillion guesses per second would take approximately 3.5 x 10^19 years — about 2.5 billion times the age of the universe.
Stripe's key entropy sits comfortably between AES-128 (128 bits) and AES-256 (256 bits). This is more than sufficient for API authentication, especially when combined with rate limiting and monitoring.
How Stripe Likely Generates Keys
While Stripe has not published their exact key generation code, we can infer the process based on the output format and industry best practices:
- Generate 24-32 bytes of random data using a CSPRNG (likely
/dev/urandomor equivalent on their infrastructure) - Encode the random bytes into base62 characters
- Prepend the appropriate prefix (
sk_live_,pk_test_, etc.) - Store a SHA-256 hash of the complete key in the database
- Display the full key to the user exactly once
- Register the key prefix with GitHub's secret scanning partnership program
The key is never stored in plaintext. When a user makes an API call, Stripe hashes the provided key and compares it against stored hashes.
Building Stripe-Style Keys: JavaScript
function generateStripeStyleKey(type = 'sk', env = 'live', length = 24) {
const prefix = `${type}_${env}_`;
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const bytes = new Uint8Array(length);
crypto.getRandomValues(bytes);
let random = '';
for (let i = 0; i < length; i++) {
random += chars[bytes[i] % chars.length];
}
return prefix + random;
}
// Usage
generateStripeStyleKey('sk', 'live'); // sk_live_EXAMPLE_DO_NOT_USE_1a2b
generateStripeStyleKey('pk', 'test'); // pk_test_EXAMPLE_DO_NOT_USE_3c4d
generateStripeStyleKey('rk', 'live'); // rk_live_EXAMPLE_DO_NOT_USE_5e6f
Building Stripe-Style Keys: Python
import secrets
import string
def generate_stripe_style_key(
key_type: str = 'sk',
env: str = 'live',
length: int = 24
) -> str:
prefix = f'{key_type}_{env}_'
chars = string.ascii_letters + string.digits
random_part = ''.join(secrets.choice(chars) for _ in range(length))
return prefix + random_part
# Usage
print(generate_stripe_style_key())
# sk_live_EXAMPLE_DO_NOT_USE_1a2b3c
Building Stripe-Style Keys: Go
package main
import (
"crypto/rand"
"fmt"
"math/big"
)
func generateStripeStyleKey(keyType, env string, length int) string {
prefix := fmt.Sprintf("%s_%s_", keyType, env)
chars := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
result := make([]byte, length)
for i := range result {
n, _ := rand.Int(rand.Reader, big.NewInt(int64(len(chars))))
result[i] = chars[n.Int64()]
}
return prefix + string(result)
}
Server-Side Key Management
Generating the key is only half the story. Here is how to manage Stripe-style keys in production:
1. Hash Before Storing
import { createHash } from 'node:crypto';
function hashKey(apiKey) {
return createHash('sha256').update(apiKey).digest('hex');
}
// Store hashKey(fullKey) in your database
// Display fullKey to user exactly once
2. Validate Incoming Keys
async function validateApiKey(providedKey) {
const hash = hashKey(providedKey);
const record = await db.apiKeys.findOne({ hash });
if (!record) return { valid: false };
if (record.revoked) return { valid: false, reason: 'revoked' };
if (record.expiresAt < Date.now()) return { valid: false, reason: 'expired' };
return { valid: true, userId: record.userId, permissions: record.permissions };
}
3. Support Key Rotation
Allow multiple active keys per user so they can rotate keys without downtime. Stripe supports this by letting users create multiple keys and revoke old ones independently.
Who Else Uses This Pattern?
Stripe's prefix pattern has been widely adopted:
| Company | Prefix | Format |
|---|---|---|
| OpenAI | sk-proj- | Base62, ~48 chars random |
| Resend | re_ | Base62, ~32 chars random |
| Clerk | sk_live_ | Base62, ~48 chars random |
| GitHub | ghp_, gho_ | Base62, ~36 chars random |
| Supabase | sbp_ | Base62, ~40 chars random |
| Vercel | vc_ | Base62, variable length |
The pattern works because it is simple, self-documenting, and compatible with automated secret detection systems.
Generate Stripe-Style Keys Instantly
Try our free API key generator — select "Prefixed" format, enter your prefix (like sk_live_), and generate keys with the same structure Stripe uses. Everything runs in your browser with crypto.getRandomValues().
Recommended Resources
To build production-grade key management like Stripe, Real-World Cryptography covers the hashing, entropy, and CSPRNG concepts behind it. For securing your API against key-based attacks, The Web Application Hacker's Handbook is essential.