API Keys Are Not the Same as Authentication. Here Is the Difference.
by Eric Hanson, Backend Developer at Clean Systems Consulting
What an API key actually is
An API key is a shared secret that identifies a client application or account. When a request arrives with a valid API key, the server knows which account is making the request. That is the entirety of what an API key gives you — identification and, by association, authorization based on that account's permissions.
What it does not give you: proof of who is operating that account right now, session management, token expiry, user-level identity, or the ability to act on behalf of a specific human user with their explicit consent.
For many server-to-server integrations, identification is exactly what you need and an API key is the right tool. Where it breaks down is when you start using an API key to model concepts it was not designed for.
The failure modes
API keys as user authentication: A mobile app stores an API key in the app bundle and uses it to authenticate requests. Every installation of the app shares the same key. This is not user authentication — it is application identification. If any one user extracts the key (trivial on an unobfuscated APK), they have the same key every other user has.
API keys as session tokens: A key that does not expire and cannot be revoked without disrupting all callers is not a session. When a developer's laptop is stolen or a key is leaked in a public GitHub commit, you cannot selectively invalidate access for that context without rotating the key for everyone sharing it.
API keys as proof of authorization: API keys issued at the account level carry all of that account's permissions by default. There is no scope — either you have the key or you do not. If a service only needs read access to billing data but has your API key, it also has write access to everything else the account can do.
Where API keys belong
API keys are appropriate for:
- Server-to-server integrations where the key is stored in environment variables or a secrets manager, never in client-side code
- Internal service mesh authentication where you want a simple, auditable way for service A to identify itself to service B
- Metering and rate limiting at the application level without user-level granularity
The common thread: the key is a secret held by a system, not a person, in a controlled environment.
What authentication actually requires
Authentication — verifying that this request comes from who it claims to come from — requires mechanisms designed for that purpose:
OAuth 2.0 handles delegated authorization: a user explicitly authorizes a third-party application to access specific resources on their behalf. The user's identity is verified by the authorization server. The application receives an access token with specific scopes. Neither the application's identity nor the user's password is shared with the resource server.
JWT (JSON Web Tokens) carry verifiable claims — the server that issued the token signed it, so any party with the public key can verify its authenticity and expiry without a database lookup. JWTs can encode user identity, roles, and scopes.
mTLS (mutual TLS) authenticates both parties via certificates. Each service presents a certificate issued by a trusted CA. This is strong for service-to-service authentication in zero-trust networks and does not rely on shared secrets at all.
OIDC (OpenID Connect), layered on OAuth 2.0, adds user identity on top of delegated authorization. The ID token contains verified claims about the user's identity.
A practical decision tree
When securing an API endpoint, ask:
- Who is the caller? A machine process (service, script, automation) or a human user acting through an application?
- Does the caller need to act as a specific user? If yes, OAuth 2.0 / OIDC. If no, API keys or mTLS may suffice.
- Does access need to be scoped? A caller that only needs read access should not have write access. JWTs with scopes or OAuth scopes handle this. API keys without scope support do not.
- Does access need to expire and be revocable per session? Short-lived JWTs with refresh tokens. API keys that do not rotate are not the right tool.
The hybrid pattern
Most mature APIs use both: API keys for application-level identification and OAuth tokens for user-level authorization. The API key identifies which application is making the request. The OAuth token identifies which user the application is acting on behalf of.
Authorization: Bearer <oauth_access_token>
X-API-Key: <application_api_key>
This gives you application-level rate limiting and metering (via the API key) alongside user-level access control (via the token). You know which application made the request and which user authorized it — independently auditable, independently revocable.
Do not conflate these two mechanisms. Design for the security model you actually need, not the one that is simplest to implement.