JSON (JavaScript Object Notation) is the most widely used data interchange format on the modern web. Nearly every REST API returns JSON, configuration files use JSON, and NoSQL databases like MongoDB store data in this format. Python offers native and extremely powerful JSON support through the json module, part of the standard library.

In this complete guide, you will learn everything about manipulating JSON data with Python: from basic serialization with json.dumps() and json.loads() to advanced techniques like custom encoders, JSON Schema validation, and REST API integration. Let's get started!

What is JSON?

JSON is a lightweight text format for storing and transporting structured data. It was originally specified by Douglas Crockford at JSON.org and later standardized by RFC 7159. Its syntax is simple and readable for both humans and machines.

JSON supports only six data types:

  • String — sequence of characters in double quotes
  • Number — integers or floating-point decimals
  • Booleantrue or false
  • Null — null value (null)
  • Array — ordered list of values in brackets []
  • Object — collection of key/value pairs in braces {}
{
  "name": "Python",
  "version": 3.12,
  "paradigms": ["Object-Oriented", "Functional", "Imperative"],
  "typing": "dynamic",
  "open_source": true,
  "creator": null
}

According to MDN Web Docs, JSON stands out for its simplicity and ubiquity on the web, being natively supported by all modern programming languages.

The Python json Module

Python includes the json module as part of its standard library. This means you don't need to install anything — just import it and start using it. The official json module documentation is the ideal starting point for in-depth reference.

import json

The module provides four main functions that form the foundation of all JSON manipulation in Python:

  • json.dumps() — converts a Python object into a JSON string
  • json.loads() — converts a JSON string into a Python object
  • json.dump() — writes a Python object directly to a JSON file
  • json.load() — reads a JSON file and converts it into a Python object

json.dumps() — Serializing Python to JSON

The json.dumps() function serializes a Python object into a JSON string. The conversion table follows the standard mapping:

  • dictObject {}
  • list, tupleArray []
  • strString
  • int, floatNumber
  • Truetrue
  • Falsefalse
  • Nonenull
import json

data = { "language": "Python", "created_in": 1991, "types": ["int", "float", "str", "list", "dict"], "interpreted": True, "author": None }

json_string = json.dumps(data) print(json_string)

Output: {"language": "Python", "created_in": 1991, "types": ["int", "float", "str", "list", "dict"], "interpreted": true, "author": null}

Notice how True becomes true and None becomes null automatically, following the JSON standard.

Advanced dumps() Parameters

json.dumps() offers several parameters to control the output:

import json

data = {"name": "Python", "version": 3.12, "features": ["simple", "powerful"]}

indent — formats with indentation for readability

print(json.dumps(data, indent=2))

sort_keys — sorts keys alphabetically

print(json.dumps(data, indent=2, sort_keys=True))

ensure_ascii — if False, allows Unicode characters

print(json.dumps({"name": "João"}, ensure_ascii=False))

Output: {"name": "João"}

separators — customizes separators (removes spaces)

print(json.dumps(data, separators=(",", ":")))

Output: {"name":"Python","version":3.12,"features":["simple","powerful"]}

The ensure_ascii=False parameter is essential when working with Portuguese or any language that uses accented characters, as explained in the Real Python JSON tutorial.

json.loads() — Converting JSON to Python

The json.loads() function does the reverse: it takes a JSON string and converts it into a Python object.

import json

json_string = '{"name": "Python", "version": 3.12, "interpreted": true, "author": null}'

data = json.loads(json_string) print(data)

Output: {'name': 'Python', 'version': 3.12, 'interpreted': True, 'author': None}

print(type(data))

Output: <class 'dict'>

The conversion respects the reverse mapping: true becomes True, null becomes None, and JSON strings become Python strings. The result is always a Python dictionary when the source JSON is an object.

json.dump() and json.load() — Working with Files

The json.dump() and json.load() functions work directly with files, avoiding the need for intermediate strings. This topic ties in with our guide on File Manipulation in Python: TXT, CSV, and JSON.

import json

Writing JSON to a file

data = { "name": "Python", "year": 1991, "frameworks": ["Django", "Flask", "FastAPI"] }

with open("data.json", "w", encoding="utf-8") as file: json.dump(data, file, indent=2, ensure_ascii=False)

Reading JSON from a file

with open("data.json", "r", encoding="utf-8") as file: loaded_data = json.load(file)

print(loaded_data["name"]) # Output: Python

Always use utf-8 encoding when working with JSON files to ensure compatibility with accented and special characters.

Serializing Custom Python Objects

By default, the json module does not know how to serialize custom class objects. Try running the code below and you will get a TypeError:

import json

class Person: def init(self, name, age): self.name = name self.age = age

p = Person("Anna", 30) print(json.dumps(p)) # TypeError: Object of type Person is not JSON serializable

There are two main approaches to solve this.

Method 1: Using a Custom JSONEncoder

Create a subclass of json.JSONEncoder and override the default() method. The official JSONEncoder documentation provides all the details for this approach.

import json
from datetime import datetime

class Person: def init(self, name, age): self.name = name self.age = age

class CustomEncoder(json.JSONEncoder): def default(self, obj): if isinstance(obj, Person): return {"name": obj.name, "age": obj.age, "_type": "Person"} if isinstance(obj, datetime): return obj.isoformat() return super().default(obj)

p = Person("Anna", 30) now = datetime.now() print(json.dumps({"person": p, "now": now}, cls=CustomEncoder, indent=2))

Method 2: Using Pydantic

Pydantic is a library that provides data validation and native JSON serialization. It is widely used with FastAPI and is the recommended approach for professional projects.

from pydantic import BaseModel

class Person(BaseModel): name: str age: int

p = Person(name="Anna", age=30) print(p.model_dump_json(indent=2))

Output: {"name": "Anna", "age": 30}

The model_dump_json() method from Pydantic already returns a valid JSON string, solving serialization elegantly and safely.

Handling Errors and Exceptions

When working with JSON data, especially from external sources, it is essential to handle parsing errors. The json module raises specific exceptions that should be caught:

import json

def process_json(text): try: data = json.loads(text) return data except json.JSONDecodeError as e: print(f"Error decoding JSON: {e}") print(f"Error position: line {e.lineno}, column {e.colno}") return None

Invalid JSON

invalid_json = '{"name": "Python", version: 3.12}' result = process_json(invalid_json)

Output: Error decoding JSON: ...

Valid JSON

valid_json = '{"name": "Python"}' result = process_json(valid_json) print(result)

Output: {'name': 'Python'}

Always validate incoming JSON before processing it. Combining try/except with json.JSONDecodeError ensures your application does not crash when receiving malformed data.

Validation with JSON Schema

To ensure JSON follows a specific structure, you can use JSON Schema. This specification defines formal contracts for JSON document structure, essential for APIs and system integrations.

from jsonschema import validate, ValidationError

schema = { "type": "object", "properties": { "name": {"type": "string", "minLength": 1}, "age": {"type": "integer", "minimum": 0}, "email": {"type": "string", "format": "email"} }, "required": ["name", "email"] }

valid_data = {"name": "Anna", "age": 30, "email": "[email protected]"} validate(instance=valid_data, schema=schema) # OK, no exception

invalid_data = {"name": "", "email": "invalid"} try: validate(instance=invalid_data, schema=schema) except ValidationError as e: print(f"Validation error: {e.message}")

While the jsonschema library needs to be installed separately (pip install jsonschema), it is extremely useful for validating API payloads, configuration files, and any JSON data that requires a predictable structure.

JSON and REST APIs

JSON is the standard input and output format for most modern REST APIs. When you make an HTTP request to an API, the response almost always comes back as JSON. Here is how to consume and send JSON using the requests library:

import json
import requests

Consuming an API that returns JSON

response = requests.get("https://api.github.com/users/python") data = response.json() # Equivalent to json.loads(response.text) print(f"Public repos: {data['public_repos']}")

Sending JSON to an API

payload = {"title": "New Post", "body": "JSON with Python"} headers = {"Content-Type": "application/json"} response = requests.post( "https://jsonplaceholder.typicode.com/posts", data=json.dumps(payload), headers=headers ) print(response.json())

To fully master API consumption, check out the complete guide on Python Requests for HTTP Requests.

FastAPI, one of the most popular Python web frameworks, uses Pydantic to validate and serialize JSON automatically. The FastAPI JSON Encoder documentation shows how the framework handles custom types transparently.

Best Practices and Performance

When working with JSON in Python, follow these recommendations:

  • Always use ensure_ascii=False when dealing with accented or Unicode content
  • Use indent only for config files — in production, omit it to reduce payload size
  • Use separators=(",", ":") in APIs to minimize JSON payload
  • Prefer Pydantic or dataclasses for object serialization in larger projects
  • Validate data with JSON Schema when the structure must be guaranteed
  • Handle exceptions with json.JSONDecodeError when processing external data

For projects requiring maximum performance, consider alternatives like orjson (written in Rust) or ujson. Python's native json module, however, is sufficient for the vast majority of use cases and has the advantage of requiring no external dependencies.

The W3Schools Python JSON tutorial offers additional examples and practical exercises to reinforce your learning.

Conclusion

JSON and Python form a powerful and ubiquitous combination in modern software development. The json module from the standard library provides all the tools needed to serialize, deserialize, read, and write JSON data in just a few lines of code.

In this guide, you learned:

  • The fundamentals of the JSON format and its data types
  • The four main functions: dumps(), loads(), dump(), load()
  • How to serialize custom Python objects with JSONEncoder and Pydantic
  • How to handle parsing errors and validate structures with JSON Schema
  • How to integrate JSON with REST APIs using the requests library
  • Best practices for performance and security

With this knowledge, you are ready to manipulate JSON data in any Python project — whether a web application, an automation script, a REST API, or a data pipeline. To go even deeper, explore the official documentation and practice with real data from public APIs.