Python Parsing JSON Responses

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 in data.
  • type(data) confirms we really got a dict, not a string.
  • data["name"] and data["email"] read fields straight out of that dict.

Output

<class 'dict'>
Leanne Graham
Sincere@april.biz

Note

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

Gwenborough

That 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 data
number = 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 missing
number = data.get("phone_number")
print(number)
email = data.get("email", "no email on file")
print(email)

Output

None
Sincere@april.biz

For 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 missing
company_name = data.get("company", {}).get("name", "unknown")
print(company_name)

Output

Romaguera-Crona

See 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 for loop visits each user, 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 users
Leanne Graham lives in Gwenborough
Ervin Howell lives in Wisokyburgh
Clementine Bauch lives in McKenziehaven
Patricia Lebsack lives in South Elvis
Chelsey 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.text gives you the raw string. To get Python objects you need response.json().
  • Using data["key"] on a key that might be missing. One absent field and the program crashes with a KeyError. 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. If a is missing, the first .get() returns None, and calling .get() on None crashes. 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 bare None in 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. 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. 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. 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. 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.

Error Handling in APIs

Share & Connect