Python has excellent JSON support built into the standard library. Most use cases need nothing more than json.dumps with an indent argument. But for command-line use, color output, or maximum speed, there are better options.

Method 1: json.dumps (the obvious one)

The standard library does the job for nearly every case:

import json

data = {"users": [{"name": "Alice", "age": 30}, {"name": "Bob", "age": 25}]}
print(json.dumps(data, indent=2))

Output:

{
  "users": [
    {
      "name": "Alice",
      "age": 30
    },
    {
      "name": "Bob",
      "age": 25
    }
  ]
}

Useful parameters:

Method 2: command-line one-liner

When you have a JSON file to format quickly:

python -m json.tool input.json
# Or with explicit indent:
python -m json.tool --indent 2 input.json

This reads the file (or stdin) and writes formatted JSON to stdout. Useful in shell pipelines:

curl https://api.example.com/data | python -m json.tool

Method 3: rich for colored output

For terminal output with syntax highlighting, the rich library is excellent:

from rich import print_json

data = {"users": [{"name": "Alice", "age": 30}]}
print_json(data=data)

Output gets full color terminal highlighting — keys in one color, strings in another, numbers in another. Great for debugging and CLI tools where readability matters.

Method 4: orjson for speed

If you're formatting megabytes of JSON regularly, orjson is dramatically faster than the standard library:

import orjson

data = {"users": [{"name": "Alice", "age": 30}]}
formatted = orjson.dumps(data, option=orjson.OPT_INDENT_2)
print(formatted.decode())

orjson is 2-5x faster than the standard library for serialization. It also handles dataclasses, datetimes, and numpy arrays natively. The trade-off: it returns bytes (not strings), and the indentation options are less flexible — only 2-space indent is supported.

Handling special types

The standard library can't serialize datetime, Decimal, or custom classes by default. Pass a custom encoder:

import json
from datetime import datetime
from decimal import Decimal

class CustomEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.isoformat()
        if isinstance(obj, Decimal):
            return str(obj)
        return super().default(obj)

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

Reading JSON files

The mirror image of json.dumps:

with open("data.json") as f:
    data = json.load(f)

# Or from a string:
data = json.loads(json_string)

Both raise json.JSONDecodeError on invalid JSON, with line and column information in the error message.

Formatting JSON in pandas DataFrames

If your JSON comes from a DataFrame, both directions:

import pandas as pd

# DataFrame → pretty JSON
df.to_json(orient="records", indent=2)

# JSON → DataFrame
df = pd.read_json("data.json")

Common pitfalls

NaN, Infinity, -Infinity. These are Python floats but invalid JSON. json.dumps emits them by default (technically invalid), but with allow_nan=False it raises an error. Decide which behavior you want explicitly.

Tuple vs list. Python tuples serialize as JSON arrays, same as lists. But deserialization always returns lists — round-trip changes tuples to lists. If type fidelity matters, convert before serializing.

Set serialization. Sets aren't JSON-serializable. Convert to a list first: json.dumps(list(my_set)).

None vs null. Python's None serializes as JSON's null. This is symmetric and works as expected.

Validating before formatting

If you receive JSON from an external source, validate before formatting to surface issues with a clear error:

import json

def safe_format(text):
    try:
        data = json.loads(text)
        return json.dumps(data, indent=2)
    except json.JSONDecodeError as e:
        return f"Invalid JSON at line {e.lineno}, col {e.colno}: {e.msg}"

For one-off formatting, our tool

If you don't want to write code, our JSON Formatter does the same thing entirely in the browser. Paste JSON, get formatted output, copy or download. Useful for inspecting API responses without leaving the browser.

Wrap-up

For 95% of cases, json.dumps(data, indent=2) is all you need. Use python -m json.tool for command-line work. Reach for rich when you want colored terminal output, or orjson when speed matters.