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:

  1. Who is the caller? A machine process (service, script, automation) or a human user acting through an application?
  2. Does the caller need to act as a specific user? If yes, OAuth 2.0 / OIDC. If no, API keys or mTLS may suffice.
  3. 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.
  4. 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.

Scale Your Backend - Need an Experienced Backend Developer?

We provide backend engineers who join your team as contractors to help build, improve, and scale your backend systems.

We focus on clean backend design, clear documentation, and systems that remain reliable as products grow. Our goal is to strengthen your team and deliver backend systems that are easy to operate and maintain.

We work from our own development environments and support teams across US, EU, and APAC timezones. Our workflow emphasizes documentation and asynchronous collaboration to keep development efficient and focused.

  • Production Backend Experience. Experience building and maintaining backend systems, APIs, and databases used in production.
  • Scalable Architecture. Design backend systems that stay reliable as your product and traffic grow.
  • Contractor Friendly. Flexible engagement for short projects, long-term support, or extra help during releases.
  • Focus on Backend Reliability. Improve API performance, database stability, and overall backend reliability.
  • Documentation-Driven Development. Development guided by clear documentation so teams stay aligned and work efficiently.
  • Domain-Driven Design. Design backend systems around real business processes and product needs.

Tell us about your project

Our offices

  • Copenhagen
    1 Carlsberg Gate
    1260, København, Denmark
  • Magelang
    12 Jalan Bligo
    56485, Magelang, Indonesia

More articles

Error Handling in Ruby — Beyond Rescue and Raise

Most Ruby codebases use rescue and raise for everything, which conflates recoverable domain failures with unexpected system errors. Here is a structured approach to error handling that scales past a few controllers.

Read more

Canada's Big Banks Are Winning the Toronto Backend Talent War — Here Is How Startups Fight Back

Toronto's financial institutions have deep pockets, stable careers, and a head start on recruiting. Startups need a different playbook.

Read more

How Seattle Founders Ship Product Without Paying Big Tech Salaries for Backend Work

Your backend roadmap has twelve items on it. Your backend team has one person. The board meeting is in six weeks.

Read more

Why Vancouver's Most Agile Startups Are Winning With Async Remote Backend Contractors

The Vancouver startups shipping the most consistently aren't the ones who cracked local backend hiring. They're the ones who stopped waiting on it.

Read more