Calling an API with requests
An API lets your program fetch data from somewhere else over the web. The everyday tool for this in Python is the requests library — not part of the stdlib, but so standard it may as well be (pip install requests).
requests.get(url) makes an HTTP GET request and hands back a response object. The three things you almost always want from it:
.status_code— the HTTP result code. 200 means OK; 404 not found; 401/403 unauthorised; 500 server error..json()— parses a JSON response body into a Python dict/list (it callsjson.loadsfor you)..text— the raw body as a string, when it isn't JSON.
Query parameters go in the params= argument as a dict — requests builds the ?key=value&... part of the URL for you, correctly escaped. Always check the status before trusting the body: a 404 page is still "a response", just not the data you wanted. response.raise_for_status() turns any 4xx/5xx into an exception in one line.
*(In the Rungo lesson the network is stubbed — a local function returns the same shape of dict — so you write the exact same code against response['status_code'] and response['temp_c']. The real-world API is shown here.)*
Syntax
import requests
r = requests.get("https://api.example.com/weather",
params={"city": "London", "units": "metric"})
r.status_code # 200, 404, ...
r.json() # parse JSON body → dict / list
r.text # raw body as a string
r.raise_for_status() # raise if 4xx/5xx, else do nothingWorked examples
A basic GET with query params
import requests
r = requests.get(
'https://api.example.com/weather',
params={'city': 'London', 'units': 'metric'},
)
# requests builds: .../weather?city=London&units=metric
if r.status_code == 200:
data = r.json()
print(data['temp_c'])
else:
print(f'Request failed: {r.status_code}')raise_for_status — fail fast on a bad response
import requests
r = requests.get('https://api.example.com/data')
r.raise_for_status() # raises requests.HTTPError on 4xx/5xx
data = r.json() # only runs if the status was OK
print(data)Using the parsed dict (the shape the lesson stubs)
# The stub returns a dict shaped like a real .json() body:
response = {'city': 'London', 'temp_c': 18,
'conditions': 'cloudy', 'status_code': 200}
def summary(response):
return f"{response['city']}: {response['temp_c']}\u00b0C, {response['conditions']}"
print(summary(response))
# Output:
# London: 18\u00b0C, cloudyCommon mistakes & gotchas
A response is not the same as a success
requests.get returns a response object even for a 404 or 500 — it doesn't raise on its own. If you call .json() and use the data without checking .status_code (or calling .raise_for_status()), you'll happily process an error page as if it were real data.
.json() raises if the body isn't JSON
Calling .json() on an HTML error page or empty body throws a JSONDecodeError. Check the status code first, and reach for .text when you're not sure the response is JSON.
Build params with params=, don't glue the URL
Hand-concatenating url + "?city=" + city breaks on spaces and special characters. Pass params={"city": city} and requests escapes everything correctly. Same lesson as pathlib: let the library build the string.
Why it matters
Almost no useful backend lives alone — it talks to weather services, payment gateways, mapping APIs, other microservices. `requests.get` and reading a status code are the bread and butter of that conversation, and knowing to *check the status before trusting the body* is what separates code that works from code that works until the API hiccups.