API Keys vs OAuth Tokens: What's the Difference
API keys are static secrets tied to an app; OAuth tokens are short-lived, scoped, and tied to a specific user's consent. Here's when to use each.
API keys and OAuth tokens both let a client prove it’s allowed to call an API, but they answer different questions. An API key answers “which application is this,” typically as a single long-lived secret the developer generated once. An OAuth token answers “which user authorized this action, for what, and until when” — it’s issued through a defined flow, tied to a specific grant of permission, and expected to expire. Picking the wrong one for a given use case is a common source of avoidable security exposure.
What an API key actually is
An API key is usually just a long random string, issued by a service to a developer or application, sent with every request — commonly in a header like X-API-Key or as a query parameter (the latter is worse, since it ends up in server logs and browser history). The service checks it against a stored value and, if it matches, treats the request as coming from whatever account or project the key belongs to.
That’s the whole model: no user context, no expiry by default, no built-in scoping beyond whatever the issuing service bothers to add on top. An API key that leaks is valid until someone notices and revokes it — there’s no natural expiration working in your favor.
What an OAuth token actually is
An OAuth 2.0 access token is the output of an authorization flow where a user explicitly grants an application permission to act on their behalf, scoped to specific permissions (“read your calendar,” not “do anything as you”), and issued with a defined lifetime — commonly minutes to hours, refreshed via a separate longer-lived refresh token rather than reused indefinitely. The token itself is often (though not always) a JWT, which lets a resource server validate it without a database lookup, but the token format isn’t the defining feature — the flow and the scoping are.
Because the grant is tied to a specific user’s consent, revocation is also more granular: a user can revoke one application’s access without affecting others, and a compromised token’s blast radius is limited both by its scope and its short lifetime.
Side by side
| API key | OAuth token | |
|---|---|---|
| Identifies | An application or project | A specific user’s granted permissions |
| Issued via | Developer dashboard, once | An authorization flow, per user consent |
| Typical lifetime | Indefinite until manually revoked | Minutes to hours, refreshed separately |
| Scoping | Often none, or coarse | Fine-grained, defined at grant time |
| Revocation | All-or-nothing for that key | Per-user, per-grant |
| Best fit | Server-to-server, no per-user context | Acting on behalf of a specific user |
When each one actually fits
API keys make sense when there’s no individual user to authenticate — a backend service calling a third-party API on behalf of your whole application, a webhook signature scheme, or a low-stakes public API with generous rate limits. The simplicity is the point: no flow to implement, no token refresh logic, just a secret sent with every request.
OAuth tokens are the right call whenever “acting on behalf of a specific user” is actually the requirement — a third-party app that needs to read a user’s files, post to their account, or access anything they didn’t explicitly and individually consent to sharing. Using an API key for this would mean either sharing one key across every user (no per-user revocation, no meaningful scoping) or minting one static key per user, which just reinvents a weaker, unscoped, non-expiring version of what OAuth already does properly.
Where they get misused
The most common mistake is treating an API key as if it carries the same security properties as an OAuth token — embedding a “user-level” API key in a mobile app or single-page app’s client-side code, where anyone can extract it, then relying on it to gate access to per-user data. A static, non-expiring, unscoped secret shipped to every client is a bad place to put anything sensitive, regardless of what it’s called. If access needs to be tied to an individual, revocable, and time-bound, that’s a sign OAuth (or at minimum, short-lived server-issued tokens) is the right tool, not a key.
The reverse mistake is rarer but happens: implementing a full OAuth flow for pure server-to-server integrations with no user in the loop at all, where the added complexity (redirect flows, consent screens, refresh logic) buys nothing over a plain API key managed through normal secrets handling.
Rate limiting and scoping still apply either way
Regardless of which mechanism you use, rate limiting and access control decisions downstream of authentication are separate concerns — an OAuth token proves who’s asking and what they’ve consented to, but enforcing what they’re actually allowed to do once authenticated is the job of an authorization model like RBAC or ABAC. Neither API keys nor OAuth tokens replace that layer; they just answer “who or what is making this request.”
The takeaway
API keys are simple, static, application-level secrets with no natural expiry — fine for server-to-server calls with no individual user involved. OAuth tokens are scoped, time-limited, and tied to a specific user’s consent, which makes them the right choice whenever an application needs to act on someone’s behalf rather than just identify itself. The failure mode to avoid is using a static key where per-user scoping and expiry actually matter — that’s usually a sign the integration needs OAuth, not a stronger key.
Tagged
Keep reading
Chisato · · 5 min read What Is Session Fixation?
Session fixation tricks a victim into using an attacker-known session ID, so logging in hands the attacker an authenticated session too.
Chisato · · 4 min read What Is IDOR? Insecure Direct Object References Explained
IDOR is an access control flaw where an app trusts a user-supplied ID to fetch a record without checking the requester actually owns it.
Chisato · · 4 min read What Is a Replay Attack?
A replay attack resends a captured, valid message to trick a system into repeating an action — and why timestamps, nonces, and signatures stop it.