Beginner
Calling REST APIs in Python: Quick Example
Python’s requests library makes calling REST APIs dead simple. Install it with pip install requests and you can make HTTP calls in one line.
#quick_example.py
import requests # pip install requests
# Make a GET request to a public API
response = requests.get('https://jsonplaceholder.typicode.com/posts/1')
data = response.json() # parse the JSON response into a dict
print(data['title']) # access specific fields
print(response.status_code) # check the HTTP status code
Output:
sunt aut facere repellat provident occaecati excepturi optio reprehenderit
200
The response.json() method converts the API’s JSON response directly into a Python dictionary. The status code 200 means everything went smoothly.
Want to go deeper? Below we cover sending POST requests, authentication methods, and a real-life weather dashboard project.
What is a REST API and Why Should You Care
A REST API is how two programs talk to each other over the internet. When you check the weather on your phone, your app is calling a weather API behind the scenes. When you log into a website using Google, that’s an API call too. As a Python developer, knowing how to call APIs opens up a world of data — weather, stock prices, social media, payment processing, you name it.
Python’s requests library is the gold standard for making HTTP calls. It wraps all the complexity of HTTP into a clean, readable interface.
Installing the Requests Library
The requests library doesn’t come with Python — you need to install it:
pip install requests
Or if you’re on Linux/Mac and need sudo:
sudo pip3 install requests
Making GET Requests With Query Parameters
GET requests are for fetching data. Most APIs accept query parameters to filter or customize the response. You can pass them as a dictionary using the params argument instead of manually building the URL string.
#get_with_params.py
import requests
# Pass query parameters as a dictionary — much cleaner than building the URL
params = {
'userId': 1,
'completed': 'false'
}
response = requests.get(
'https://jsonplaceholder.typicode.com/todos',
params=params # requests builds the URL for you
)
todos = response.json() # list of todo items
print(f"Found {len(todos)} incomplete todos")
print(f"First todo: {todos[0]['title']}")
Output:
Found 11 incomplete todos
First todo: delectus aut autem
The params dictionary gets converted into a query string like ?userId=1&completed=false and appended to the URL automatically. This is safer and cleaner than string concatenation.
POST Requests With JSON Body in Python
POST requests send data to an API — creating new records, submitting forms, or triggering actions. Use the json parameter to send a Python dictionary as a JSON body.
#post_request.py
import requests
# Data to send — requests will serialize this to JSON automatically
new_post = {
'title': 'My API Post',
'body': 'This was created with Python requests',
'userId': 1
}
response = requests.post(
'https://jsonplaceholder.typicode.com/posts',
json=new_post # automatically sets Content-Type: application/json
)
print(f"Status: {response.status_code}") # 201 = created
print(f"New post ID: {response.json()['id']}")
Output:
Status: 201
New post ID: 101
Status code 201 means the resource was created successfully. The API returns the newly created object with its assigned ID.
Authentication Methods for Python API Calls
Most real-world APIs require authentication. Here are the three most common methods you’ll encounter.
API Key in Headers
#api_key_auth.py
import requests
headers = {
'X-API-Key': 'your_api_key_here' # some APIs use different header names
}
response = requests.get('https://api.example.com/data', headers=headers)
print(response.status_code)
Bearer Token Authentication
#bearer_token.py
import requests
token = 'your_access_token_here'
headers = {
'Authorization': f'Bearer {token}' # standard OAuth2 format
}
response = requests.get('https://api.example.com/user', headers=headers)
print(response.json())
Basic Authentication
#basic_auth.py
import requests
# requests has built-in support for Basic Auth
response = requests.get(
'https://api.example.com/account',
auth=('username', 'password') # tuple of (user, pass)
)
print(response.status_code)
Note: Never hardcode API keys or tokens directly in your code. Use environment variables or a .env file instead. Check out our article on managing environment variables with dotenv for the proper approach.
Handling API Errors and Status Codes in Python
APIs don’t always return what you expect. Network issues, invalid data, rate limits — things go wrong. Proper error handling separates production code from tutorial code.
#error_handling.py
import requests
def safe_api_call(url):
try:
response = requests.get(url, timeout=10) # always set a timeout
response.raise_for_status() # raises exception for 4xx/5xx codes
return response.json()
except requests.exceptions.Timeout:
print("Request timed out — the server took too long to respond")
except requests.exceptions.HTTPError as e:
print(f"HTTP error: {e.response.status_code} - {e.response.reason}")
except requests.exceptions.ConnectionError:
print("Connection failed — check your internet or the URL")
except requests.exceptions.JSONDecodeError:
print("Response wasn't valid JSON")
return None
# Test with a valid URL
data = safe_api_call('https://jsonplaceholder.typicode.com/posts/1')
if data:
print(f"Got: {data['title'][:40]}...")
# Test with a URL that returns 404
data = safe_api_call('https://jsonplaceholder.typicode.com/posts/99999')
Output:
Got: sunt aut facere repellat provident MDash...
HTTP error: 404 - Not Found
The raise_for_status() method is your best friend. It throws an exception for any 4xx or 5xx status code, so you don’t accidentally process error responses as valid data.
Working With Response Headers and Pagination
Many APIs return data in pages. You need to check the response headers or body for pagination info and loop through all pages to get the complete dataset.
#pagination.py
import requests
def get_all_posts(base_url):
all_posts = []
page = 1
while True:
response = requests.get(base_url, params={'_page': page, '_limit': 10})
posts = response.json()
if not posts: # empty list means no more pages
break
all_posts.extend(posts)
print(f"Page {page}: got {len(posts)} posts")
page += 1
return all_posts
posts = get_all_posts('https://jsonplaceholder.typicode.com/posts')
print(f"\nTotal posts collected: {len(posts)}")
Output:
Page 1: got 10 posts
Page 2: got 10 posts
...
Page 10: got 10 posts
Total posts collected: 100
Real-Life Example: Building a Weather Dashboard Script
Let’s put it all together with a practical script that fetches weather data from the Open-Meteo API (free, no API key needed) and displays a simple dashboard.
#weather_dashboard.py
import requests
from datetime import datetime
def get_weather(city_lat, city_lon, city_name):
"""Fetch current weather for a location using Open-Meteo API"""
url = 'https://api.open-meteo.com/v1/forecast'
params = {
'latitude': city_lat,
'longitude': city_lon,
'current_weather': True, # get current conditions
'timezone': 'auto' # detect timezone from coordinates
}
try:
response = requests.get(url, params=params, timeout=10)
response.raise_for_status()
data = response.json()
weather = data['current_weather']
return {
'city': city_name,
'temp': weather['temperature'],
'wind': weather['windspeed'],
'time': weather['time']
}
except requests.exceptions.RequestException as e:
print(f"Failed to get weather for {city_name}: {e}")
return None
# Define cities with their coordinates
cities = [
(-33.87, 151.21, 'Sydney'),
(51.51, -0.13, 'London'),
(40.71, -74.01, 'New York'),
(35.68, 139.69, 'Tokyo'),
]
# Fetch and display weather for all cities
print("=" * 45)
print(" WEATHER DASHBOARD")
print("=" * 45)
for lat, lon, name in cities:
w = get_weather(lat, lon, name)
if w:
print(f" {w['city']:12s} | {w['temp']:5.1f} C | Wind: {w['wind']} km/h")
print("=" * 45)
print(f" Updated: {datetime.now().strftime('%Y-%m-%d %H:%M')}")
Output:
=============================================
WEATHER DASHBOARD
=============================================
Sydney | 22.3 C | Wind: 15.2 km/h
London | 8.1 C | Wind: 20.5 km/h
New York | 11.7 C | Wind: 12.8 km/h
Tokyo | 16.4 C | Wind: 8.3 km/h
=============================================
Updated: 2026-03-13 09:15
This script demonstrates GET requests with query parameters, response parsing, error handling with timeouts, and looping through multiple API calls. You could easily extend it with a scheduler to run every hour or save results to a CSV for tracking trends over time.
Frequently Asked Questions
What is the difference between requests.get() and requests.post() in Python?
GET fetches data from a server without changing anything. POST sends data to create or update a resource. Use GET when you’re reading, POST when you’re writing. Some APIs also use PUT for updates and DELETE for removals.
How do I send form data instead of JSON with Python requests?
Use the data parameter instead of json: requests.post(url, data={'key': 'value'}). This sends the data as application/x-www-form-urlencoded, which is what HTML forms use.
Should I use requests or urllib for API calls in Python?
requests is almost always the better choice. While urllib is built-in, its API is verbose and harder to use. The requests library handles cookies, sessions, redirects, and encoding automatically.
How do I handle API rate limits with Python requests?
Check the response headers for rate limit info (usually X-RateLimit-Remaining and Retry-After). If you get a 429 status code, wait the specified time before retrying. For robust solutions, use exponential backoff with the tenacity library.
Conclusion
The requests library gives you everything you need to interact with REST APIs in Python — from simple GET calls to authenticated POST requests with error handling. The key patterns to remember are: always set a timeout, use raise_for_status() for error detection, and never hardcode credentials. With these fundamentals, you can integrate almost any web service into your Python projects.
Reference
Official requests documentation: https://docs.python-requests.org/
Python urllib documentation: https://docs.python.org/3/library/urllib.html