How the Web Works

Expert · 18 min read · ▶ live playground · ✦ checkpoint

Every web page, mobile app, and Python service that talks over the internet is built on the same small conversation: ask for something, get an answer back. Once you can read that conversation, APIs become clear too: an API is a program boundary that lets software ask other software for work or data.

This page does not contact the internet. Your browser Python runtime is offline, so you will inspect canned web messages and JSON, a text data format for structured values, in the same shape real web traffic uses.

The web conversation

A client is a program that sends a message asking for something, such as a browser, mobile app, or Python script. A server is a program that waits for those messages, does work, and sends answers back.

A resource is a named piece of data or behavior, like one book record, a reading-log list, or a checkout action. A request is the message a client sends to ask for a resource or action. A response is the message the server sends back.

A protocol is an agreed set of message rules, and HTTP is the protocol most web clients and servers use for requests and responses. Think of HTTP like an order slip at a cafe: the customer writes the action, the counter, a few notes, and sometimes the main content; the cafe sends back a tray with a status label and the result.

A URL is the address of a web resource, and the path is the part after the server name that names the resource on that server. A query string is the optional ?name=value part after the path that carries filters or search values. A method is the HTTP action word, such as GET for reading data. A header is a named extra fact about the message, like "I accept JSON." A body is the optional main content carried inside a request or response.

raw_request = """GET /api/books?author=Mina HTTP/1.1
Host: api.bookclub.test
Accept: application/json
User-Agent: Python learner
"""
 
request_line, *header_lines = raw_request.strip().splitlines()
method, target, protocol = request_line.split()
path, query_string = target.split("?", 1)
 
headers = {}
for line in header_lines:
    name, value = line.split(": ", 1)
    headers[name.lower()] = value
 
print("Method:", method)
print("Path:", path)
print("Query:", query_string)
print("Protocol:", protocol)
print("Accepts:", headers["accept"])

Method: GET
Path: /api/books
Query: author=Mina
Protocol: HTTP/1.1
Accepts: application/json

Those pieces are boring in the best way. A server does not guess your intent from vibes. It reads the method, path, headers, and body, then decides what response to send.

Status codes

A status code is a three-digit number in an HTTP response that summarizes what happened. It is the stamp on the cafe tray: before you inspect the food, you know whether the order worked, needs correction, or hit a kitchen problem.

Status codes come in families:

  • 1xx means informational/provisional, a temporary "keep going" note before the final response.
  • 2xx means success, such as 200 OK for a normal response or 201 Created after creating something.
  • 3xx means redirection, where the client may need a different URL.
  • 4xx means the client sent a request the server cannot or will not satisfy, such as 400 Bad Request, 401 Unauthorized, 403 Forbidden, or 404 Not Found.
  • 5xx means the server failed while trying to handle a valid-looking request.
def explain_status(code):
    if 100 <= code < 200:
        return "informational/provisional"
    if 200 <= code < 300:
        return "success"
    if 300 <= code < 400:
        return "redirect"
    if 400 <= code < 500:
        return "client-side problem"
    if 500 <= code < 600:
        return "server-side problem"
    return "unusual status"
 
responses = [
    (100, "Continue"),
    (200, "OK"),
    (201, "Created"),
    (404, "Not Found"),
    (500, "Internal Server Error"),
]
 
for code, phrase in responses:
    print(f"{code} {phrase}: {explain_status(code)}")

100 Continue: informational/provisional
200 OK: success
201 Created: success
404 Not Found: client-side problem
500 Internal Server Error: server-side problem

The status code does not replace the response body. It gives the first, quick reading. The body can carry page text, JSON data, or an error explanation.

raw_response = """HTTP/1.1 404 Not Found
Content-Type: application/json
 
{"error": "book not found", "requested_id": 42}
"""
 
header_text, body_text = raw_response.split("\n\n", 1)
status_line, content_type_line = header_text.splitlines()
protocol, status_text, reason = status_line.split(" ", 2)
status_code = int(status_text)
 
print("Protocol:", protocol)
print("Status:", status_code)
print("Reason:", reason)
print("Body:", body_text.strip())

Protocol: HTTP/1.1
Status: 404
Reason: Not Found
Body: {"error": "book not found", "requested_id": 42}

REST as an API shape

An API is a documented program boundary. REST is an API design style that treats application data as named resources and uses HTTP methods to work with those resources.

The concrete analogy: REST is like a library desk with labeled shelves and action stamps. /books/42 names the shelf card; GET, POST, PATCH, and DELETE tell the clerk what kind of action you want.

An endpoint is a specific method plus URL path that an API agrees to handle. REST APIs usually make endpoint names nouns, not verbs, because the method already carries the action.

routes = [
    ("GET", "/books", "list books"),
    ("GET", "/books/42", "read one book"),
    ("POST", "/books", "create a book"),
    ("PATCH", "/books/42", "update part of a book"),
    ("DELETE", "/books/42", "remove a book"),
]
 
for method, path, meaning in routes:
    print(f"{method:<6} {path:<12} -> {meaning}")

GET /books -> list books
GET /books/42 -> read one book
POST /books -> create a book
PATCH /books/42 -> update part of a book
DELETE /books/42 -> remove a book

REST is a style, not a Python package. In lesson 23.2, you will be the client consuming APIs. In lesson 23.3, you will see how Python can be the server that defines endpoints.

JSON, the API data format

JSON is a text data format for sending structured values between programs. It is the packing slip both sides can read: strings, numbers, booleans, empty values, lists, and key-value records written in one predictable text shape.

A JSON object is a key-value collection that maps naturally to a Python dictionary. A JSON array is an ordered collection that maps naturally to a Python list. JSON is not Python, though: JSON uses double-quoted strings, true, false, and null; Python turns those into True, False, and None when you parse it.

import json
 
response_body = """
{
  "book": {
    "id": 42,
    "title": "Clean APIs",
    "available": true,
    "tags": ["web", "python"]
  },
  "borrower": null
}
"""
 
payload = json.loads(response_body)
book = payload["book"]
 
print(f"{book['title']} is tagged: {', '.join(book['tags'])}")
print("Available?", book["available"])
print("Borrower:", payload["borrower"])
print("Python type:", type(payload).__name__)

Clean APIs is tagged: web, python
Available? True
Borrower: None
Python type: dict

That json.loads() call is the handoff point. Before it runs, you have text. After it runs, you have normal Python objects: dictionaries, lists, strings, numbers, booleans, and None.

Try it - read a whole API response

This playground parses one complete canned response: first line, headers, and JSON body. Change the status code to 500, remove one item, or edit the next_page value and run it again.

python — playgroundlive
⌘/Ctrl + Enter to run

When a real Python client calls an API, the networking library handles the actual internet exchange. Your job is still this: check the status, read the headers that matter, parse the JSON body, and then work with ordinary Python data.

Checkpoint

Answer all three to mark this lesson complete

You now have the web conversation map: HTTP request in, HTTP response out, REST for naming actions, and JSON for carrying structured data. Next, you will use that map as a Python client: consuming APIs, sending headers, handling pages of results, and scraping responsibly when an API is not available.

+50 XP on completion