json.dumps(data, indent=2, ensure_ascii=False) turns a Python object into JSON text with every member on its own line, two spaces per level. ensure_ascii=False keeps letters such as é as they are; without it, Python writes them as \u00e9.
import json
text = '{"name":"café","id":12345678901234567890,"tags":["api","ops"],"active":true,"manager":null}'
data = json.loads(text)
print(json.dumps(data, indent=2, ensure_ascii=False))
{
"name": "café",
"id": 12345678901234567890,
"tags": [
"api",
"ops"
],
"active": true,
"manager": null
}
The formatter below prints the same for that text with 2 spaces.
From the shell
python3 -m json.tool data.json
python3 -m json.tool --indent 2 --sort-keys --no-ensure-ascii data.json
python3 -m json.tool --compact data.json # minify
python3 -m json.tool data.json pretty.json # write to a file
json.tool indents by 4 spaces and escapes non-ASCII unless told otherwise, so the first command prints "name": "caf\u00e9". --indent, --tab, --compact and --no-ensure-ascii need Python 3.9 or later, and Python 3.14 also accepts python3 -m json. Don’t redirect into the file you’re reading: python3 -m json.tool data.json > data.json empties it before Python starts, and you get Expecting value: line 1 column 1 (char 0).
Sorting keys
sort_keys=True (or --sort-keys) orders the keys of every object at every depth by code point, so "B" comes before "a". Arrays keep their order.
Numbers: integers survive, floats don’t
Python reads JSON integers as int, which has no size limit (though by default it won’t parse one longer than 4,300 digits), so 12345678901234567890 keeps every digit, unlike in JavaScript. Floats go through float and come back rewritten:
print(json.dumps(json.loads('{"price":1.10,"exp":1e5,"big":1e400}')))
# {"price": 1.1, "exp": 100000.0, "big": Infinity}
1.10 becomes 1.1, 1e5 becomes 100000.0, and 1e400 overflows to Infinity, which RFC 8259 doesn’t allow. json.loads also accepts NaN and Infinity as input. Pass allow_nan=False to dumps to get ValueError: Out of range float values are not JSON compliant instead of invalid output. The formatter below copies number tokens as written, so 1.10 stays 1.10.
Other gotchas
indent="\t"indents with tabs;indent=0puts each member on its own line with no indentation.- A repeated key keeps its last value without a warning.
- A
dateorDecimalraisesTypeError: Object of type date is not JSON serializable.default=strwrites such values as strings:{"at": "2026-10-15"}. separators=(",", ":")with no indent gives the most compact output.