Skip to content

How to Make HTTP Requests with Bearer Token Authentication in Python

Learn how to send authenticated HTTP requests in Python using Bearer tokens with the requests library and urllib, including proper header formatting and error handling.

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).

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

API_ENDPOINT = 'https://jsonplaceholder.typicode.com/posts' # Example endpoint

YOUR_BEARER_TOKEN = 'YOUR_ACTUAL_SECRET_TOKEN_HERE'

post_data = {'title': 'foo', 'body': 'bar', 'userId': 1}

request_headers = {
    'Authorization': f'Bearer {YOUR_BEARER_TOKEN}',
    'Content-Type': 'application/json' # Often needed for POST data
}

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
        timeout=10 # Good practice to set a timeout
    )
    response.raise_for_status() # Raise an exception for bad status codes (4xx or 5xx)

    print(f"Status Code: {response.status_code}") # e.g., 201 Created

    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 with Bearer followed by the token.
  • This dictionary is passed via the headers= argument to requests.post().
  • The same headers dictionary can be used with requests.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'

header_fstring = f'Bearer {YOUR_BEARER_TOKEN}'
print(f"f-string: '{header_fstring}'")

header_concat = 'Bearer ' + YOUR_BEARER_TOKEN
print(f"Concatenation: '{header_concat}'")

header_format = 'Bearer {}'.format(YOUR_BEARER_TOKEN)
print(f"str.format(): '{header_format}'")

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

  def __call__(self, request_obj):
    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

import requests

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}

bearer_auth = BearerAuth(YOUR_BEARER_TOKEN)

try:
    print(f"\nSending GET request to {API_ENDPOINT_USERS}...")
    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}")
except requests.exceptions.RequestException as e:
    print(f"GET Error: {e}")


try:
    print(f"\nSending POST request to {API_ENDPOINT_POSTS}...")
    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

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}

encoded_data = json.dumps(post_data_dict).encode('utf-8')
request_headers = {
    'Authorization': f'Bearer {YOUR_BEARER_TOKEN}',
    'Content-Type': 'application/json; charset=utf-8' # Be explicit
}

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:
        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}")

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

API_ENDPOINT = 'https://jsonplaceholder.typicode.com/users/1'
YOUR_BEARER_TOKEN = 'YOUR_ACTUAL_SECRET_TOKEN_HERE'

request_headers = {
    'Authorization': f'Bearer {YOUR_BEARER_TOKEN}',
}

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:
        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 with headers dict: Highly recommended for most use cases. It's simple, direct, and aligns well with how HTTP headers work.
  • requests with Custom AuthBase: 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 (like requests). 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 the headers 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 a Request object and adding the header to its headers 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.

Navigation