By WebNest Studio Engineering Team
Python Tutorial
HTTP and API Basics in Python with requests
Most modern applications need to talk to other systems over the internet, and HTTP is the protocol almost all of that communication happens over. Python's third-party requests library is the most widely used tool for making HTTP calls because it wraps the lower-level built-in urllib module in a much simpler, more readable interface — installed with pip install requests.
Understanding a handful of core ideas — HTTP methods, status codes, and JSON — is enough to consume the vast majority of web APIs you'll encounter.
Why it matters
Programs get weather data, take payments, send messages and talk to AI models by calling web APIs. The steps are always the same: build a URL with parameters, send a request, check the status code and parse the JSON that comes back. This lesson is the foundation for both using APIs and, later, building them with FastAPI.
GET and POST Requests
A GET request retrieves data from a server and should not change anything on the server side — it's what happens whenever you visit a webpage or fetch data from an API endpoint. A POST request sends data to a server, typically to create or modify something, such as submitting a form or creating a new record. With requests, these look like requests.get(url, params={...}) for query parameters and requests.post(url, json={...}) for sending a JSON request body.
Status Codes
Every HTTP response includes a numeric status code summarizing what happened. Codes in the 200s mean success (200 OK is the most common). Codes in the 300s indicate redirection. Codes in the 400s mean the client made a bad request (404 Not Found, 401 Unauthorized, 400 Bad Request). Codes in the 500s mean the server itself failed while processing an otherwise valid request. Checking response.status_code, or calling response.raise_for_status() (which raises an exception for 4xx/5xx responses), is essential before trusting a response's contents.
Parsing JSON Responses
Most modern web APIs return data formatted as JSON (JavaScript Object Notation), a lightweight text format that maps naturally onto Python dictionaries and lists. Calling response.json() on a requests response object parses the JSON body and returns it as native Python data structures, ready to use directly — no manual string parsing required. If the fallback urllib module is used instead of requests, you would need to manually decode the response bytes and pass them through Python's built-in json.loads() function to get the same result.
Examples
A GET request with query parameters and JSON parsing
import requests
response = requests.get(
"https://api.example.com/users",
params={"active": "true", "limit": 2}
)
response.raise_for_status() # raises an exception for 4xx/5xx responses
data = response.json()
print(response.status_code)
print(type(data))
print(data)
200
<class 'list'>
[{'id': 1, 'name': 'Asha', 'active': True}, {'id': 2, 'name': 'Ravi', 'active': True}]
A POST request sending JSON, and the urllib fallback for comparison
import requests
# --- Using requests (typical, third-party) ---
payload = {"title": "New Task", "done": False}
response = requests.post("https://api.example.com/tasks", json=payload)
print(response.status_code)
print(response.json())
# --- Using urllib (standard library fallback, no installation needed) ---
import json
import urllib.request
data_bytes = json.dumps(payload).encode("utf-8")
req = urllib.request.Request(
"https://api.example.com/tasks",
data=data_bytes,
headers={"Content-Type": "application/json"},
method="POST",
)
with urllib.request.urlopen(req) as resp:
result = json.loads(resp.read().decode("utf-8"))
print(result)
201
{'id': 42, 'title': 'New Task', 'done': False}
{'id': 42, 'title': 'New Task', 'done': False}
Common Mistakes
- Assuming a request succeeded just because it didn't raise a connection error, without checking response.status_code or calling raise_for_status() — a 404 or 500 response is still a "successful" HTTP round trip from requests' point of view.
- Calling response.json() on a response that isn't actually JSON (such as an HTML error page), which raises a decoding error instead of the expected dictionary.
- Passing a dictionary to the "data" parameter when a JSON body was intended — requests.post(url, data={...}) form-encodes the payload, while requests.post(url, json={...}) sends it as a proper JSON body with the correct Content-Type header.
- Hardcoding API keys or tokens directly into request code instead of reading them from environment variables or a configuration file.
Exercise
Without using the network, practise the three steps around a request. Build the full URL for a GET request with two query parameters. Parse the JSON body of a response and print the name inside it. Write describe, which classifies a status code as success, client error or server error.
Expected output: https://api.example.com/users?page=2&limit=10, Asha, client error (one per line)
import json
from urllib.parse import urlencode
base = "https://api.example.com/users"
params = {"page": 2, "limit": 10}
# TODO: print the full URL with the query string
body = '{"data": {"name": "Asha"}}'
# TODO: parse the body and print the name
def describe(status):
# TODO: 200-299 -> "success", 400-499 -> "client error", 500-599 -> "server error"
pass
print(describe(404))
Show hints
urlencode(params)producespage=2&limit=10; join it to the base with?.- Chained comparisons are convenient here:
200 <= status < 300.
Show solution
import json
from urllib.parse import urlencode
base = "https://api.example.com/users"
params = {"page": 2, "limit": 10}
print(f"{base}?{urlencode(params)}")
body = '{"data": {"name": "Asha"}}'
print(json.loads(body)["data"]["name"])
def describe(status):
if 200 <= status < 300:
return "success"
if 400 <= status < 500:
return "client error"
return "server error"
print(describe(404))
Quick quiz
1. Which HTTP method is used to retrieve data without changing anything?
- A. GET
- B. POST
- C. DELETE
- D. PUT
Show answer
A. POST creates, PUT and PATCH update, and DELETE removes.
2. What does the status code 404 mean?
- A. The request succeeded
- B. The resource was not found
- C. The server crashed
- D. Authentication is required
Show answer
B. Codes from 400 to 499 indicate a problem with the request.
3. With the requests library, how is a JSON response body converted to Python objects?
- A. response.text
- B. response.data
- C. response.json()
- D. json(response)
Show answer
C. It parses the body and returns dictionaries and lists.
Interview questions
What do the classes of HTTP status codes mean?
Show answer
2xx means success: 200 OK, 201 Created, 204 No Content. 3xx means redirection. 4xx means the client made a mistake: 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found. 5xx means the server failed: 500 Internal Server Error, 503 Service Unavailable. A client should check the code before using the body.
What should you always do when calling an external API from Python?
Show answer
Set a timeout, because without one a request can hang indefinitely. Check the status, for example with response.raise_for_status(), before reading the body. Handle network exceptions. Keep API keys out of the code, in environment variables. For calls that may fail temporarily, retry a limited number of times with a growing delay.
Key Points to Remember
- GET retrieves data without side effects; POST sends data, typically to create or change something on the server.
- HTTP status codes summarize the outcome: 2xx success, 3xx redirect, 4xx client error, 5xx server error.
- requests.get/post plus response.json() is the standard, simple pattern for consuming JSON APIs in Python.
- urllib is the built-in fallback when requests (a third-party dependency) is not installed or allowed.
Practice in the playground
Write and run your own code for this topic, or start from the exercise or an example. Runs Python in your browser. Your code for each topic is kept in this browser.
Run the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.
Written by
WebNest Studio Engineering Team
Practical lessons from developers working with Java, Python, React, databases and production systems.
Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering