Contact
Evil Genius SocietyHomeLearnForumLeaderboardSocialsMessages
← Back to module

Lesson 2 of 8

response.json() hands you an ordinary Python dictionary or list. From there it's all Module 3 skills — but API data is messier than data you built yourself, so a few habits matter.

import requests

response = requests.get("https://api.github.com/users/python")
data = response.json()

print(type(data))
print(data["name"])
print(data["public_repos"])

.json() versus json.loads()

response.json() is a convenience for json.loads(response.text). Same result; use .json() with requests.

It raises if the body isn't valid JSON — which happens when a server returns an HTML error page instead of data. Lesson 8.5 handles that.

Real responses nest several levels deep. Read the path left to right, exactly as in lesson 3.8:

import json

body = '''{
  "count": 2,
  "results": [
    {"name": "Ada", "address": {"city": "London"}},
    {"name": "Grace", "address": {"city": "New York"}}
  ]
}'''

data = json.loads(body)

print(data["count"])
print(data["results"][0]["name"])
print(data["results"][0]["address"]["city"])

for person in data["results"]:
    print(f"{person['name']} - {person['address']['city']}")

Missing keys are the norm

This is the big difference from your own data: an API can omit a field at any time. Optional fields are missing rather than null, and APIs change without warning.

import json

data = json.loads('{"name": "Ada"}')

print(data["email"])

That raises KeyError. Use .get() with a default instead — the lesson 3.5 habit, now essential:

import json

data = json.loads('{"name": "Ada"}')

print(data.get("email"))
print(data.get("email", "no email provided"))

For nested paths, chain carefully. data.get("address", {}).get("city") works because the fallback {} still has a .get():

import json

with_address = json.loads('{"address": {"city": "London"}}')
without = json.loads('{"name": "Ada"}')

print(with_address.get("address", {}).get("city", "unknown"))
print(without.get("address", {}).get("city", "unknown"))

Extracting what you need

Don't pass raw API responses around your program. Pull out the fields you care about into your own shape — then an API change breaks in one place instead of everywhere:

import json

body = '''{"results": [
  {"name": "Ada", "public_repos": 8, "extra": "ignore me"},
  {"name": "Grace", "public_repos": 3, "extra": "ignore me"}
]}'''

raw = json.loads(body)

people = [
    {"name": r["name"], "repos": r.get("public_repos", 0)}
    for r in raw["results"]
]

print(people)
print(sum([p["repos"] for p in people]))

Exploring an unfamiliar response

When you don't know the shape yet, print the keys first:

import json

data = json.loads('{"name": "Ada", "id": 1, "tags": ["python"]}')

print(sorted(data.keys()))
print(json.dumps(data, indent=2))

json.dumps(data, indent=2) pretty-prints it — the fastest way to see the structure.

Try it yourself

Parse a nested response, handle a missing field, and reshape it.

Checkpoint

  • response.json() returns plain Python dicts and lists.
  • Navigate nested data left to right, as with any nested structure.
  • Assume fields can be missing — prefer .get(key, default) over data[key].
  • Chain .get("a", {}).get("b") for nested optional paths.
  • Extract into your own shape so an API change breaks one place, not many.

Questions about this lesson

Log in to ask a question.