Back to Blog
Python

Python httpx Authentication: Bearer Tokens and API Keys

python httpx authentication bearer token and api keys: Practical guide to authenticating httpx requests with bearer tokens and API keys, including reusable Auth subcla...

httpxpython authenticationbearer tokenapi keyhttp client
Illustration of an HTTP request with a highlighted bearer token in its Authorization header being sent to a secure API endpoint.

python httpx authentication bearer token and api keys requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.

When you need python httpx authentication for bearer tokens or API keys, the implementation comes down to setting the right headers on your requests. Both schemes are common in REST APIs, but they differ in how the server expects the credential and how you should manage it in your Python code.

Sending a Bearer Token with httpx

The standard way to send a bearer token is through the Authorization header with the Bearer scheme. httpx does not have a dedicated bearer_token parameter, so you set the header explicitly:

import httpx token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." headers = {"Authorization": f"Bearer {token}"} response = httpx.get("https://api.example.com/me", headers=headers)

This works for both httpx.get() and httpx.Client() calls. When you use a Client, you can set the header once and reuse it across requests:

with httpx.Client(headers={"Authorization": f"Bearer {token}"}) as client: response = client.get("https://api.example.com/me") other = client.get("https://api.example.com/orgs")

The Authorization header is the HTTP standard for bearer tokens, defined by RFC 6750. Most OAuth2-protected APIs expect this exact format: the literal word Bearer, a single space, then the token value. Do not add quotes around the token; the header value is the token itself. If you omit the Bearer prefix and send only the token, many servers reject the request with a 401 even though the token is valid.

Sending API Keys with httpx

API keys are less standardized than bearer tokens. Some APIs expect them in a custom header, others in a query parameter, and a few still use cookie-based delivery. You need to check the API documentation to know which one applies.

API Key in a Header

A common convention is X-API-Key, but many services use their own header name such as X-Auth-Token or X-Api-Key:

import httpx api_key = "sk_live_4f8a2c..." headers = {"X-API-Key": api_key} response = httpx.get("https://api.example.com/data", headers=headers)

API Key as a Query Parameter

Some APIs accept the key as a query parameter, typically named api_key or key:

import httpx params = {"api_key": "sk_live_4f8a2c..."} response = httpx.get("https://api.example.com/data", params=params)

Query-parameter keys appear in server access logs, proxy logs, and browser history. If the API supports header-based keys, prefer that over the query parameter form. The header keeps the secret out of URLs, which is important because URLs are frequently logged and cached.

Using httpx.Auth for Reusable Authentication

If you make many authenticated calls across different parts of your codebase, setting headers manually on every request becomes repetitive and error-prone. httpx provides an Auth base class that lets you encapsulate the authentication logic and attach it to a Client.

import httpx class BearerAuth(httpx.Auth): def __init__(self, token: str): self.token = token def auth_flow(self, request: httpx.Request): request.headers["Authorization"] = f"Bearer {self.token}" yield request

You then pass an instance to the client:

with httpx.Client(auth=BearerAuth(token)) as client: response = client.get("https://api.example.com/me")

The auth_flow method receives the outgoing request before it is sent. You modify the headers and yield the request back to httpx. This pattern is particularly useful when you need to refresh tokens: you can intercept a 401 response inside auth_flow, obtain a new token, modify the request, and yield it again.

import httpx class RefreshingAuth(httpx.Auth): def __init__(self, get_token): self.get_token = get_token def auth_flow(self, request: httpx.Request): request.headers["Authorization"] = f"Bearer {self.get_token()}" response = yield request if response.status_code == 401: # In a real implementation, refresh the token here. request.headers["Authorization"] = f"Bearer {self.get_token()}" response = yield request return response

Note that auth_flow is a generator. The first yield sends the request and receives the response back. If the response indicates an expired token, you can retry with a fresh one. This keeps token-refresh logic inside the authentication layer rather than scattered across call sites.

Handling Authentication Failures

An expired bearer token or an invalid API key typically produces a 401 Unauthorized response. Some APIs return 403 Forbidden when the key is valid but lacks permission for the resource. httpx does not raise an exception for these status codes by default; you must check response.status_code or use response.raise_for_status().

response = client.get("https://api.example.com/me") if response.status_code == 401: print("Token expired or invalid") elif response.status_code == 403: print("Authenticated but not authorized") else: response.raise_for_status()

When you use raise_for_status(), a 401 or 403 raises httpx.HTTPStatusError. You can catch it and inspect the response for the exact status code. Do not assume that a 401 always means the token is expired; it can also mean the token was malformed, the header was missing, or the token was revoked.

Security Considerations for Tokens and Keys

Bearer tokens and API keys are credentials. Treat them with the same care as passwords.

Never hardcode tokens in source files. Use environment variables, a secrets manager, or a configuration file that is excluded from version control:

import os import httpx token = os.environ["API_TOKEN"] headers = {"Authorization": f"Bearer {token}"}

Be aware that httpx logs request URLs by default when you enable logging. If you send an API key as a query parameter, that key will appear in the log output. Header-based credentials are not logged by default, but you should still avoid putting them in exception messages or print statements.

TLS is the transport layer that protects these credentials in transit. httpx verifies certificates by default, so a plain https:// URL is sufficient for most cases. Do not disable certificate verification in production; verify=False should only be used in controlled test environments where you understand the risk.

If you use a Client with a long lifetime, consider rotating tokens before they expire. The Auth subclass pattern makes this easier because the token source is centralized. For API keys that are long-lived, store them in a secrets manager and load them at startup rather than embedding them in code.

Choosing Between Bearer Tokens and API Keys

The choice is usually dictated by the API you are calling, not by httpx. Both are supported equally well by the library; the difference is in how the server expects the credential.

SchemeTypical headerLifetimeCommon use case
Bearer tokenAuthorization: Bearer <token>Short-livedOAuth2, user sessions
API keyX-API-Key: <key>Long-livedService-to-service calls

Bearer tokens are typically issued by an OAuth2 flow and expire after minutes or hours. They are often scoped to a specific user or permission set. API keys are usually static identifiers for a service account and remain valid until revoked.

When you control the server, prefer bearer tokens for user-facing endpoints because they support expiration and revocation. Use API keys for internal services where a long-lived credential is acceptable and rotation is handled operationally.

In httpx, the implementation cost is identical for both. The Auth subclass pattern works for either scheme, and the same client configuration applies. The operational difference — token refresh, key rotation, revocation — is what should drive your choice.

python httpx authentication bearer token and api keys: Pract | RYUSLOG DEV