API Keys and Basic Authentication

7 min read

Almost every API you'll test in real work is gated by authentication of some kind. The simplest two — API keys and basic authentication — are still everywhere despite being the oldest schemes around. They show up in internal tools, third-party services, and "quick prototype" endpoints that ended up in production. Understanding how they work, how to test them properly, and where they fall down is foundational to everything that follows in this chapter.

API keys

An API key is a long random string that identifies the calling application. Pass it with the request and the server knows who you are.

Two ways APIs accept keys:

  • In a header (preferred):
    X-API-Key: abc123def456ghi789
    
    Some APIs use the standard Authorization header instead:
    Authorization: ApiKey abc123def456ghi789
    
  • As a query parameter (less safe):
    https://api.example.com/data?api_key=abc123def456ghi789
    

Why the query-string variant is worse: URLs end up in proxy logs, browser history, server access logs, and analytics tools. A leaked URL becomes a leaked credential. Headers don't share that exposure surface.

API keys identify the calling app, not a specific user. They're great for service-to-service communication, public APIs (Stripe, OpenAI, Twilio all use them), and rate-limiting per consumer. They're not great for user-level permissions — for that you want OAuth or JWTs (next two lessons).

Basic authentication

Basic auth is even older than API keys. It sends the username and password on every request, encoded (not encrypted!) as Base64:

Authorization: Basic YWxpY2U6cGFzc3dvcmQ=

That string is just base64("alice:password"). Decode it and you have the password.

curl has built-in support:

curl -u alice:password https://api.example.com/users

Three things to remember about basic auth:

  • Never use it without HTTPS. Base64 is reversible; over plain HTTP, the credential is effectively in the clear.
  • It sends credentials with every request. That's more exposure than a token-based scheme.
  • It's still common in internal tools, CI integrations, and legacy enterprise APIs. You'll meet it.

How API key auth flows

Step 1 of 5

Client adds key

The client sets an Authorization or X-API-Key header on the outgoing request, pulling the key from environment variables or a secret manager.

Five steps, the same shape every time. Your tests must cover each step's failure mode.

A test matrix for API key auth

For any endpoint protected by an API key, you should have at least these tests:

ScenarioSetupExpected
Valid keyX-API-Key: <known-good>200 with expected body
No keyheader omitted401 Unauthorized
Empty keyX-API-Key:401
Invalid keyX-API-Key: garbage123401
Revoked keykey disabled in admin panel401
Wrong header namekey in X-Api-Key instead of X-API-Key (if case-sensitive)401
Insufficient scoperead-only key on a write endpoint403 Forbidden
Key in query when only header allowed?api_key=... instead of header401

Eight scenarios. None overlap; each catches a different bug.

A subtle one worth flagging: 401 vs 403. A missing or malformed key is a 401 (we don't know who you are). A valid key that lacks permission is a 403 (we know who you are, but you can't do this). Your tests should expect the right one.

Testing basic auth

The same matrix shape, adjusted for credentials:

ScenarioSetupExpected
Valid credentials-u alice:password200
Wrong password-u alice:wrongpass401
Non-existent user-u nobody:anything401
Empty username-u :password401
Empty password-u alice:401
No Authorization headerheader omitted401

Notice that "non-existent user" and "wrong password" should return the same 401. Returning a different status (or a different error message) tells an attacker which usernames exist — a textbook user-enumeration vulnerability.

Storing keys safely in test code

The cardinal sin of API testing: hardcoding credentials in test files and committing them to git.

# DON'T
api_key = "sk_live_abc123def456ghi789"
 
# DO
api_key = os.environ["API_KEY"]

A few practical patterns:

  • Keep secrets in environment variables, locally and in CI (.env files for dev, GitHub Actions secrets for CI).
  • Never log the key value. Log only "key present: yes/no" or the first few characters.
  • Rotate test keys regularly, and have the ability to revoke a leaked key fast.
  • If a key is committed by accident, revoke it immediately even if you removed it in a follow-up commit. Git history is forever.

⚠️ Common mistakes

  • Confusing 401 and 403. Permission tests should expect 403; missing-credential tests should expect 401. Conflating the two hides bugs in either direction.
  • Putting keys in URLs because it's "easier." Query-param keys end up in logs, analytics, and screen-shared URL bars. Use headers.
  • Treating Base64 as encryption. It's not. Anyone who sees a basic-auth header can decode it instantly. HTTPS is the only thing keeping it private on the wire.

🎯 Practice task

Test API key auth on a real public API. 20 minutes.

  1. Sign up for a free OpenWeatherMap or The Cat API account and get an API key. (Both have generous free tiers and require keys.)
  2. Make an authenticated GET with curl using the key in the documented header. Confirm 200.
  3. Repeat the request with no key. Note the status code and error message.
  4. Repeat with a deliberately bad key (X-API-Key: not-a-real-key). Compare to the no-key response.
  5. Try the same key as a query parameter (most APIs accept either form). Note where the key now appears in curl -v output (URL line vs header line).
  6. Browse the API Testing Concepts cheat sheet for the section on auth — confirm your test matrix covers each scenario listed.
  7. Stretch: write a short script (Python with requests, or any language you know) that reads the key from os.environ, makes the call, and asserts on the status. Run it once with the env var set and once without — confirm the env-var version doesn't leak the key into source.

You've covered the simplest auth schemes. The next lesson tackles the modern standard for delegated authorisation: OAuth 2.0.

// tip to track lessons you complete and pick up where you left off across devices.