The Python Requests library makes it easy to communicate with websites and web APIs. Instead of working directly with low-level networking details, you can send an HTTP request with a few readable lines of code and receive a response object that contains the status, headers, text, and JSON data.
This beginner guide explains how to install Requests, send GET and POST requests, pass query parameters, add headers, read JSON, upload data, use timeouts, handle errors, and reuse connections with a session. You will also build a small practical API client.
Before starting, a basic understanding of Python data types and dictionaries and exception handling will be helpful.
What Is the Requests Library?
Requests is a third-party Python package for HTTP communication. It supports common methods such as GET, POST, PUT, PATCH, and DELETE. It also handles redirects, cookies, authentication, compression, and HTTPS certificates.
The official Requests documentation contains the complete API reference. For background on HTTP concepts, see the MDN HTTP guide.
Install Python Requests
Create and activate a virtual environment before installing project dependencies. Then run:
python -m pip install requestsConfirm the installation:
import requests
print(requests.__version__)Using python -m pip helps ensure that the package is installed for the same Python interpreter that runs your script. The virtual environment guide explains why isolated dependencies are useful.
Send Your First GET Request
A GET request retrieves a resource:
import requests
response = requests.get(
"https://jsonplaceholder.typicode.com/posts/1",
timeout=10,
)
print(response.status_code)
print(response.text)The timeout value limits how long the program waits for the server. Always use a timeout in production scripts. Without one, a request can remain blocked for an unexpectedly long time.
Understand the Response Object
The response contains several useful attributes:
response.status_code: numeric HTTP status.response.ok: true for successful status ranges.response.headers: response headers.response.text: decoded text.response.content: raw bytes.response.json(): parsed JSON data.response.url: final URL after parameters and redirects.
data = response.json()
print(data["title"])JSON objects normally become Python dictionaries, while JSON arrays become lists. Review the data types guide when working with nested API responses.
Check Status Codes Correctly
Do not assume every request succeeded. Use raise_for_status() to raise an exception for client and server errors:
import requests
response = requests.get(
"https://jsonplaceholder.typicode.com/posts/1",
timeout=10,
)
response.raise_for_status()
post = response.json()
print(post["title"])Common status codes include 200 for success, 201 for a created resource, 400 for a bad request, 401 for missing authentication, 404 for a missing resource, and 500 for a server failure.
Send Query Parameters
Pass parameters with a dictionary instead of manually building a URL:
import requests
params = {
"userId": 1,
"_limit": 5,
}
response = requests.get(
"https://jsonplaceholder.typicode.com/posts",
params=params,
timeout=10,
)
response.raise_for_status()
print(response.url)
for post in response.json():
print(post["title"])Requests encodes the parameters safely and adds them to the query string.
Add Headers
Headers provide metadata such as the expected response format, authentication token, or user agent:
headers = {
"Accept": "application/json",
"User-Agent": "AcademifyExample/1.0",
}
response = requests.get(
"https://jsonplaceholder.typicode.com/posts/1",
headers=headers,
timeout=10,
)Never publish private API keys directly in source code. Store secrets in environment variables and read them at runtime.
Send a POST Request with JSON
POST commonly creates a resource or submits data:
import requests
payload = {
"title": "Learning Requests",
"body": "A practical HTTP example",
"userId": 1,
}
response = requests.post(
"https://jsonplaceholder.typicode.com/posts",
json=payload,
timeout=10,
)
response.raise_for_status()
print(response.status_code)
print(response.json())The json argument serializes the dictionary and sets the JSON content type. Use data instead when a server expects form fields.
Handle Errors and Timeouts
Catch specific Requests exceptions:
import requests
try:
response = requests.get(
"https://jsonplaceholder.typicode.com/posts/1",
timeout=5,
)
response.raise_for_status()
data = response.json()
except requests.Timeout:
print("The request took too long.")
except requests.ConnectionError:
print("Could not connect to the server.")
except requests.HTTPError as error:
print(f"HTTP error: {error}")
except requests.RequestException as error:
print(f"Request failed: {error}")
else:
print(data["title"])Specific exceptions produce clearer messages and make recovery decisions easier. For persistent applications, record failures with Python logging.
Use a Session for Repeated Requests
A Session reuses connections and keeps shared cookies and headers:
import requests
with requests.Session() as session:
session.headers.update({
"Accept": "application/json",
"User-Agent": "AcademifyExample/1.0",
})
for post_id in range(1, 4):
response = session.get(
f"https://jsonplaceholder.typicode.com/posts/{post_id}",
timeout=10,
)
response.raise_for_status()
print(response.json()["title"])Sessions are especially useful when a script makes many calls to the same host.
Download a File Safely
Use streaming for large downloads so the entire file is not loaded into memory:
from pathlib import Path
import requests
url = "https://www.python.org/static/community_logos/python-logo.png"
output = Path("python-logo.png")
with requests.get(url, stream=True, timeout=20) as response:
response.raise_for_status()
with output.open("wb") as file:
for chunk in response.iter_content(chunk_size=8192):
if chunk:
file.write(chunk)
print(f"Saved to {output.resolve()}")The pathlib guide shows cleaner ways to manage file paths.
Build a Small API Client Function
Functions make request logic reusable and testable:
import requests
BASE_URL = "https://jsonplaceholder.typicode.com"
def get_post(post_id: int) -> dict:
if post_id < 1:
raise ValueError("post_id must be positive")
response = requests.get(
f"{BASE_URL}/posts/{post_id}",
timeout=10,
)
response.raise_for_status()
return response.json()
try:
post = get_post(1)
except (ValueError, requests.RequestException) as error:
print(f"Could not load the post: {error}")
else:
print(post["title"])This separation lets you test validation independently and mock the HTTP call in automated tests. The Pytest guide explains how to test Python functions.
Common Beginner Mistakes
- Making requests without a timeout.
- Using
response.json()without checking the status first. - Placing API keys directly in the script.
- Catching only a broad
Exception. - Downloading large files without streaming.
- Ignoring rate limits documented by an API provider.
- Assuming every server returns JSON.
Frequently Asked Questions
Is Requests included with Python?
No. Install it with pip inside your project environment.
What is the difference between text and content?
text returns decoded text. content returns raw bytes and is useful for images and binary files.
Should I use Requests for asynchronous code?
Requests is synchronous. For high-concurrency applications, consider an async HTTP client and learn the foundations in the asyncio guide.
How do I send authentication?
The API may require a header token, basic authentication, OAuth, or another scheme. Follow that API’s official documentation.
Conclusion
Python Requests provides a readable way to work with HTTP. Start with GET requests, inspect status codes, parse JSON, add timeouts, and handle specific errors. Then use sessions, streaming, and reusable functions as your scripts become larger.
The most important habit is to treat every network call as something that can fail. Careful validation and clear error handling turn a quick script into dependable software.






