The actual distinction
An API key identifies a calling system. OAuth lets a user authorise an application to act on their behalf, with a scope and a revocation path.
"Our backend calls the payments API", key. "Users connect their GitHub account to our product". OAuth. The middle ground is rarer than vendors suggest.
When keys are the right answer
Server-to-server, one organisation, no delegation. Simpler to issue, simpler to use, no redirect flow, and a smaller failure surface.
To use them well: issue per integration rather than one key everywhere, so you can revoke one without breaking the rest. Prefix them so they're recognisable in a leak scan (sk_live_, whsec_). Store hashes, not the keys themselves, you can't display an existing key, only issue a new one. Support rotation with an overlap period. Scope them if you can, and log usage per key.
Never put a key in client-side code. A browser bundle or a mobile binary is public, whatever your intentions, and this was among the most frequent things we found scanning 24 AI-built apps. If a browser needs to reach an API, proxy it through your own backend.
When you need OAuth
Whenever the data belongs to a user rather than to the integrating system.
What it buys you: scoped consent, expiry through short-lived access tokens, revocation without rotating a shared credential, and an audit trail of who authorised what.
What it costs: a redirect flow, token refresh handling, and a meaningfully larger set of ways to get it wrong.
Use Authorization Code with PKCE for anything user-facing, including single-page apps. Implicit flow is deprecated. Client Credentials is the machine-to-machine variant, and if that's what you're reaching for, a scoped API key may be simpler.
Common mistakes either way
Keys. One key for everything, no rotation, stored in plaintext, in client-side code, no per-key logging.
OAuth. Unvalidated redirect_uri (an open redirect here leaks tokens), missing PKCE, overbroad scopes requested by default, refresh tokens without rotation, trusting an unverified email claim to link accounts.
Personal access tokens
The middle option. A user-generated, long-lived, scoped credential. Good for CLI tools and CI where a redirect flow doesn't fit. Treat them like keys: hash at rest, scope tightly, expire them, and show the user their active tokens.
Choosing
If a user grants an application access to their data, OAuth. If your system calls another system it owns credentials for, a key. If you're building a public API, start with keys for server-side integrations and add OAuth when partners need user-delegated access, building OAuth before anyone needs it is a large amount of work for no benefit.