How to Make HTTP Requests with Bearer Token Authentication in Python
Many modern APIs use Bearer Token authentication (often JWTs - JSON Web Tokens) to authorize requests. This involves sending an Authorization
header with the value Bearer <your_token>
. Python's popular requests
library and the built-in urllib
module both provide ways to include this header in your HTTP requests.
This guide demonstrates how to send Bearer tokens correctly using requests
(directly and via a custom auth class) and urllib.request
.
Understanding Bearer Token Authentication
Bearer authentication is an HTTP authentication scheme involving security tokens called bearer tokens. The client sends this token in the Authorization
header of the HTTP request to access protected resources. The server receiving the request verifies the token to grant access. The standard format for the header is:
Authorization: Bearer <token>
Where <token>
is the actual token string (e.g., a long JWT).
Method 1: Using requests
with the headers
Argument (Recommended)
The requests
library (install via pip install requests
) provides the most straightforward way to add custom headers, including the Authorization
header.
Setting the Authorization
Header
You create a Python dictionary representing the headers and pass it to the headers
parameter of the request function (requests.get
, requests.post
, etc.).
Example: POST Request with Bearer Token
import requests
import json # Often needed for request/response body
# --- Configuration ---
API_ENDPOINT = 'https://jsonplaceholder.typicode.com/posts' # Example endpoint
# Replace with your actual Bearer token
YOUR_BEARER_TOKEN = 'YOUR_ACTUAL_SECRET_TOKEN_HERE'
# Data to send (for POST request)
post_data = {'title': 'foo', 'body': 'bar', 'userId': 1}
# --- Create Headers ---
# ✅ Define the headers dictionary with the Authorization header
request_headers = {
'Authorization': f'Bearer {YOUR_BEARER_TOKEN}',
'Content-Type': 'application/json' # Often needed for POST data
}
# --- Make the Request ---
try:
print(f"Sending POST request to {API_ENDPOINT}...")
response = requests.post(
API_ENDPOINT,
headers=request_headers,
json=post_data, # Use json parameter for dict data -> auto-sets Content-Type
# data=json.dumps(post_data), # Alternative if sending raw JSON string
timeout=10 # Good practice to set a timeout
)
response.raise_for_status() # Raise an exception for bad status codes (4xx or 5xx)
# --- Process Response ---
print(f"Status Code: {response.status_code}") # e.g., 201 Created
# Decode the JSON response body
response_data = response.json()
print("Response JSON:")
print(response_data)
except requests.exceptions.RequestException as e:
print(f"An error occurred: {e}")
Example Output (if token was valid and endpoint worked):
Sending POST request to https://jsonplaceholder.typicode.com/posts...
Status Code: 201
Response JSON:
{'title': 'foo', 'body': 'bar', 'userId': 1, 'id': 101}
- We create the
request_headers
dictionary. - The
'Authorization'
key's value starts withBearer
followed by the token. - This dictionary is passed via the
headers=
argument torequests.post()
. - The same
headers
dictionary can be used withrequests.get()
,requests.put()
, etc.
Formatting the Header Value
You can construct the Authorization
header string in several ways:
YOUR_BEARER_TOKEN = 'YOUR_ACTUAL_SECRET_TOKEN_HERE'
# Using f-string (Recommended)
header_fstring = f'Bearer {YOUR_BEARER_TOKEN}'
print(f"f-string: '{header_fstring}'")
# Using string concatenation (+)
header_concat = 'Bearer ' + YOUR_BEARER_TOKEN
print(f"Concatenation: '{header_concat}'")
# Using str.format()
header_format = 'Bearer {}'.format(YOUR_BEARER_TOKEN)
print(f"str.format(): '{header_format}'")
# All produce: 'Bearer YOUR_ACTUAL_SECRET_TOKEN_HERE'
f-strings are generally the most readable option in modern Python.
Method 2: Using requests
with a Custom AuthBase
Class
For reusable authentication logic, especially if handling token refresh or complex schemes, requests
allows you to create custom authentication classes inheriting from requests.auth.AuthBase
.
Creating a BearerAuth
Class
import requests
class BearerAuth(requests.auth.AuthBase):
"""Custom Auth class for Bearer Token."""
def __init__(self, token):
self.token = token
# This method is called by requests to modify the request object
def __call__(self, request_obj):
# ✅ Add the Authorization header to the request
request_obj.headers['Authorization'] = f'Bearer {self.token}'
return request_obj # Return the modified request
This class takes the token during initialization and adds the correct header when attached to a request via the auth
parameter.
Example: Using the Custom Class for GET/POST
# --- Reusing the BearerAuth class from above ---
import requests
# --- Configuration ---
API_ENDPOINT_USERS = 'https://jsonplaceholder.typicode.com/users/1' # GET example
API_ENDPOINT_POSTS = 'https://jsonplaceholder.typicode.com/posts' # POST example
YOUR_BEARER_TOKEN = 'YOUR_ACTUAL_SECRET_TOKEN_HERE'
post_data = {'title': 'new post', 'body': 'content', 'userId': 5}
# --- Create Auth Instance ---
bearer_auth = BearerAuth(YOUR_BEARER_TOKEN)
# --- Make GET Request ---
try:
print(f"\nSending GET request to {API_ENDPOINT_USERS}...")
# ✅ Pass the auth instance to the 'auth' parameter
response_get = requests.get(API_ENDPOINT_USERS, auth=bearer_auth, timeout=10)
response_get.raise_for_status()
print(f"GET Status Code: {response_get.status_code}")
# print(f"GET Response: {response_get.json()}")
except requests.exceptions.RequestException as e:
print(f"GET Error: {e}")
# --- Make POST Request ---
try:
print(f"\nSending POST request to {API_ENDPOINT_POSTS}...")
# ✅ Pass the same auth instance to the 'auth' parameter
response_post = requests.post(API_ENDPOINT_POSTS, json=post_data, auth=bearer_auth, timeout=10)
response_post.raise_for_status()
print(f"POST Status Code: {response_post.status_code}")
print(f"POST Response: {response_post.json()}")
except requests.exceptions.RequestException as e:
print(f"POST Error: {e}")
Using the auth
parameter keeps the header logic separate from the main request call, which can be cleaner for complex authentication.
Method 3: Using urllib.request
Python's built-in urllib
module can also send custom headers, though its API is generally considered less user-friendly than requests
.
Adding Headers with urllib.request.Request
You need to create a Request
object and add headers to it before opening the URL.
Example: POST Request with urllib
import urllib.request
import json # Needed to encode data and potentially decode response
# --- Configuration ---
API_ENDPOINT = 'https://jsonplaceholder.typicode.com/posts'
YOUR_BEARER_TOKEN = 'YOUR_ACTUAL_SECRET_TOKEN_HERE'
post_data_dict = {'title': 'urllib post', 'body': 'test', 'userId': 2}
# --- Prepare Data and Headers ---
# ✅ Encode the POST data to bytes
encoded_data = json.dumps(post_data_dict).encode('utf-8')
# ✅ Create headers dictionary
request_headers = {
'Authorization': f'Bearer {YOUR_BEARER_TOKEN}',
'Content-Type': 'application/json; charset=utf-8' # Be explicit
}
# --- Create and Send Request ---
# ✅ Create a Request object with url, data, and headers
req = urllib.request.Request(API_ENDPOINT, data=encoded_data, headers=request_headers, method='POST')
print(f"\nSending POST request to {API_ENDPOINT} using urllib...")
try:
with urllib.request.urlopen(req, timeout=10) as response:
# --- Process Response ---
print(f"Status Code: {response.getcode()}")
# Read the response body (bytes) and decode it
response_body_bytes = response.read()
response_body_str = response_body_bytes.decode('utf-8')
# Parse JSON if applicable
response_data = json.loads(response_body_str)
print("Response JSON (urllib):")
print(response_data)
except urllib.error.URLError as e:
print(f"urllib Error: {e}")
except json.JSONDecodeError as e:
print(f"JSON Decode Error: {e}")
Note the need to manually encode the request data to bytes and decode the response bytes.
Example: GET Request with urllib
import urllib.request
import json
# --- Configuration ---
API_ENDPOINT = 'https://jsonplaceholder.typicode.com/users/1'
YOUR_BEARER_TOKEN = 'YOUR_ACTUAL_SECRET_TOKEN_HERE'
# --- Prepare Headers ---
request_headers = {
'Authorization': f'Bearer {YOUR_BEARER_TOKEN}',
}
# --- Create and Send Request ---
# ✅ Create Request object, data is None for GET
req = urllib.request.Request(API_ENDPOINT, data=None, headers=request_headers, method='GET')
print(f"\nSending GET request to {API_ENDPOINT} using urllib...")
try:
with urllib.request.urlopen(req, timeout=10) as response:
# --- Process Response ---
print(f"Status Code: {response.getcode()}")
response_body_bytes = response.read()
response_body_str = response_body_bytes.decode('utf-8')
response_data = json.loads(response_body_str)
print("Response JSON (urllib):")
print(response_data)
except urllib.error.URLError as e:
print(f"urllib Error: {e}")
except json.JSONDecodeError as e:
print(f"JSON Decode Error: {e}")
Choosing the Right Method
requests
withheaders
dict: Highly recommended for most use cases. It's simple, direct, and aligns well with how HTTP headers work.requests
with CustomAuthBase
: Good for complex authentication logic, reusing authentication across multiple requests, or integrating with token refresh mechanisms. Might be overkill for simple bearer tokens.urllib.request
: Use if you cannot have external dependencies (likerequests
). Be prepared for more manual work involving data encoding/decoding and potentially more complex error handling.
Conclusion
Sending Bearer Token authentication with Python HTTP requests involves setting the Authorization
header correctly.
- The
requests
library makes this easy via theheaders
dictionary argument:headers={'Authorization': f'Bearer {token}'}
. This is the preferred method for simplicity and readability. requests
also supports custom authentication classes (auth=BearerAuth(token)
) for more structured or reusable authentication logic.- The built-in
urllib.request
module requires manually creating aRequest
object and adding the header to itsheaders
dictionary, along with manual handling of request/response body encoding.
Choose the method that best suits your project's dependencies and complexity requirements. For most applications, the requests
library offers a superior developer experience.