The Server Calls You
A Python web server (the standard http.server module) is built from handler classes: subclass BaseHTTPRequestHandler and write a do_GET method. The server accepts connections and calls your method once per request — self.path and self.headers hold the request, and send_response, send_header and self.wfile build the response.
A real server calls serve_forever() on a fixed port and runs forever. The examples here pick port 0 — a random free port — and run serve_forever in a background thread, so every example starts its server, uses it and finishes by itself. They also override log_message with an empty method: the base class prints a timestamped log line for every request, and the demos want clean, stable output.
A First Handler
The smallest possible handler writes a line of text. Everything an API does — JSON, status codes, routing — happens inside this same method shape.
Hello from a handler
The handler writes into self.wfile; the client reads exactly that as the response body, with the status line chosen by send_response(200).
Output: complete Python program
import threading
import urllib.request
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
class HelloHandler(BaseHTTPRequestHandler):
# The server calls this method once per GET request.
def do_GET(self):
self.send_response(200)
self.send_header("Content-Type", "text/plain")
self.end_headers()
self.wfile.write(b"hello from the server\n")
def log_message(self, fmt, *args):
pass # no request log lines in the demo output
server = ThreadingHTTPServer(("127.0.0.1", 0), HelloHandler)
port = server.server_address[1]
threading.Thread(target=server.serve_forever, daemon=True).start()
with urllib.request.urlopen(f"http://127.0.0.1:{port}/") as resp:
print("status:", resp.status, resp.reason)
print("body: ", resp.read().decode(), end="")
server.shutdown()
Answering with JSON
An API handler does three things: choose a status code, set the Content-Type header, and write the encoded data — json.dumps builds the text, self.wfile.write sends the bytes.
Order matters: send_response first, then the headers, then end_headers, and only then the body.
A JSON endpoint
The handler dumps a dict straight into the response. The client receives valid JSON and the right content type — this is the skeleton of every endpoint in the rest of this group.
Output: complete Python program
import json
import threading
import urllib.request
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
class UserHandler(BaseHTTPRequestHandler):
def do_GET(self):
body = json.dumps({"id": 1, "name": "Ana"}) + "\n"
self.send_response(200) # status first
self.send_header("Content-Type", "application/json")
self.end_headers() # headers closed
self.wfile.write(body.encode()) # then the body
def log_message(self, fmt, *args):
pass
server = ThreadingHTTPServer(("127.0.0.1", 0), UserHandler)
port = server.server_address[1]
threading.Thread(target=server.serve_forever, daemon=True).start()
with urllib.request.urlopen(f"http://127.0.0.1:{port}/user") as resp:
print("status:", resp.status, resp.reason)
print("type: ", resp.headers["Content-Type"])
print("body: ", resp.read().decode(), end="")
server.shutdown()