Practical guide · verified against the real thing
How to call an API with Python (standard library only)
In one line: urllib.request from first principles: headers that matter, timeouts that save you, query parameters that don't get mangled, and where the requests library fits.
You do not need a third-party library to call an API from Python — the standard library's urllib.request does the whole job, and learning it first means you understand what every fancier library is doing for you. This piece is the working pattern: build a request, set the headers that matter, always set a timeout, read the response.
The minimal real call
import json
import urllib.request
url = "https://api.github.com/zen"
req = urllib.request.Request(url, headers={"User-Agent": "bryme-demo/1.0"})
with urllib.request.urlopen(req, timeout=10) as resp:
print("Status:", resp.status)
body = resp.read().decode("utf-8")
print(body)
Three deliberate details. The User-Agent header: many APIs reject or throttle default Python clients, and a named, honest identifier is both polite and required by some providers. The timeout: without it, a stuck server can hang your program indefinitely — ten seconds is a sane default for most APIs. And the context manager (with): it closes the connection deterministically instead of leaving cleanup to luck. The response body arrives as bytes; .decode("utf-8") turns it into text, and if the endpoint promises JSON, json.loads on that text gives you data structures — the full move is in parsing JSON in Python.
Query parameters without the string surgery
Hand-building URLs with f-strings breaks the first time a value contains a space or an ampersand. urlencode does the escaping correctly:
from urllib.parse import urlencode
import json
import urllib.request
params = urlencode({"q": "json parsing", "per_page": "3"})
url = "https://api.github.com/search/repositories?" + params
req = urllib.request.Request(url, headers={"User-Agent": "bryme-demo/1.0"})
with urllib.request.urlopen(req, timeout=10) as resp:
results = json.loads(resp.read().decode("utf-8"))
for item in results["items"]:
print(item["full_name"])
This is also where URL-encoding stops being trivia: urlencode percent-encodes every value the way the server expects — the same transformation as the URL encoder tool.
Where requests fits
The third-party requests library is the community favourite, and for good reason: requests.get(url, params=..., timeout=10) compresses the ceremony while keeping the same shape — headers, parameters, timeouts, then .json() on the response. Install it with pip install requests when a project justifies a dependency. Everything you learned with urllib transfers directly, including the part that matters most when calls go wrong: the error-handling ladder.
Sources
Next