Skip to content

API Key Authentication — Simple Token-Based Access for Public APIs

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about API Key Authentication. We cover key concepts, practical examples, and best practices to help you master this topic.

API key authentication uses a static token string that clients include in requests to identify themselves, offering a simple balance of security and usability for public APIs.

What You'll Learn

How API keys work, best practices for generation and transmission, security limitations, and when API keys are appropriate for your API.

Why It Matters

API keys are the most widely used authentication method for public APIs. Services like Google Maps, Stripe, and GitHub all use API keys. They provide simple access control without the complexity of OAuth2 or JWT.

Real-World Use

Google Cloud APIs use API keys for project identification, Stripe uses publishable and secret keys for different environments, and weather APIs use keys for usage tracking.

flowchart LR
    A["Developer"] -->|"Requests API key"| B["API Portal"]
    B -->|"Issues key: sk-abc123"| A
    A -->|"Request + X-API-Key: sk-abc123"| C["API Server"]
    C -->|"Validate key"| D["Key Database"]
    D -->|"Valid"| E["200 OK + Data"]
    D -->|"Invalid"| F["401 Unauthorized"]
    style A fill:#dbeafe,stroke:#2563eb
    style B fill:#fef3c7,stroke:#d97706
    style C fill:#fef3c7,stroke:#d97706
    style E fill:#dcfce7,stroke:#16a34a
    style F fill:#fecaca,stroke:#dc2626

How API Keys Work

  1. Developer registers and requests an API key
  2. Server generates a unique key and stores a hash of it
  3. Developer includes the key in API requests (usually via header)
  4. Server looks up the key, validates it, and identifies the client

Transmission Methods

Method Example Pros Cons
Header X-API-Key: sk-abc123 Not in logs or browser history Requires custom header support
Query Param ?api_key=sk-abc123 Simple for GET requests Exposed in URLs, logs
Bearer Token Authorization: Bearer sk-abc123 Standard header Less semantic than X-API-Key

Code Example: API Key Generation and Validation

import secrets
import hashlib
from flask import Flask, request, jsonify

app = Flask(__name__)

# In production, use a real database
valid_keys = {}

def generate_api_key():
    key = f"sk-{secrets.token_hex(16)}"
    key_hash = hashlib.sha256(key.encode()).hexdigest()
    return key, key_hash

@app.route("/api/keys", methods=["POST"])
def create_key():
    key, key_hash = generate_api_key()
    valid_keys[key_hash] = {"owner": "user-123", "active": True}
    return jsonify({"api_key": key})

def validate_api_key():
    key = request.headers.get("X-API-Key")
    if not key:
        return None
    key_hash = hashlib.sha256(key.encode()).hexdigest()
    info = valid_keys.get(key_hash)
    if info and info["active"]:
        return info
    return None

@app.route("/api/data")
def get_data():
    client = validate_api_key()
    if not client:
        return jsonify({"error": "Invalid API key"}), 401
    return jsonify({"data": "sensitive data", "client": client["owner"]})

if __name__ == "__main__":
    app.run()

Expected output:

$ curl -X POST http://localhost:5000/api/keys
{"api_key":"sk-1a2b3c4d5e6f7g8h9i0j..."}

$ curl -H "X-API-Key: sk-1a2b3c4d5e6f7g8h9i0j..." http://localhost:5000/api/data
{"data":"sensitive data","client":"user-123"}

Code Example: Python Client

import requests

API_KEY = "sk-your-key-here"
headers = {"X-API-Key": API_KEY}

response = requests.get(
    "https://api.example.com/v1/users",
    headers=headers
)

if response.status_code == 200:
    print(response.json())
elif response.status_code == 401:
    print("Invalid or missing API key")

Code Example: Server-Side Rate Limiting with API Keys

from collections import defaultdict
import time

rate_limits = defaultdict(list)
MAX_REQUESTS = 100
WINDOW_SECONDS = 60

def check_rate_limit(api_key):
    now = time.time()
    window_start = now - WINDOW_SECONDS
    requests = [t for t in rate_limits[api_key] if t > window_start]
    rate_limits[api_key] = requests
    if len(requests) >= MAX_REQUESTS:
        return False
    rate_limits[api_key].append(now)
    return True

@app.route("/api/data")
def get_data_with_rate_limit():
    key = request.headers.get("X-API-Key")
    if not key:
        return {"error": "Missing API key"}, 401
    if not check_rate_limit(key):
        return {"error": "Rate limit exceeded"}, 429
    return {"data": "success"}

Common Mistakes

1. Exposing API Keys in Client-Side Code

API keys in mobile apps or JavaScript frontends can be extracted. Use a backend proxy or API key restrictions (HTTP referrers, IP addresses).

2. Storing Keys in Plain Text

Store a hash of the API key, not the key itself. If your database is breached, hashed keys cannot be used to make API calls.

3. Not Allowing Key Rotation

Users should be able to generate new keys and revoke old ones. Without rotation, a compromised key is valid forever.

4. Using API Keys for User Authentication

API keys identify applications, not users. They are not suitable for user-specific access control (e.g., "show my data"). Use JWT or OAuth2 for user auth.

5. Putting Keys in URLs

?api_key=abc123 exposes the key in server logs, browser history, and referrer headers. Always use headers.

Practice Questions

  1. Why should API keys be hashed before storage?
  2. What is the difference between a secret key and a publishable key?
  3. Why should API keys be transmitted via headers rather than query parameters?
  4. How can you restrict API key usage to specific domains?
  5. Can API keys identify individual users?

Answers:

  1. If the database is breached, hashed keys cannot be used directly. The server hashes the incoming key and compares hashes.
  2. Publishable keys (frontend) have limited permissions; secret keys (backend) have full access. This principle of Least Privilege limits damage if a key is exposed.
  3. Headers are less likely to appear in server logs, browser history, or referrer headers. URLs are visible in many places.
  4. During key creation, store allowed referrer domains or IP ranges. The server validates the request's Origin header against these restrictions.
  5. No — API keys identify applications. For user-specific identity, use JWT or OAuth2 tokens.

Challenge: Build an API key management system with key generation, hashed storage, key rotation, deactivation, and usage tracking. Include rate limits per key.

FAQ

What is the difference between an API key and a JWT?

An API key is a static token that identifies the application. A JWT is a signed token that contains user identity and claims. JWTs are more flexible but require more infrastructure.

Can API keys expire?

Yes. While traditionally static, modern APIs support expiring keys (e.g., 30-day, 90-day). The server checks expiry during validation.

How do I revoke an API key?

Remove it from the valid keys database or mark it as inactive. The server checks the active flag on each request.

Should I use UUIDs or random strings for API keys?

Use cryptographically random strings (e.g., secrets.token_hex). UUIDs are predictable. Prefix keys (e.g., sk-) to identify the key type.

How many API keys should a client have?

At least two: one for development and one for production. Each environment should have separate keys with appropriate permissions.

Mini Project

Build a Flask API key management service with endpoints for creating, listing, revoking, and rotating keys. Store hashed keys in a dictionary (simulating a database) with rate limiting per key.

What's Next

Now that you understand API keys, learn about Token-Based Authentication which provides more flexibility with dynamic tokens.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro