Beginner

Almost every modern application deals with JSON at some point. Whether you are pulling data from a REST API, saving user preferences, or passing configuration between services, JSON is the format you will run into most often. It stands for JavaScript Object Notation, but do not let the name fool you — Python works with it beautifully.
The good news is that Python ships with a built-in json module, so there is nothing extra to install. In just a few lines of code you can read a JSON file into a Python dictionary, modify the data, and write it back out again. This makes it one of the easiest data formats to work with in Python.
In this article we will cover everything you need to know about working with JSON files in Python 3. We will start with a quick example, then walk through reading, writing, pretty printing, handling nested structures, dealing with common errors, and finish with a real-life project that ties it all together.
Reading and Writing JSON in Python: Quick Example
Python’s built-in json module handles JSON files with just two key functions: json.load() to read and json.dump() to write. No pip install required.
#quick_example.py
import json # built-in module, no installation needed
# Read a JSON file into a Python dictionary
with open('data.json', 'r') as file:
data = json.load(file) # converts JSON to a dict
print(data) # display the dictionary
print(type(data)) # confirm it's a dict
Output:
{'name': 'Alice', 'age': 30, 'city': 'Sydney'}
<class 'dict'>
The json.load() function reads the entire JSON file and converts it into a native Python dictionary. From there you can access values using normal dictionary syntax like data['name'].
Want to go deeper? Below we cover how to write JSON files, pretty printing, working with nested JSON, and a real-life project example that puts it all together.
What is JSON and Why Use It in Python?
JSON is a lightweight text-based format for storing and exchanging data. It uses key-value pairs (like a Python dictionary) and ordered lists (like a Python list). Here is what a simple JSON file looks like:
{
"name": "Alice",
"age": 30,
"city": "Sydney",
"languages": ["Python", "JavaScript"]
}
The reason JSON is so popular with Python developers is the direct mapping between JSON types and Python types:
| JSON Type | Python Type |
|---|---|
object {} | dict |
array [] | list |
string "" | str |
| number (int) | int |
| number (float) | float |
| true / false | True / False |
| null | None |
This means that when you read JSON into Python, you get back familiar data structures you already know how to work with.
How To Read a JSON File Into a Python Dictionary

The most common operation is reading a JSON file from disk. Python’s json.load() function takes a file object and returns the parsed data.
#read_json.py
import json # built-in JSON module
# Open the file in read mode and parse the JSON
with open('data.json', 'r') as file:
data = json.load(file) # parse the file contents into a Python dict
# Access individual values using dictionary keys
print(f"Name: {data['name']}") # access the 'name' key
print(f"Age: {data['age']}") # access the 'age' key
print(f"City: {data['city']}") # access the 'city' key
Output:
Name: Alice
Age: 30
City: Sydney
Each key from the JSON file becomes a dictionary key in Python. We use f-strings to format the output nicely, but you could also just print the entire dictionary with print(data).
Note: Always use the with statement when opening files. This ensures the file is properly closed after reading, even if an error occurs during parsing.
Reading JSON From a String With json.loads()
Sometimes you receive JSON as a string rather than from a file — for example, from an API response. In that case, use json.loads() (note the extra “s” which stands for “string”).
#read_json_string.py
import json # built-in JSON module
# A JSON-formatted string (could come from an API response)
json_string = '{"product": "Laptop", "price": 1299.99, "in_stock": true}'
# Parse the string into a Python dictionary
data = json.loads(json_string) # loads() for strings, load() for files
print(data)
print(f"Product: {data['product']}, Price: ${data['price']}")
Output:
{'product': 'Laptop', 'price': 1299.99, 'in_stock': True}
Product: Laptop, Price: $1299.99
Notice how the JSON true was automatically converted to Python’s True. The json module handles all type conversions for you.
How To Write a Python Dictionary to a JSON File

Writing data to a JSON file is just as simple. Use json.dump() to write a dictionary (or list) to a file.
#write_json.py
import json # built-in JSON module
# Create a Python dictionary with our data
user = {
"name": "Bob",
"age": 25,
"city": "Melbourne",
"is_student": True,
"courses": ["Python 101", "Data Science"]
}
# Write the dictionary to a JSON file
with open('user.json', 'w') as file:
json.dump(user, file) # converts dict to JSON and writes to file
print("Data written to user.json successfully!")
Output:
Data written to user.json successfully!
If you open user.json in a text editor, you will see all the data on a single line. That is valid JSON, but not very readable. In the next section we will fix that with pretty printing.
Converting a Dictionary to a JSON String With json.dumps()
If you want to convert to a JSON string without writing to a file, use json.dumps():
#dict_to_string.py
import json # built-in JSON module
data = {"name": "Charlie", "score": 95}
# Convert dictionary to a JSON-formatted string
json_string = json.dumps(data) # dumps() returns a string
print(json_string)
print(type(json_string)) # confirm it's a string, not a dict
Output:
{"name": "Charlie", "score": 95}
<class 'str'>
The result is a plain string that you can send over a network, store in a database, or log to a file.
How To Pretty Print JSON in Python
By default, json.dump() and json.dumps() produce compact, single-line output. For human-readable output, use the indent parameter.
#pretty_print.py
import json # built-in JSON module
user = {
"name": "Diana",
"age": 28,
"address": {
"street": "123 Main St",
"city": "Brisbane",
"postcode": "4000"
},
"hobbies": ["reading", "hiking", "coding"]
}
# Pretty print with 4-space indentation
formatted = json.dumps(user, indent=4) # indent=4 for readable output
print(formatted)
Output:
{
"name": "Diana",
"age": 28,
"address": {
"street": "123 Main St",
"city": "Brisbane",
"postcode": "4000"
},
"hobbies": [
"reading",
"hiking",
"coding"
]
}
The indent=4 parameter adds four spaces of indentation at each level, making nested structures easy to read. You can use any number you like — indent=2 is also common.
Sorting Keys Alphabetically in JSON Output
If you want the keys sorted for consistency (useful for diffs in version control), add sort_keys=True:
#sorted_json.py
import json # built-in JSON module
data = {"zebra": 1, "apple": 2, "mango": 3}
# Sort keys alphabetically in the output
print(json.dumps(data, indent=2, sort_keys=True)) # keys in A-Z order
Output:
{
"apple": 2,
"mango": 3,
"zebra": 1
}
The keys are now in alphabetical order. This does not change the original dictionary — it only affects the JSON output.
Working With Nested JSON Structures in Python

Real-world JSON is rarely flat. APIs typically return deeply nested structures with dictionaries inside dictionaries, and lists of objects. Here is how to navigate them.
#nested_json.py
import json # built-in JSON module
# A more complex, nested JSON structure
json_data = """
{
"company": "TechCorp",
"employees": [
{"name": "Alice", "role": "Developer", "skills": ["Python", "Django"]},
{"name": "Bob", "role": "Designer", "skills": ["Figma", "CSS"]},
{"name": "Charlie", "role": "DevOps", "skills": ["Docker", "AWS"]}
]
}
"""
data = json.loads(json_data) # parse the JSON string
# Access nested values
print(f"Company: {data['company']}") # top-level key
print(f"First employee: {data['employees'][0]['name']}") # nested access
print(f"Alice's skills: {data['employees'][0]['skills']}") # list inside dict
Output:
Company: TechCorp
First employee: Alice
Alice's skills: ['Python', 'Django']
We chain dictionary key access and list indexing together. data['employees'] returns a list, [0] gets the first item (a dict), and ['name'] gets the name from that dict.
Looping Through a List of JSON Objects
To process every item in a JSON array, use a simple for loop:
#loop_json.py
import json # built-in JSON module
json_data = '[{"name": "Alice", "score": 92}, {"name": "Bob", "score": 85}, {"name": "Charlie", "score": 97}]'
students = json.loads(json_data) # parse into a list of dicts
# Loop through each student and print their info
for student in students:
print(f"{student['name']} scored {student['score']}") # access keys in each dict
Output:
Alice scored 92
Bob scored 85
Charlie scored 97
Each item in the list is a dictionary, so we access student['name'] and student['score'] inside the loop. This pattern is exactly what you will use when working with API responses that return lists of results.
Handling Common JSON Errors in Python

What Happens When the File Does Not Exist
#error_file_not_found.py
import json # built-in JSON module
try:
with open('missing_file.json', 'r') as file:
data = json.load(file) # this will fail if the file doesn't exist
except FileNotFoundError:
print("Error: The file was not found. Check the file path.") # handle gracefully
Output:
Error: The file was not found. Check the file path.
Always wrap file operations in a try-except block. This prevents your program from crashing and gives you a chance to show a helpful message or use a default value.
What Happens When the JSON is Invalid
#error_invalid_json.py
import json # built-in JSON module
bad_json = '{"name": "Alice", "age": }' # missing value — invalid JSON
try:
data = json.loads(bad_json) # this will raise a JSONDecodeError
except json.JSONDecodeError as e:
print(f"Error: Invalid JSON — {e}") # print the specific error message
Output:
Error: Invalid JSON — Expecting value: line 1 column 27 (char 26)
The JSONDecodeError tells you exactly where the problem is — column 27 in this case, which is right after "age": where a value should be but is missing.
Safely Accessing Keys That Might Not Exist
#safe_access.py
import json # built-in JSON module
data = {"name": "Alice", "age": 30}
# Use .get() with a default value instead of direct key access
email = data.get('email', 'not provided') # returns default if key missing
print(f"Email: {email}")
Output:
Email: not provided
Using .get() with a fallback value avoids KeyError exceptions. This is especially useful when parsing JSON from external APIs where fields may be optional.
How To Update and Modify JSON Files in Python
A common task is reading a JSON file, updating some values, and writing it back.
#update_json.py
import json # built-in JSON module
# Step 1: Read the existing file
with open('user.json', 'r') as file:
data = json.load(file) # load current data
print(f"Before: {data}")
# Step 2: Modify the data
data['age'] = 26 # update an existing field
data['email'] = 'bob@example.com' # add a new field
# Step 3: Write the updated data back
with open('user.json', 'w') as file:
json.dump(data, file, indent=4) # write with pretty formatting
print(f"After: {data}")
print("File updated successfully!")
Output:
Before: {'name': 'Bob', 'age': 25, 'city': 'Melbourne', 'is_student': True, 'courses': ['Python 101', 'Data Science']}
After: {'name': 'Bob', 'age': 26, 'city': 'Melbourne', 'is_student': True, 'courses': ['Python 101', 'Data Science'], 'email': 'bob@example.com'}
File updated successfully!
The read-modify-write pattern is the standard way to update JSON files. We read the data in, change what we need using normal dictionary operations, then write the whole thing back out.
Real-Life Example: Building a Contact Book CLI With JSON

Let’s put everything together into a practical project. We will build a simple command-line contact book that stores contacts in a JSON file. It supports adding new contacts, listing all contacts, and searching by name.
#contact_book.py
import json # built-in JSON module
import os # to check if the file exists
CONTACTS_FILE = 'contacts.json' # our data file
def load_contacts():
"""Load contacts from the JSON file, or return an empty list if no file exists."""
if not os.path.exists(CONTACTS_FILE): # check if file exists first
return []
with open(CONTACTS_FILE, 'r') as file:
return json.load(file) # parse and return the list of contacts
def save_contacts(contacts):
"""Save the contacts list to the JSON file with pretty formatting."""
with open(CONTACTS_FILE, 'w') as file:
json.dump(contacts, file, indent=4) # write with indentation for readability
def add_contact(name, phone, email):
"""Add a new contact to the contact book."""
contacts = load_contacts() # load existing contacts
new_contact = { # create the new contact as a dict
"name": name,
"phone": phone,
"email": email
}
contacts.append(new_contact) # add to the list
save_contacts(contacts) # write back to file
print(f"Contact '{name}' added successfully!")
def list_contacts():
"""Print all contacts in a readable format."""
contacts = load_contacts()
if not contacts: # handle empty contact book
print("No contacts found.")
return
print(f"\n{'Name':<20} {'Phone':<15} {'Email'}")
print("-" * 55)
for c in contacts: # loop through each contact
print(f"{c['name']:<20} {c['phone']:<15} {c['email']}")
def search_contact(name):
"""Search for a contact by name (case-insensitive)."""
contacts = load_contacts()
results = [c for c in contacts if name.lower() in c['name'].lower()] # filter matches
if not results:
print(f"No contacts found matching '{name}'.")
return
for c in results:
print(f"Found: {c['name']} | {c['phone']} | {c['email']}")
# Demo: Add some contacts and interact with the book
add_contact("Alice Smith", "0412-345-678", "alice@example.com")
add_contact("Bob Johnson", "0498-765-432", "bob@example.com")
add_contact("Alice Wong", "0456-111-222", "awong@example.com")
print("\n--- All Contacts ---")
list_contacts()
print("\n--- Search for 'Alice' ---")
search_contact("Alice")
Output:
Contact 'Alice Smith' added successfully!
Contact 'Bob Johnson' added successfully!
Contact 'Alice Wong' added successfully!
--- All Contacts ---
Name Phone Email
-------------------------------------------------------
Alice Smith 0412-345-678 alice@example.com
Bob Johnson 0498-765-432 bob@example.com
Alice Wong 0456-111-222 awong@example.com
--- Search for 'Alice' ---
Found: Alice Smith | 0412-345-678 | alice@example.com
Found: Alice Wong | 0456-111-222 | awong@example.com
This project uses every concept from the article: reading JSON (load_contacts), writing JSON (save_contacts), working with nested structures (list of dicts), error handling (file existence check), and pretty printing (indent=4). The contacts persist between runs because they are saved to contacts.json on disk. You could extend this with delete and edit functionality using the same read-modify-write pattern we covered earlier.
Frequently Asked Questions
Can I use JSON files for configuration in Python?
Yes, JSON works well for simple configuration files. However, JSON does not support comments, which makes it less ideal for config files where you want to explain what each setting does. For configuration with sections and comments, consider using ConfigParser with .ini files instead.
What is the difference between json.load() and json.loads()?
json.load() reads from a file object (you pass it an open file), while json.loads() reads from a string (you pass it a JSON-formatted string). The "s" in loads stands for "string". Use load() when reading from files on disk and loads() when parsing JSON from API responses or variables.
How do I handle very large JSON files in Python?
For files that are too large to fit in memory, consider using the ijson library which supports streaming/incremental parsing. Install it with pip install ijson. For most files under a few hundred megabytes, the built-in json.load() works fine.
Can I store Python objects like datetime in JSON?
Not directly. JSON only supports strings, numbers, booleans, null, lists, and objects. To store a datetime, convert it to a string first using .isoformat() and convert it back when reading with datetime.fromisoformat(). You can also write a custom encoder class that inherits from json.JSONEncoder.
Is JSON or CSV better for storing data in Python?
It depends on your data structure. JSON is better for nested, hierarchical data (like API responses or configuration). CSV is better for flat, tabular data (like spreadsheets). JSON preserves data types while CSV treats everything as text. Choose based on what your data looks like, not which is "better" in general.
Conclusion
We have covered the complete workflow for working with JSON files in Python 3 — from reading with json.load() and json.loads(), to writing with json.dump() and json.dumps(), to pretty printing, handling nested data, managing errors safely, and building a real contact book application that persists data to disk.
The built-in json module is one of the most useful tools in the Python standard library and something you will reach for in almost every project. Now that you have a solid understanding of how it works, try extending the contact book example or applying these techniques to your next API integration.
Reference
Subscribe to our newsletter for more Python tutorials and tips!