Skip to main content

Working with data: JSON and your first API idea

This is the bridge lesson — the one that connects everything you've learned to the guides above. Almost every modern program talks to other programs: a weather app asks a weather service, an AI app asks a model, a store asks a payment service. They exchange data, and the language they overwhelmingly use to exchange it is JSON. If you can read and build JSON and navigate the nested data inside it, the AI and web guides open up to you. That's this lesson.

What JSON is

JSON stands for JavaScript Object Notation, but don't let the name fool you — it has nothing to do with JavaScript anymore. It is a simple text format for representing data: lists, key/value pairs, strings, numbers, and booleans, written as plain text that any language can read and write. Here's a piece of JSON:

{
"name": "Ada",
"age": 30,
"active": true,
"languages": ["Python", "JavaScript"]
}

Look closely — it is almost exactly a Python dictionary. That's the happy secret of this lesson: JSON maps cleanly onto the Python types you already know.

JSONPython
object { }dict
array [ ]list
string "..."str
number 30, 3.14int / float
true / falseTrue / False
nullNone

The differences are tiny and worth memorizing: JSON keys must be in double quotes (never single), and JSON uses lowercase true/false/null where Python uses True/False/None. The json module handles that translation for you.

json.loads and json.dumps

JSON arrives as text (a string). To work with it you turn it into Python objects; to send it back out you turn Python objects into text. Two functions from the standard-library json module do exactly that:

  • json.loads(text)loads from a string: JSON text → Python objects. (Remember it as "loads = load string.")
  • json.dumps(obj)dumps to a string: Python objects → JSON text. (Remember it as "dumps = dump string.")
import json

# JSON text (note the double quotes, and true not True)
text = '{"name": "Ada", "age": 30, "active": true}'

data = json.loads(text) # parse the text into a real Python dict
print(type(data)) # <class 'dict'>
print(data["name"]) # Ada — index it like any dict
print(data["age"] + 1) # 31 — it's a real int, do math with it

# now the other direction
person = {"name": "Lin", "age": 25}
out = json.dumps(person) # Python dict -> JSON text
print(out) # {"name": "Lin", "age": 25}
print(type(out)) # <class 'str'>

The crucial mental shift: json.loads gives you back ordinary dicts and lists — everything you learned in those lessons applies directly. Once parsed, JSON data is just Python data.

:::tip Pretty-printing with indent json.dumps(data, indent=2) adds line breaks and indentation, turning a cramped one-liner into the readable, nested form you saw at the top of this lesson. Great for printing data while you're figuring out its shape. :::

Real JSON is nested — dicts inside lists inside dicts. This is where beginners stall, so let's make it mechanical. The rule: use [ ] to step in one level at a time — a string key to enter a dict, an integer index to enter a list — and just keep stepping until you reach the value you want.

import json

text = '''
{
"city": "Lagos",
"users": [
{"name": "Ada", "scores": [90, 85]},
{"name": "Lin", "scores": [75, 95]}
]
}
'''

data = json.loads(text)

print(data["city"]) # Lagos — top-level dict key
print(data["users"]) # the whole list of user dicts
print(data["users"][0]) # {'name': 'Ada', 'scores': [90, 85]} — first user
print(data["users"][0]["name"]) # Ada — that user's name
print(data["users"][1]["scores"][0]) # 75 — second user's first score

Read data["users"][1]["scores"][0] left to right as a path: in data, go to users (a list), take item 1 (a dict), go to scores (a list), take item 0. Each bracket is one step deeper. Build these paths one bracket at a time, printing as you go, and nested data stops being scary.

Looping over a list of records is the most common real task — pull one field from every item:

for user in data["users"]:
print(user["name"], "->", user["scores"])
# Ada -> [90, 85]
# Lin -> [75, 95]

# or with a comprehension (you learned these!) — just the names
names = [user["name"] for user in data["users"]]
print(names) # ['Ada', 'Lin']

:::warning .get() avoids KeyError on missing keys If a key might be absent, data["maybe"] raises a KeyError. Use data.get("maybe") to get None instead of a crash, or data.get("maybe", "default") to supply a fallback. Real-world JSON often has missing or optional fields, so this habit saves you constantly. :::

Where the JSON comes from: an API, conceptually

So far we typed the JSON ourselves. In real programs it arrives from an API. An API (Application Programming Interface) is, for our purposes, a web address you send a request to, that sends data back — usually as JSON. You ask a weather API for "London," it replies with a JSON blob of temperature and conditions. You ask an AI API a question, it replies with JSON containing the answer.

The most common way to talk to one is an HTTP request — HTTP is the protocol your browser already uses to fetch web pages. The popular requests package (the one you saw pip installed two lessons ago) makes it a one-liner:

import requests

# ILLUSTRATION ONLY — this hits a real server over the network
response = requests.get("https://api.example.com/users")
data = response.json() # parse the JSON body into Python objects

# from here it's exactly what you just practiced:
for user in data["users"]:
print(user["name"])

Three things to notice, because this is the whole pattern the next guides use:

  1. requests.get(url) sends an HTTP GET request (GET = "fetch me this") and hands back a response object — an instance of a class, exactly the kind of object you learned to read last lesson.
  2. response.json() is a method on that object that parses the JSON body for you (it's doing a json.loads under the hood) and returns ordinary Python dicts and lists.
  3. From there, it's just nested-data navigation — the data["users"][0]["name"] skill you already have. The API was just a fancy way to get the JSON; once you have it, nothing is new.

:::note Why this stays illustrative here The runnable challenges on this site execute in your browser's Python sandbox, which can't reach the live network — so a real requests.get(...) won't run here. That's fine: the network call is the easy part you'll do for real once Python is on your machine (next lesson). The skill that matters — turning a JSON response into Python data and navigating it — you can practice fully right now on JSON strings, which is exactly what the challenge below does. :::

Why it matters

JSON is the lingua franca between programs, and an API is how your code reaches out to the rest of the software world. The Modern AI guide is, at its core, send a request to a model's API, get JSON back, pull the answer out of it. The Modern Web Dev guide serves JSON from APIs and consumes it in the browser. Every bit of that rests on what you just did: parse JSON into dicts and lists, and walk the nesting to the value you need. You've reached the doorway of the next guides — and you can already read what's on the other side.

Where this leads: calling APIs and parsing their JSON is the daily work of AI and web engineering — this is the exact on-ramp. See Where this leads next.

Practice

This challenge operates on a JSON string (mock API data), so it runs right here — no network needed. Parse it and pull out the data:

⌨️ Challenge — Python · practice (not graded)

Write top_scorer(text) that takes a JSON string shaped like {"players": [{"name": "Ada", "score": 90}, ...]} and returns the NAME (a string) of the player with the highest score. Use json.loads to parse it, then loop the players list. Assume at least one player and no ties.

Checkpoint

Required checkpoint

Working with data: JSON and APIs

Pass to unlock the Next button below

Next: Run Python on your own machine →