, ,

How Do You Save Data as JSON and Call an API? (Python for Beginners, Part 16)

Convert Python data to JSON and back, save and load it, then call a live web API with requests, parse the JSON, and handle the request failing gracefully.

Python for Beginners · Part 16 of 20

Two programs need to swap data. One is written in Python, the other in something else entirely, running on a server across the world. They agree on a shared format, plain text that both sides can read, and that format is almost always JSON. It is the language the web uses to move data around, and Python speaks it with one small built-in module.

So this part answers two linked questions: how do you save your data in a format other programs understand, and how do you pull data from a service on the internet?

This part builds on files from Part 11, dictionaries from Part 8, and the package you installed in Part 14. You need Python 3 and about thirty minutes. The API section makes one real network call, so you need to be online for that piece.

What JSON is, and why it looks familiar

JSON stands for JavaScript Object Notation, but do not let the name distract you. It is just text arranged in a shape you already know, because a JSON object looks almost exactly like a Python dictionary and a JSON array looks like a Python list. Python converts between the two with the standard json module, and there are only two verbs to learn: dumps, which turns a Python value into JSON text, and loads, which turns JSON text back into Python.

python
import json

data = {'amount': 40, 'paid': True, 'note': None}
text = json.dumps(data)
print(text)
output
{"amount": 40, "paid": true, "note": null}

Read that output closely, because the small changes matter. Python’s True became true, and None became null. JSON has its own spelling for these, and all its text uses double quotes, never single. You do not do this conversion by hand; json.dumps handles it. But knowing it happens explains a lot of surprises later.

PythonBecomes in JSON
dictobject
list, tuplearray
strstring (double quoted)
int, floatnumber
True, Falsetrue, false
Nonenull

Table 1. How Python values map to JSON when you save them.

Saving the tracker as JSON

In Part 11 you saved expenses as comma separated lines and split them back by hand. JSON does that work for you and survives nested data without any parsing. Use json.dump to write straight to a file, and json.load to read it back, both keeping your list of dictionaries intact:

expenses.py
import json

expenses = [
    {'amount': 120.5, 'category': 'food'},
    {'amount': 40, 'category': 'travel'},
]

with open('expenses.json', 'w') as f:
    json.dump(expenses, f, indent=2)

with open('expenses.json') as f:
    loaded = json.load(f)
print(loaded)
output
[{'amount': 120.5, 'category': 'food'}, {'amount': 40, 'category': 'travel'}]

The indent=2 tells Python to lay the file out on multiple lines so a human can read it. Open expenses.json in your editor and you see a tidy, structured file. Unlike the hand split text from Part 11, this round trips perfectly: what you save is exactly what you load, types and all, with no manual conversion in the middle.

Gotcha: your own objects are not JSONTry to dump an Expense object from Part 15 straight to JSON and Python stops:
output
TypeError: Object of type Expense is not
JSON serializable
JSON only knows the basic types in the table above, not your custom classes. The fix is to hand it a dictionary instead, for example json.dumps(expense.__dict__), which turns the object’s attributes into a plain dict first. Convert objects to dictionaries on the way out, and back to objects on the way in.

Calling a web API

An API is a program’s doorway: a URL you request, that hands back data instead of a web page. Most return JSON. To call one you use the requests package, the same kind you installed in Part 14. Install it into your environment with pip install requests, then ask a free currency service for today’s dollar to rupee rate:

python
import requests

url = 'https://api.frankfurter.dev/v1/latest'
params = {'base': 'USD', 'symbols': 'INR'}
response = requests.get(url, params=params, timeout=10)
response.raise_for_status()
data = response.json()
print(data['rates']['INR'])
output
88.42

Your number will differ, because exchange rates change every day; that is the point of asking a live service instead of hard coding a value. Walk the code: requests.get fetches the URL with your parameters, raise_for_status turns a failed request into a clear error instead of silent bad data, and response.json() parses the returned JSON into an ordinary Python dictionary you index like any other. The timeout stops your program hanging forever if the server never answers.

your program requests.get the API a server online request a URL JSON comes back

Figure 1. You send a request to a URL, the API sends JSON back, and .json() turns it into a dict.

Putting them together in the tracker

Now the tracker can save its data as JSON and show the total in another currency using a live rate. This ties Part 12’s error handling to a real world truth: the network fails sometimes, and a program that assumes it never will is a program that crashes on a train.

expenses.py
import requests

def usd_to_inr(amount):
    url = 'https://api.frankfurter.dev/v1/latest'
    params = {'base': 'USD', 'symbols': 'INR'}
    try:
        r = requests.get(url, params=params, timeout=10)
        r.raise_for_status()
        rate = r.json()['rates']['INR']
        return amount * rate
    except requests.RequestException:
        return None

total = 160.5
converted = usd_to_inr(total)
if converted is None:
    print('rate unavailable, showing USD only')
else:
    print(f'{converted:,.2f} INR')
output
14,191.41 INR

Again, your figure will differ with the day’s rate. What matters is the shape: the call is wrapped so a network failure returns None instead of crashing, and the caller checks for that before printing. I will say plainly what many tutorials leave out: a bare requests.get with no timeout and no error handling is fine in a demo and wrong in anything real, because the internet is not reliable and your program has to expect that.

Why this matters on the jobReading and writing JSON and calling APIs is a huge share of everyday programming. Services talk to each other in JSON, config and data files are JSON, and half the tickets you pick up will involve fetching something from an API and reshaping its JSON. Doing it with a timeout and error handling, rather than assuming the happy path, is exactly the difference between code that survives production and code that pages someone at night.
Real interview question"How do you turn an API’s JSON response into Python data?" A clean answer: call the endpoint with requests.get, then response.json() parses the JSON body into a Python dict or list you can index. A strong follow up is mentioning that you check raise_for_status or the status code first, and handle the request failing, so you never treat an error page as real data.
Try it yourselfTake the dictionary {'city': 'Pune', 'temp': 31} and turn it into a nicely indented JSON string with json.dumps and indent=2, then print it. Expected output:
expected output
{
  "city": "Pune",
  "temp": 31
}
Show solution
python
import json

data = {'city': 'Pune', 'temp': 31}
print(json.dumps(data, indent=2))

The indent=2 spreads the object across lines with two space steps, and note the keys come out in double quotes, the JSON way.

JSON and API questions that come up

What is the difference between dumps and dump? json.dumps returns a string; json.dump writes straight to a file. The same pairing holds for loads, which reads a string, and load, which reads a file. The extra s is for string.

Do I need a key or an account to call an API? Some APIs need a key, many public ones do not. The currency service here needs nothing. When an API does need a key, you pass it along with the request, and you keep it out of your code.

Why did my JSON keys come back as strings when they were numbers? JSON object keys are always text. If you store a number as a key, it returns as a string, which trips people converting data back.

Is requests part of Python? No, it is a package you install with pip, which is why Part 14 came first. The standard library has urllib, but requests is far friendlier and what most code uses.

What if the API is down or I am offline? The request raises an error. Wrap the call in try and except as shown, and decide what your program does when the data does not arrive.

Digging into nested JSON

Real API responses are rarely flat. They nest objects inside objects, and lists inside those, and getting a value out means stepping down the layers with the same indexing you already use for dictionaries and lists. The currency response is a small example: the rate you want sits two levels in, under the rates key and then the currency code.

python
data = {
    'base': 'USD',
    'date': '2026-07-03',
    'rates': {'INR': 88.42, 'EUR': 0.92},
}
print(data['rates']['INR'])
print(data['rates']['EUR'])
output
88.42
0.92

Each square bracket steps one level down. The first reaches the rates object, the second picks a currency inside it. When a response hands you a list instead, you reach items by position, so something like data['results'][0]['rate'] takes the first record and then its rate. Mixing key lookups and index numbers as you descend is completely normal, and matching each bracket to a layer in the printed response is how you keep from getting lost.

One catch: asking for a key that is not there raises a KeyError and stops the program. When you are not certain a field exists in a response, reach for .get, which returns None, or a default you choose, instead of crashing:

python
print(data.get('rates', {}).get('GBP'))
output
None

The currency has no GBP entry, so instead of a crash you get None, which your code can check for. This matters with real APIs, where a field you expect is sometimes simply absent.

In practiceWhen you meet a new API, print json.dumps(response.json(), indent=2) once and read the shape before writing any indexing. Seeing the layers laid out saves you from guessing which brackets to type, and it is the fastest way to understand a response you have never seen.
You can nowYou can now convert Python data to JSON and back, save and load structured files with json.dump and json.load, call a live web API with requests, parse its JSON, and handle the call failing without crashing. Your tracker exports JSON and reads a real exchange rate. In Part 17 you make the tracker run itself, your first taste of automation.
your scriptGETapi.frankfurter.devJSONrate 88.42
You send a request, the API answers with JSON, and your code reads one value out.
Python for Beginners · Part 16 of 20
« Previous: Part 15  |  Complete Guide  |  Next: Part 17

About The Author


Discover more from Journal of Intelligent Infrastructure – By Dr Pranay Jha

Subscribe to get the latest posts sent to your email.

Leave a Reply

Your email address will not be published. Required fields are marked *

Architect’s Toolkit

About the Author

Dr. Pranay Jha is a Cloud and AI Consultant with 18+ years of experience in hybrid cloud, virtualization, and enterprise infrastructure transformation. He specializes in VMware technologies, multi-cloud strategy, and Generative AI solutions. He holds a PhD in Computer Applications with research focused on Cloud and AI, has published multiple research papers, and has been a VMware vExpert since 2016 and a VMUG Community Leader.

Discover more from Journal of Intelligent Infrastructure - By Dr Pranay Jha

Subscribe now to keep reading and get access to the full archive.

Continue reading