Python
HTTP authentication
Requests library
programming tutorial
web development

How do I use basic HTTP authentication with the Python Requests library?

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

HTTP Basic authentication is simple: the client sends a username and password with each request. In Python, the requests library already knows how to build the correct Authorization header, so the main job is using that support cleanly and keeping credentials out of source code.

The simplest requests pattern

For one-off calls, pass a (username, password) tuple to the auth argument. requests converts it into the proper Basic auth header for you.

python
1import requests
2
3response = requests.get(
4    "https://httpbin.org/basic-auth/demo/swordfish",
5    auth=("demo", "swordfish"),
6    timeout=10,
7)
8
9print(response.status_code)
10print(response.json())

This is the most common answer because it is short and correct. It is also better than building the header manually, since the library handles encoding and request preparation consistently.

Use HTTPBasicAuth when you want explicit intent

The tuple form is convenient, but HTTPBasicAuth can make larger codebases easier to read.

python
1import requests
2from requests.auth import HTTPBasicAuth
3
4auth = HTTPBasicAuth("demo", "swordfish")
5
6response = requests.get(
7    "https://httpbin.org/basic-auth/demo/swordfish",
8    auth=auth,
9    timeout=10,
10)
11
12print(response.ok)

This becomes useful when your application supports multiple authentication schemes and you want the chosen strategy to be obvious from the code.

Reuse credentials with a session

If you make several requests to the same service, create a Session. That gives you connection reuse and centralizes the auth configuration.

python
1import requests
2from requests.auth import HTTPBasicAuth
3
4session = requests.Session()
5session.auth = HTTPBasicAuth("demo", "swordfish")
6
7for endpoint in [
8    "https://httpbin.org/basic-auth/demo/swordfish",
9    "https://httpbin.org/headers",
10]:
11    response = session.get(endpoint, timeout=10)
12    print(endpoint, response.status_code)

This pattern is cleaner than repeating auth= on every call, especially if you later add common headers, retry logic, or timeouts through a wrapper client.

Keep credentials out of code

Hard-coding secrets is fine for a quick demo and poor for real systems. Read them from the environment instead.

python
1import os
2import requests
3
4username = os.environ["API_USERNAME"]
5password = os.environ["API_PASSWORD"]
6
7response = requests.get(
8    "https://httpbin.org/basic-auth/demo/swordfish",
9    auth=(username, password),
10    timeout=10,
11)
12
13print(response.status_code)

In production, those values might come from a secrets manager, container environment, or CI variables. The key point is that the application code should not be the secret store.

Handle failures separately from transport errors

A failed login and a broken network are different problems. A 401 Unauthorized response means the server answered the request and rejected the credentials. Connection timeouts or TLS issues raise exceptions instead.

python
1import requests
2
3try:
4    response = requests.get(
5        "https://httpbin.org/basic-auth/demo/swordfish",
6        auth=("wrong-user", "wrong-password"),
7        timeout=10,
8    )
9
10    if response.status_code == 401:
11        print("Authentication failed")
12    else:
13        response.raise_for_status()
14        print(response.text)
15
16except requests.RequestException as exc:
17    print(f"Transport error: {exc}")

Keeping those paths separate makes retry logic and error reporting much more accurate.

Manual headers are possible but rarely necessary

If you are debugging or writing a thin wrapper around another HTTP layer, you can build the header yourself. Most of the time, that is extra work without a benefit.

python
1import base64
2import requests
3
4token = base64.b64encode(b"demo:swordfish").decode("ascii")
5headers = {
6    "Authorization": f"Basic {token}",
7}
8
9response = requests.get(
10    "https://httpbin.org/headers",
11    headers=headers,
12    timeout=10,
13)
14
15print(response.status_code)

This works, but using auth= is safer and clearer unless you have a concrete reason not to.

Common Pitfalls

The biggest mistake is using Basic auth over plain HTTP. Base64 is not encryption, so anyone who can observe the traffic can recover the credentials. Use HTTPS.

Another problem is logging full request headers during debugging. That can leak the Authorization header into local logs, CI output, or monitoring systems. Redact it whenever request logging is enabled.

Developers also tend to repeat auth= everywhere instead of creating a session or dedicated API client. That increases duplication and makes later changes harder.

Finally, do not treat every failure as an authentication failure. A 401 is different from a DNS error, a socket timeout, or a certificate problem, and the fix depends on which one happened.

Summary

  • Use requests.get(..., auth=(user, password)) for the simplest correct Basic auth call.
  • 'HTTPBasicAuth makes the authentication strategy more explicit.'
  • 'requests.Session() is better for repeated authenticated requests.'
  • Store credentials outside the source code, such as in environment variables or a secret store.
  • Use HTTPS and handle 401 responses separately from transport exceptions.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.