Python Networking — requests, urllib & sockets
Networking in Python spans two levels: high-level HTTP (talking to web APIs and sites) and low-level sockets (raw TCP/UDP connections). Most apps live at the HTTP level.
| Tool | Level | Notes |
|---|---|---|
requestsrequests | High (HTTP) | Third-party, the de-facto standard. pip install requestspip install requests. |
urllib.requesturllib.request | High (HTTP) | Built in; more verbose, no install needed. |
urllib.parseurllib.parse | Helper | Build and parse URLs (pure, no network). |
socketsocket | Low (TCP/UDP) | Raw connections; build your own protocols. |
requests — the friendly HTTP client
requestsrequests makes HTTP calls readable. Install it first (pip install requestspip install requests).
import requests
resp = requests.get("https://api.example.com/users", params={"page": 2})
print(resp.status_code) # 200
print(resp.headers["Content-Type"])
data = resp.json() # parse a JSON body into Python
print(data)import requests
resp = requests.get("https://api.example.com/users", params={"page": 2})
print(resp.status_code) # 200
print(resp.headers["Content-Type"])
data = resp.json() # parse a JSON body into Python
print(data)import requests
# Send JSON in the body
resp = requests.post(
"https://api.example.com/users",
json={"name": "Ada", "role": "admin"},
headers={"Authorization": "Bearer TOKEN"},
timeout=10,
)
resp.raise_for_status() # raise if status is 4xx/5xx
print(resp.json())import requests
# Send JSON in the body
resp = requests.post(
"https://api.example.com/users",
json={"name": "Ada", "role": "admin"},
headers={"Authorization": "Bearer TOKEN"},
timeout=10,
)
resp.raise_for_status() # raise if status is 4xx/5xx
print(resp.json())requestsrequests piece | Purpose |
|---|---|
requests.get/post/put/deleterequests.get/post/put/delete | The HTTP verbs. |
params={...}params={...} | Query-string parameters. |
json={...}json={...} | Send a JSON body. |
headers={...}headers={...} | Custom request headers. |
resp.status_coderesp.status_code | The HTTP status (200, 404, …). |
resp.json()resp.json() / resp.textresp.text | Parsed JSON / raw text. |
resp.raise_for_status()resp.raise_for_status() | Turn error statuses into exceptions. |
timeout=timeout= | Fail fast instead of hanging forever. |
urllib — the standard-library option
When you can’t add dependencies, urllib.requesturllib.request does the same job with more ceremony.
import urllib.request
import json
req = urllib.request.Request(
"https://api.example.com/data",
headers={"User-Agent": "my-app"},
)
with urllib.request.urlopen(req, timeout=10) as resp:
body = resp.read().decode("utf-8")
data = json.loads(body)
print(data)import urllib.request
import json
req = urllib.request.Request(
"https://api.example.com/data",
headers={"User-Agent": "my-app"},
)
with urllib.request.urlopen(req, timeout=10) as resp:
body = resp.read().decode("utf-8")
data = json.loads(body)
print(data)urllib.parse — build and parse URLs
This part of urlliburllib is pure (no network) and extremely useful for assembling query strings and dissecting URLs.
from urllib.parse import urlencode, urlparse, parse_qs
# Build a query string from a dict
qs = urlencode({"q": "python", "page": 2})
print(qs) # q=python&page=2
# Dissect a URL
parts = urlparse("https://example.com/search?q=python&page=2")
print(parts.scheme) # https
print(parts.netloc) # example.com
print(parts.path) # /search
print(parse_qs(parts.query)) # {'q': ['python'], 'page': ['2']}from urllib.parse import urlencode, urlparse, parse_qs
# Build a query string from a dict
qs = urlencode({"q": "python", "page": 2})
print(qs) # q=python&page=2
# Dissect a URL
parts = urlparse("https://example.com/search?q=python&page=2")
print(parts.scheme) # https
print(parts.netloc) # example.com
print(parts.path) # /search
print(parse_qs(parts.query)) # {'q': ['python'], 'page': ['2']}socket — low-level TCP
For custom protocols or learning how the network really works, use raw sockets. Here’s a minimal echo server and client.
import socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("127.0.0.1", 9000))
server.listen()
print("listening on 9000")
conn, addr = server.accept() # blocks until a client connects
with conn:
data = conn.recv(1024) # read up to 1024 bytes
conn.sendall(data) # echo it backimport socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("127.0.0.1", 9000))
server.listen()
print("listening on 9000")
conn, addr = server.accept() # blocks until a client connects
with conn:
data = conn.recv(1024) # read up to 1024 bytes
conn.sendall(data) # echo it backimport socket
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect(("127.0.0.1", 9000))
s.sendall(b"hello")
reply = s.recv(1024)
print(reply) # b'hello'import socket
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect(("127.0.0.1", 9000))
s.sendall(b"hello")
reply = s.recv(1024)
print(reply) # b'hello'| socket call | Purpose |
|---|---|
socket(AF_INET, SOCK_STREAM)socket(AF_INET, SOCK_STREAM) | Create a TCP socket. |
bind((host, port))bind((host, port)) | Attach a server to an address. |
listen()listen() / accept()accept() | Wait for and accept a connection. |
connect((host, port))connect((host, port)) | Client connects to a server. |
sendall(bytes)sendall(bytes) / recv(n)recv(n) | Send / receive raw bytes. |
Sockets speak bytes, not strings — encode with
.encode().encode()and decode with.decode().decode().
Choosing a level
Talking to a web API or website? -> requests (or urllib if no installs)
Just building/parsing a URL? -> urllib.parse
Custom protocol / raw TCP/UDP? -> socketTalking to a web API or website? -> requests (or urllib if no installs)
Just building/parsing a URL? -> urllib.parse
Custom protocol / raw TCP/UDP? -> socketCommon pitfalls
- No timeout — network calls can hang forever; always pass
timeout=timeout=. - Ignoring status codes — check
resp.status_coderesp.status_codeor callraise_for_status()raise_for_status(). - Sending strings over sockets — sockets need bytes; encode/decode explicitly.
requestsrequestsisn’t built in — it needspip install requestspip install requests;urlliburllibdoes not.
Practice Exercises
These exercises use urllib.parseurllib.parse, which runs without any network access.
Exercise 1 – Build a query string
Exercise 2 – Extract the host from a URL
Exercise 3 – Parse query parameters
Summary
- For HTTP, reach for
requestsrequests(clean API) orurllib.requesturllib.request(built in, verbose). - Always set a
timeouttimeoutand check the status code. urllib.parseurllib.parsebuilds and dissects URLs withurlencodeurlencode,urlparseurlparse, andparse_qsparse_qs.socketsocketgives raw TCP/UDP for custom protocols — remember it speaks bytes.- Pick the level that matches the task: APIs → requests, URLs → urllib.parse, raw connections → socket.
If this helped you, consider buying me a coffee ☕
Buy me a coffeeWas this page helpful?
Let us know how we did
