Python course menu
Working With APIs in Python: requests, JSON, Parameters and Errors
Learn to use web APIs in Python with the requests library: send GET and POST requests, read JSON responses, pass query parameters, check status codes, handle timeouts and errors, and use API keys safely.
On this page
An API (Application Programming Interface) lets programs talk to other programs. Web APIs let your Python code get weather forecasts, exchange rates, GitHub data, or send messages, usually by sending an HTTP request to a URL and receiving JSON back. The requests library makes this simple.
Setup#
requests is a third-party library. Install it (ideally inside a virtual environment):
python -m pip install requestsThe examples use JSONPlaceholder, a free fake API built for learning. Its responses never change, so you will see the same output as below.
Your first GET request#
import requests
response = requests.get("https://jsonplaceholder.typicode.com/todos/1", timeout=10)
print(response.status_code)
print(response.headers["Content-Type"])
data = response.json()
print(data)
print(data["title"], data["completed"])200
application/json; charset=utf-8
{'userId': 1, 'id': 1, 'title': 'delectus aut autem', 'completed': False}
delectus aut autem Falserequests.get(url)sends the request and returns a response object.status_code200 means success..json()parses the JSON body into Python dictionaries and lists (see JSON).timeout=10stops waiting after 10 seconds. Always set a timeout; without one, a slow server can freeze your program forever.
HTTP status codes#
Query parameters#
Pass filters as a dictionary with params; requests builds the URL for you:
r = requests.get("https://jsonplaceholder.typicode.com/posts",
params={"userId": 1}, timeout=10)
print(r.url)
posts = r.json()
print(len(posts), posts[0]["id"], posts[0]["title"][:30])https://jsonplaceholder.typicode.com/posts?userId=1
10 1 sunt aut facere repellat proviSending data with POST#
Create something on the server by sending JSON:
new_post = {"title": "Learning Python", "body": "APIs are fun", "userId": 1}
r = requests.post("https://jsonplaceholder.typicode.com/posts", json=new_post, timeout=10)
print(r.status_code)
print(r.json())201
{'title': 'Learning Python', 'body': 'APIs are fun', 'userId': 1, 'id': 101}json= converts your dictionary to JSON and sets the right headers. (JSONPlaceholder pretends to save it and returns a new id; nothing is really stored.)
Handling errors#
A missing resource does not raise an exception by itself; check the status:
r = requests.get("https://jsonplaceholder.typicode.com/posts/99999", timeout=10)
print(r.status_code, r.ok, r.json())
try:
r.raise_for_status()
except requests.HTTPError as e:
print("HTTPError:", e)404 False {}
HTTPError: 404 Client Error: Not Found for url: https://jsonplaceholder.typicode.com/posts/99999raise_for_status() turns 4xx and 5xx responses into exceptions. A robust helper handles network problems too:
def get_user(user_id):
try:
r = requests.get(f"https://jsonplaceholder.typicode.com/users/{user_id}", timeout=5)
r.raise_for_status()
return r.json()
except requests.Timeout:
print("The request timed out")
except requests.HTTPError as e:
print("HTTP error:", e.response.status_code)
except requests.RequestException as e:
print("Network problem:", type(e).__name__)
return None
user = get_user(1)
print(user["name"], "|", user["address"]["city"], "|", user["company"]["name"])
print(get_user(999))Leanne Graham | Gwenborough | Romaguera-Crona
HTTP error: 404
Nonerequests.RequestException is the parent of all requests errors, including ConnectionError when a server cannot be reached at all.
A real public API#
Real APIs work the same way. GitHub's API, for example:
r = requests.get("https://api.github.com/repos/python/cpython", timeout=10)
repo = r.json()
print(repo["full_name"], "|", repo["language"], "|", repo["html_url"])python/cpython | Python | https://github.com/python/cpythonThe response also contains numbers that change constantly, such as stargazers_count. Each API documents its URLs, parameters and fields; reading those docs is part of the job.
API keys and authentication#
Most real APIs require a key or token, usually sent in a header:
import os
headers = {"Authorization": f"Bearer {os.environ['WEATHER_API_KEY']}"}
r = requests.get("https://api.example.com/v1/forecast",
params={"city": "Pune"}, headers=headers, timeout=10)Respect each API's rate limits: if you receive status 429, wait before retrying.
Without installing anything#
The standard library's urllib.request can make simple requests too, though requests is much friendlier:
import json
import urllib.request
with urllib.request.urlopen("https://jsonplaceholder.typicode.com/todos/2", timeout=10) as resp:
data = json.load(resp)
print(data["id"], data["title"])2 quis ut nam facilis et officia quiCommon mistakes#
Practice#
Practice exercise
Fetch all todos for userId 1 from https://jsonplaceholder.typicode.com/todos, print how many are completed out of the total, and list the ids of the first five that are not completed.
Show hint
Use params={"userId": 1}, then count with sum(1 for t in todos if t["completed"]).
Show solution
import requests
r = requests.get("https://jsonplaceholder.typicode.com/todos",
params={"userId": 1}, timeout=10)
r.raise_for_status()
todos = r.json()
done = sum(1 for t in todos if t["completed"])
print(f"{done} of {len(todos)} completed")
print([t["id"] for t in todos if not t["completed"]][:5])11 of 20 completed
[1, 2, 3, 5, 6]Key takeaway#
Finished this lesson?