FastAPI API Key Authentication with Security and Depends
How to secure a FastAPI app with header-based API key authentication using APIKeyHeader, Security, and Depends.
Introduction
Sometimes you build an API that should only be accessible to clients that present a valid key. FastAPI makes this straightforward with its built-in APIKeyHeader and dependency injection system.
The dependency
# api/core.py
import os
from fastapi import HTTPException, Security
from fastapi.security import APIKeyHeader
from starlette.status import HTTP_403_FORBIDDEN
API_KEY = os.environ['ApiKey']
API_KEY_NAME = os.environ['ApiKeyName']
api_key_header = APIKeyHeader(name=API_KEY_NAME, auto_error=False)
async def get_api_key(
api_key_header: str = Security(api_key_header),
) -> str:
if api_key_header == API_KEY:
return api_key_header
raise HTTPException(HTTP_403_FORBIDDEN, detail='Could not validate credentials')
Walking through what each part does:
API_KEYandAPI_KEY_NAMEare read from environment variables so the secret never lives in code.API_KEY_NAMEis the name of the HTTP header that clients must send (e.g.X-API-Key).APIKeyHeader(name=API_KEY_NAME, auto_error=False)creates a FastAPI security scheme that knows to extract the value from that header on every request. Settingauto_error=Falsemeans FastAPI won’t immediately return a 422 if the header is missing — it instead passesNonetoget_api_keyso we can raise a more meaningful403ourselves.Security(api_key_header)inside the function signature tells FastAPI to resolve the header value and inject it asapi_key_header. UsingSecurityrather thanDependsalso registers the scheme in the OpenAPI spec, so the “Authorize” button appears in the interactive/docs.- The
ifcheck compares the incoming header value against the expected key. A match returns the key (which makes it available to the route handler viaDepends); anything else raises a403 Forbidden.
Protecting an endpoint
Add the get_api_key function as a dependency on any route you want to guard:
# groups.py
from api.core import get_api_key
from fastapi import Depends
@router.get("/{id}/")
async def read_group(id: str, api_key: str = Depends(get_api_key)):
...
Any request without the correct header value will be rejected before your handler runs. Clients that do pass the right key get through and the key value is available as api_key if you need it.
Conclusion
One of the nice things about this pattern is how well it composes. Define get_api_key once and reuse it across as many routes as you need with Depends. If you later want to move to JWT or OAuth2, you only need to swap out that single dependency — your route handlers stay untouched.
Resources
- FastAPI Security Reference — documents
APIKeyHeader,APIKeyCookie, andAPIKeyQuery - FastAPI Dependencies Reference — documents
DependsandSecurity