API Key vs JWT
Simple and revocable vs stateless and self-contained — understanding the trade-offs.
For a broader overview of all API authentication options, see the API Authentication Methods guide.
- •You need instant revocation (delete from DB, done)
- •Server-to-server calls within a trusted boundary
- •Simple public API with per-client rate limiting
- •You want to avoid token size or signing complexity
- •You need stateless verification (no DB lookup per request)
- •Multiple services need to verify the same token
- •You want to embed user context (ID, roles, tenant) in the token
- •Short-lived access with built-in expiry is acceptable
Many systems use both: API keys for M2M calls, JWTs for user sessions.
Core Comparison
| Aspect | API Key | JWT |
|---|---|---|
| Structure | Opaque random string | Base64URL-encoded header.payload.signature |
| Verification | Database lookup | Cryptographic signature check (no DB) |
| Carries data? | No — server looks up context from DB | Yes — claims in payload (sub, roles, exp…) |
| Expiry | Manual (no built-in) | Built-in exp claim |
| Revocation | Instant (delete DB record) | Hard — requires blocklist or waiting for expiry |
| Token size | Small (32–64 chars) | Larger (150–500+ chars depending on claims) |
| Stateless? | No (always needs DB) | Yes (signature encodes trust) |
| Readable payload | No (opaque) | Yes (Base64URL-decoded, not encrypted by default) |
| Implementation complexity | Low | Medium (signing, verification, key rotation) |
| Primary use case | Server-to-server, public APIs | User sessions, microservices auth propagation |
API Keys Explained
An API key is a long random string — typically 128–256 bits of entropy — that acts as a shared secret between the client and server. When the client makes a request, it includes the key. The server looks up the key in its database to identify the caller, check permissions, and apply rate limits.
API keys are opaque: they carry no information themselves. Everything the server needs to know — who this caller is, what they're allowed to do, whether the key is still valid — lives in the database record. This makes revocation trivial: just mark the key as inactive or delete it.
The downside is latency. Every request requires a database roundtrip to validate the key. For high-throughput APIs, this adds up. Caching the validated key in memory or Redis (with a short TTL) is the standard mitigation.
Security note: HTTPS is required
API keys provide no protection against a man-in-the-middle attack if sent over unencrypted HTTP. Always use HTTPS. Never put keys in URL query parameters — they appear in server logs and browser history. Send them in the Authorization: Bearer header.
JWTs Explained
A JWT (JSON Web Token) is a compact, self-contained token with three Base64URL-encoded sections joined by dots: a header (algorithm and token type), a payload (claims), and a signature. The server issues the token by signing the header and payload with its private key (RS256/ES256) or shared secret (HS256).
When a service receives a JWT, it verifies the signature cryptographically. If valid, it trusts the claims in the payload — the user ID, roles, expiration time, etc. — without querying a database. This is the key advantage: horizontal scale with no shared state.
The payload is Base64URL-encoded, not encrypted. Anyone who has the token can decode and read the claims. If you need to keep claim data confidential, use JWE (JSON Web Encryption), though this is less common and more complex.
The revocation problem
Because verification is stateless, you can't "un-issue" a JWT before it expires without re-introducing state. Short expiry times (15–60 minutes) are the usual answer, combined with refresh tokens. For scenarios where immediate revocation is critical (user banned, password changed), you may need a distributed blocklist.
Real-World Patterns
Services like Stripe, SendGrid, and Twilio use API keys for their external APIs. The key identifies the account, is easy for developers to manage (create, rotate, revoke in a dashboard), and works from any server language without a JWT library.
API keys make sense here because there's no "user session" — it's your server calling their API. Revocation and per-key rate limiting are more important than avoiding DB lookups.
React/Next.js apps and mobile apps typically use JWTs (or opaque tokens from an OAuth server). The user logs in, receives a short-lived access token (JWT) and a refresh token. The access token is passed to the API on every request; the API verifies the signature without hitting a DB.
This pattern scales well and keeps the API stateless. The refresh token is stored securely (HTTP-only cookie) and used to get new access tokens silently.
In a microservices system, the API gateway authenticates incoming requests and issues a short-lived JWT (or forwards the user's JWT). Downstream services verify the JWT signature locally — they extract the user ID, roles, and tenant from the payload without calling the auth service on every request.
This reduces coupling and the auth service doesn't become a bottleneck. Service-to-service calls that don't involve a user context may still use API keys or mTLS.
Many platforms use API keys to authenticate the machine/application (identifying which service is calling) and JWTs to authenticate the user (identifying which user's data is being accessed). A request might carry both: an X-API-Key header for the application and a Authorization: Bearer <jwt> for the user.
This lets you rate limit and scope permissions at the application level (API key) while propagating user identity and roles via JWT — without coupling the two concerns.
Decision Checklist
- →Need to revoke instantly, no grace period—API Key
- →Multiple services verify the same token—JWT
- →Server-to-server, no user involved—API Key
- →User session with embedded roles and expiry—JWT
- →High-throughput API, can't afford DB on every request—JWT (or API Key + Redis cache)
- →Public-facing API with per-customer rate limiting—API Key
- →Need tamper-proof requests (not just auth)—HMAC Request Signing