The problem: every project reinvents API auth
I've built 77+ API integrations. Each time, I used to write auth handling from scratch. Token refresh? Copy-paste. API key rotation? Rewrite. OAuth flow? Start over.
Then I standardized on ONE pattern. Now I copy it into every project and it just works.
The Universal Auth Pattern
import httpx
import time
from dataclasses import dataclass
from typing import Optional
@dataclass
class AuthConfig:
"""Works with API keys, Bearer tokens, and OAuth."""
api_key: Optional[str] = None
bearer_token: Optional[str] = None
oauth_client_id: Optional[str] = None
oauth_client_secret: Optional[str] = None
oauth_token_url: Optional[str] = None
_cached_token: Optional[str] = None
_token_expires: float = 0
def get_headers(self) -> dict:
if self.api_key:
return {'X-API-Key': self.api_key}
if self.bearer_token:
return {'Authorization': f'Bearer {self.bearer_token}'}
if self.oauth_client_id:
token = self._get_oauth_token()
return {'Authorization': f'Bearer {token}'}
return {}
def _get_oauth_token(self) -> str:
if self._cached_token and time.time() < self._token_expires:
return self._cached_token
resp = httpx.post(self.oauth_token_url, data={
'grant_type': 'client_credentials',
'client_id': self.oauth_client_id,
'client_secret': self.oauth_client_secret,
})
data = resp.json()
self._cached_token = data['access_token']
self._token_expires = time.time() + data.get('expires_in', 3600) - 60
return self._cached_token
Usage: 3 lines to auth with any API
# API Key
auth = AuthConfig(api_key='sk-1234567890')
# Bearer Token
auth = AuthConfig(bearer_token='ghp_xxxxxxxxxxxx')
# OAuth2
auth = AuthConfig(
oauth_client_id='your-id',
oauth_client_secret='your-secret',
oauth_token_url='https://auth.example.com/oauth/token'
)
# Then use it:
client = httpx.Client(headers=auth.get_headers())
resp = client.get('https://api.example.com/data')
Why this pattern wins
- One interface for all auth types. Swap API key for OAuth? Change one line.
- Token caching built in. No more requesting a new token per request.
-
Testable. Mock
AuthConfigin tests instead of mocking HTTP calls. - Copy-pasteable. Drop it into any project, works immediately.
I use this across all 77 of my Apify scrapers. Same pattern, every time.
How do you handle API auth? Do you have a go-to pattern or do you reinvent it each time?
I write about Python patterns and automation. Follow for more.
More from me: 10 Dev Tools I Use Daily | 77 Scrapers on a Schedule | 150+ Free APIs
Top comments (0)