Making HTTP Requests with requests

· 3 min read · Updated March 14, 2026 · beginner
python http requests web api

The requests library is the de facto standard for making HTTP requests in Python. It provides a clean, intuitive API that handles the messy details of HTTP so you can focus on your application logic.

If you have worked with Python built-in urllib, requests will feel like a breath of fresh air. It handles connection pooling, Keep-Alive, and automatic JSON decoding with almost no setup.

Installing requests

Before using requests, install it via pip:

pip install requests

That is it. The library has no required dependencies beyond Python standard library.

Making Your First Request

The most common HTTP method is GET, used to fetch data from a server:

import requests

response = requests.get("https://api.github.com/users/octocat")

print(response.status_code)  # 200
print(response.text)         # Raw response body as string

The response object contains everything about the request. The status_code tells you whether the request succeeded. A 200 means OK, 404 means not found, and 5xx errors indicate server problems.

Working with Response Data

The requests library automatically decodes common response formats. For JSON responses, use the .json() method:

import requests

response = requests.get("https://api.github.com/users/octocat")
data = response.json()

print(data["login"])      # octocat
print(data["followers"])  # number of followers

If the response is not valid JSON, .json() raises an exception.

For other response types, access the raw content directly:

response = requests.get("https://example.com/image.png")
content = response.content  # Raw bytes

# Save to file
with open("image.png", "wb") as f:
    f.write(content)

Sending Data with POST

POST requests send data to the server, commonly used when submitting forms or creating resources:

import requests

payload = {"username": "alice", "password": "secret123"}
response = requests.post("https://httpbin.org/post", data=payload)

print(response.status_code)  # 200
print(response.json())       # Echoes back what was sent

The data parameter sends form-encoded data. For JSON bodies, use json=:

import requests

payload = {"title": "My Post", "body": "Content here"}
response = requests.post("https://httpbin.org/post", json=payload)

print(response.status_code)  # 200

Using json= automatically sets the Content-Type header to application/json.

Query Parameters

Append parameters to the URL using the params argument:

import requests

params = {"q": "python requests", "limit": 5}
response = requests.get("https://httpbin.org/get", params=params)

print(response.url)  # https://httpbin.org/get?q=python+requests&limit=5

The library handles URL encoding automatically.

Custom Headers

Pass custom headers with the headers argument:

import requests

headers = {
    "Authorization": "Bearer your-token-here",
    "Accept": "application/json",
    "User-Agent": "MyApp/1.0"
}
response = requests.get("https://api.example.com/data", headers=headers)

Common uses include authentication tokens, content type negotiation, and user agent strings.

Handling Errors Gracefully

HTTP errors are inevitable. The requests library provides ways to handle them:

import requests

response = requests.get("https://httpbin.org/status/404")

# Check status code manually
if response.status_code == 404:
    print("Resource not found")

# Raise an exception for bad status codes
try:
    response.raise_for_status()
except requests.HTTPError as e:
    print(f"Request failed: {e}")

Using raise_for_status() is cleaner than checking status codes manually—it raises an exception for any 4xx or 5xx response.

Timeouts

Always set timeouts to prevent your program from hanging indefinitely:

import requests

# Timeout after 5 seconds
response = requests.get("https://httpbin.org/delay/10", timeout=5)
# Raises requests.exceptions.ReadTimeout

A timeout of 5-10 seconds is reasonable for most APIs. Adjust based on the service you are calling.

Session Objects

If you make multiple requests to the same server, use a Session to reuse connections:

import requests

session = requests.Session()
session.headers.update({"Authorization": "Bearer token"})

# Subsequent requests reuse the connection and headers
response1 = session.get("https://api.example.com/users")
response2 = session.get("https://api.example.com/posts")

Sessions also let you persist cookies across requests, which is useful for authentication flows.

Real-World Example: Fetching Weather Data

Here is a practical example using a public weather API:

import requests
import os

API_KEY = os.environ.get("WEATHER_API_KEY")
CITY = "London"

url = f"https://api.openweathermap.org/data/2.5/weather"
params = {"q": CITY, "appid": API_KEY, "units": "metric"}

try:
    response = requests.get(url, params=params, timeout=10)
    response.raise_for_status()
    
    data = response.json()
    print(f"Temperature: {data["main"]["temp"]}C")
    print(f"Conditions: {data["weather"][0]["description"]}")
    
except requests.exceptions.Timeout:
    print("Request timed out")
except requests.exceptions.HTTPError as e:
    print(f"API error: {e}")
except requests.exceptions.RequestException as e:
    print(f"Request failed: {e}")

This pattern—timeout, raise_for_status(), JSON parsing, and exception handling—works well for most API integrations.

See Also