Pretty-print JSON in Python

json.dumps(data, indent=2, ensure_ascii=False)

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=0 puts each member on its own line with no indentation.
  • A repeated key keeps its last value without a warning.
  • A date or Decimal raises TypeError: Object of type date is not JSON serializable. default=str writes such values as strings: {"at": "2026-10-15"}.
  • separators=(",", ":") with no indent gives the most compact output.

Try it in the JSON Formatter & Validator

Result
{
  "name": "café",
  "id": 12345678901234567890,
  "tags": [
    "api",
    "ops"
  ],
  "active": true,
  "manager": null
}

5 keys, nesting 2, 92 bytes in → 126 bytes out

Runs in your browser. Nothing you enter is sent anywhere.