Python Parsing JSON Responses
Table of Contents + β
In the last lesson you learned API Authentication. Now you can prove who you are and reach an API. But once the API answers, you get back a wall of text. That text is almost always JSON. The thing is, raw JSON text is hard to work with directly. This lesson shows you how to turn that reply into normal Python dicts and lists. Then you pull out exactly the fields you want.
π€ Why Parse JSON At All?
When you call an API, the reply comes back as one long string. Something like this:
{"name": "Alex", "age": 29, "city": "Berlin"}To your program, that whole thing is just text. You cannot do reply["name"] on a string. Python would not know that "name" is supposed to be a key. So you would be stuck slicing characters by hand. That is painful. And it breaks the moment the data changes.
Parsing means turning that text into real Python objects you can read. Once it is parsed, that JSON becomes a normal Python dictionary. Then reply["name"] gives you "Alex" straight away.
π§© JSON and Python Speak the Same Shape
Here is the nice part. JSON looks almost exactly like Python. A JSON object becomes a Python dict. A JSON array becomes a Python list. The values map across cleanly too.
| JSON | Python after parsing |
|---|---|
object { } | dict |
array [ ] | list |
| string | str |
| number | int or float |
| true / false | True / False |
| null | None |
So once you parse, you already know how to read it. It is just dicts and lists. The same ones you have used all along.
π οΈ response.json() Does The Work
When you use the requests library, the response object has a handy method called .json(). It reads the reply text and parses it into Python for you in one step. No manual work needed.
Think of it like a translator at an airport. The announcement comes in a language you do not speak. The translator listens and hands you the same message in your own language. response.json() is that translator. Raw text goes in, a Python dict or list comes out.
Here is a small real call. We use a public test API called JSONPlaceholder that returns fake-but-realistic data, so you can run this yourself right now.
import requests
response = requests.get("https://jsonplaceholder.typicode.com/users/1")data = response.json() # parse the JSON text into a Python dict
print(type(data))print(data["name"])print(data["email"])Let us read it line by line:
requests.get(...)calls the API and gives back a response object.response.json()parses the reply and returns a Python dict, which we store indata.type(data)confirms we really got adict, not a string.data["name"]anddata["email"]read fields straight out of that dict.
Output
<class 'dict'>Leanne GrahamSincere@april.bizNote
This is example output. The site can change its sample data at any time, so the exact name or email you see might differ. The shape stays the same though.
Notice we never touched the raw text. .json() handled all of it.
π₯ Reading Nested Fields Safely
Real API data is rarely flat. Often a dict holds another dict inside it. For our user, the address sits inside its own object, and the city sits inside the address.
Reading deep like this works, but it is risky:
city = data["address"]["city"]print(city)Output
GwenboroughThat is fine when every key exists. The problem is what happens when a key is missing. Say the API leaves out address for some users. Then data["address"] blows up with a KeyError and your whole program crashes.
# β Avoid: crashes if "phone_number" is not in the datanumber = data["phone_number"]Output
Traceback (most recent call last): File "main.py", line 1, in <module>KeyError: 'phone_number'The safe way is the dictβs .get() method. Instead of crashing on a missing key, .get() returns None. You can also pass a fallback value to return instead.
# β
Good: returns None or your fallback if the key is missingnumber = data.get("phone_number")print(number)
email = data.get("email", "no email on file")print(email)Output
NoneSincere@april.bizFor nested data, chain .get() carefully. Use an empty dict as the fallback so the next .get() still has something to call:
# β
Good: stays safe even if "company" is missingcompany_name = data.get("company", {}).get("name", "unknown")print(company_name)Output
Romaguera-CronaSee how that reads? If company is missing, data.get("company", {}) gives back an empty dict. Then .get("name", "unknown") runs on that empty dict and quietly returns "unknown". No crash anywhere.
Tip
Use square brackets data["key"] only when you are sure the key is always there. The moment a key might be missing, switch to .get() with a sensible fallback.
π Looping Over A List Of Results
Lots of APIs do not return one item. They return a list of them. A search for users, a feed of posts, a page of products. The parsed result is then a Python list, and each item in it is a dict.
This call asks for all the users, not just one. So .json() gives back a list. We loop over it and pull one field from each:
import requests
response = requests.get("https://jsonplaceholder.typicode.com/users")users = response.json() # this time it is a list of dicts
print(f"Got {len(users)} users")
for user in users: name = user.get("name", "no name") city = user.get("address", {}).get("city", "unknown") print(f"{name} lives in {city}")Walking through it:
response.json()returns a list because the API sent back an array.len(users)tells us how many came back.- The
forloop visits eachuser, which is itself a dict. - Inside, we use
.get()so one odd record with a missing field does not stop the whole loop.
Output
Got 10 usersLeanne Graham lives in GwenboroughErvin Howell lives in WisokyburghClementine Bauch lives in McKenziehavenPatricia Lebsack lives in South ElvisChelsey Dietrich lives in Roscoeview...Note
That is representative output. The list is trimmed here for space, and the sample data on the live site may differ. Your real run will print every user the API returns.
This pattern covers most of your day-to-day API work. Parse the reply. Check whether it is one dict or a list of them. Then read the fields you care about with .get().
β οΈ Common Mistakes
A few traps catch people again and again:
- Treating the response as text.
response.textgives you the raw string. To get Python objects you needresponse.json(). - Using
data["key"]on a key that might be missing. One absent field and the program crashes with aKeyError. Reach for.get()instead. - Forgetting that
.json()can return a list, not a dict. If the API sends an array, you must loop, not index by key. - Chaining
.get("a").get("b")without a fallback. Ifais missing, the first.get()returnsNone, and calling.get()onNonecrashes. Pass{}as the fallback. - Calling
.json()on a reply that is not JSON at all, like an HTML error page. That raises a parsing error, which the next lesson handles.
β Best Practices
Small habits that keep your code calm:
- Parse once. Call
response.json()a single time and store the result in a variable. Do not call it over and over. - Default to
.get()for any field that comes from outside your control. - Give fallbacks that make sense.
"unknown"for a name reads better than a bareNonein your output. - Print the parsed data once while you are learning the shape. A quick
print(data)shows you the exact keys before you start reading them. - Keep the empty-dict trick
{ }ready for nested reads so a missing branch never crashes you.
π οΈ Practice Challenge
Try this with the same public API. Fetch a single post from https://jsonplaceholder.typicode.com/posts/1 and print its title. Then fetch the list at https://jsonplaceholder.typicode.com/posts and print the title of each of the first three posts. Read fields with .get() so a missing one never crashes you.
π§© What Youβve Learned
A quick recap of what you can now do:
- β Explain why raw JSON text needs parsing before you can read it.
- β
Use
response.json()to turn an API reply into Python dicts and lists in one step. - β Map JSON shapes to Python: objects become dicts, arrays become lists.
- β
Read nested fields safely with
.get()and a fallback, so a missing key never crashes you. - β Loop over a list of results and pull out the fields you need from each dict.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
What does response.json() return when the API sends back a JSON object?
Why: A JSON object parses into a Python dict, so you can read fields with keys.
- 2
Why use data.get('city') instead of data['city'] when the key might be missing?
Why: .get() returns None (or your fallback) for a missing key, while square brackets raise a KeyError.
- 3
What is the point of data.get('company', {}).get('name', 'unknown')?
Why: The empty-dict fallback lets the second .get() run safely even when 'company' is absent.
- 4
If response.json() returns a list, how do you read each item?
Why: A JSON array becomes a list, so you loop over it and read each dict inside.
π Whatβs Next?
You can read API replies now, but real calls sometimes fail or send back something unexpected. Next you will learn how to catch those problems gracefully instead of letting them crash your program.