HTTP Methods and Status Codes
20 min · Intermediate
Need help? Ask a tutor on WhatsApp (opens in a new tab)Every time a browser or a Python program talks to a server, it sends a request and gets a response. HTTP is the set of rules for both. Two things matter most: the method (what to do) and the status code (what happened).
A request and a response
HTTP is the text protocol a client and a server use to talk on the web. The client sends a request, and the server answers with a response that has a status code.
A client sends a request. A server sends back one response. Both are plain text: a first line, some header lines, a blank line, then an optional body.
request = ( "GET /products/7 HTTP/1.1\r\n" "Host: shop.example.com\r\n" "\r\n")print(request.splitlines()[0])Example 1 · Parse a raw request
raw = ( "POST /orders HTTP/1.1\r\n" "Host: shop.example.com\r\n" "Content-Type: application/json\r\n" "\r\n" '{"item": "pen"}')head, body = raw.split("\r\n\r\n", 1)lines = head.split("\r\n")method, path, version = lines[0].split()headers = dict(line.split(": ", 1) for line in lines[1:])print(method, path, version)print(headers["Host"])print(body)POST /orders HTTP/1.1
shop.example.com
{"item": "pen"}What this code does
- Headers end at the first blank line; everything after it is the body.
- Each header line is
Name: value, sosplit(": ", 1)builds a dictionary.
The first line holds the method, the path and the protocol version. The Host header names the site.
Methods say what to do
Idempotent means that sending the same request many times leaves the server in the same state as sending it once. A safe method goes further: it changes nothing at all.
The method is the verb of the request. GET reads, POST creates, PUT replaces, PATCH changes part, and DELETE removes. Two words help here: safe and idempotent.
SAFE = {"GET", "HEAD"}IDEMPOTENT = {"GET", "HEAD", "PUT", "DELETE"}for method in ("GET", "POST", "PUT", "DELETE"): print(method, method in SAFE, method in IDEMPOTENT)Example 2 · Safe and idempotent in numbers
store = {}log = []
def put(key, value): store[key] = value
def post(value): log.append(value)
for _ in range(3): put(1, "Pen") post("Pen")
print("PUT result:", store)print("POST result:", log)PUT result: {1: 'Pen'}
POST result: ['Pen', 'Pen', 'Pen']What this code does
- PUT three times leaves one record: the same state as once.
- POST three times adds three records, so it is not idempotent.
POST is neither safe nor idempotent. Sending it twice can create two orders.
Status codes say what happened
Status code is the three-digit number in a response that tells the client whether the request worked. The first digit gives the family: 2 success, 3 redirect, 4 client error, 5 server error.
The first digit gives the family. 2xx means success, 3xx means look elsewhere, 4xx means the client made a mistake, and 5xx means the server failed.
from http import HTTPStatus
for code in (200, 201, 301, 404, 500): status = HTTPStatus(code) print(code, status.phrase)Example 3 · Status code families
from http import HTTPStatus
names = {2: "success", 3: "redirect", 4: "client error", 5: "server error"}for code in (200, 204, 301, 404, 429, 500): status = HTTPStatus(code) print(code, status.phrase, "->", names[code // 100])200 OK -> success 204 No Content -> success 301 Moved Permanently -> redirect 404 Not Found -> client error 429 Too Many Requests -> client error 500 Internal Server Error -> server error
What this code does
HTTPStatusgives the official text for any code.- Integer division by 100 returns the family digit.
The standard library knows every code and its official phrase. Use it instead of typing numbers from memory.
The codes you will use most
Learn a short list well: 200 OK, 201 Created, 204 No Content, 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 409 Conflict, 422 Unprocessable Content and 500 Internal Server Error.
from http import HTTPStatus
print(HTTPStatus.CREATED.value)print(HTTPStatus.NOT_FOUND.value)print(HTTPStatus(401).name)Example 4 · Methods and codes in a tiny FastAPI app
from fastapi import FastAPI, HTTPExceptionfrom fastapi.testclient import TestClientfrom pydantic import BaseModel
app = FastAPI()products = {1: "Keyboard"}
class NewProduct(BaseModel): name: str
@app.get("/products/{pid}")def read(pid: int): if pid not in products: raise HTTPException(status_code=404, detail="not found") return {"id": pid, "name": products[pid]}
@app.post("/products", status_code=201)def create(item: NewProduct): pid = max(products) + 1 products[pid] = item.name return {"id": pid, "name": item.name}
@app.delete("/products/{pid}", status_code=204)def remove(pid: int): products.pop(pid, None)
client = TestClient(app)print(client.get("/products/1").status_code)print(client.get("/products/9").status_code)print(client.post("/products", json={"name": "Mouse"}).status_code)print(client.post("/products", json={"price": 5}).status_code)print(client.delete("/products/2").status_code)print(client.put("/products/1").status_code)200 404 201 422 204 405
What this code does
- Each route sets its own success code: 200 by default, 201 for create, 204 for delete.
- A missing item gives 404, bad input gives 422, and a method the route does not support gives 405.
The word "Unauthorized" in 401 really means "not logged in". "Forbidden" in 403 means "logged in, but not allowed".
Next step
Check your understanding
12 quick questions