How to pretty print JSON in JavaScript, Python, and the shell
Pretty print JSON with JSON.stringify, Python, jq, or a browser tool. Covers indentation choices, key order, Unicode output, and common pitfalls.
APIs and logging libraries emit JSON in its smallest form, which is perfect for machines and miserable for people. Pretty printing adds line breaks and indentation so you can see the nesting. It takes one line in most languages, but there are details around indentation, key order, and non-ASCII text that are worth knowing before you start copying output into pull requests.
What a pretty printer does
Take a response like the one below. It is valid, and a program will read it without trouble, but a person has to count braces to work out which fields belong to the customer and which to the first order.
A pretty printer parses the text and writes it back with each member on its own line and children indented one level deeper than their parent. Nothing else changes. The same keys, values, and array order survive, and any parser will read the formatted version as identical data to the original.
That is worth stressing because people sometimes worry that formatting will break a payload. It will not, provided the formatter parses the input properly. The one place whitespace matters is inside strings, where spaces and escaped newlines are data, and a correct pretty printer leaves those alone. If you are copying formatted JSON into a request body, the server will accept it just as it accepts the compact version, although the request will be a little larger.
{"customer":{"id":88,"name":"Marta"},"orders":[{"id":1,"total":19.99,"items":["pen","ink"]},{"id":2,"total":5,"items":[]}]}JavaScript: JSON.stringify with an indent
The third argument of JSON.stringify sets the indentation. Pass a number up to 10 for that many spaces, or a string of up to 10 characters to use as the indent. The second argument is a replacer; null means keep everything. You can also pass an array of key names there to include only those properties, which is a quick way to trim a large object for display.
In the browser console, you can pretty print a response in one go with console.log(JSON.stringify(data, null, 2)). Be careful with values that JSON cannot represent: undefined properties disappear, functions are skipped, and circular references throw a TypeError.
The array form of the second argument deserves a caution. It filters keys at every level of nesting, not just the top, so a key name that appears in a nested object is kept or dropped there as well. Used on a response where id appears at several depths, it can give a misleading picture of the document, so treat it as a display trick rather than a way to produce data for another system.
const data = JSON.parse(
'{"customer":{"id":88,"name":"Marta"},"orders":[{"id":1,"total":19.99,"items":["pen","ink"]}]}'
);
console.log(JSON.stringify(data, null, 2));
console.log(JSON.stringify(data, null, "\t")); // tabs
console.log(JSON.stringify(data, ["customer", "name"], 2)); // only these keysPython: json.dumps and json.tool
In Python, json.dumps takes an indent argument. Two things catch people out. By default it escapes every non-ASCII character as \uXXXX, so a name like Zoë shows up as Zo\u00eb; pass ensure_ascii=False to keep the real characters. And key order follows insertion order unless you pass sort_keys=True.
For a file or a piped response, you do not need a script at all. The json.tool module reads from standard input or a file and writes formatted JSON, and it exits with an error on invalid input.
import json
data = json.loads('{"name": "Zoë", "tags": ["b", "a"]}')
print(json.dumps(data, indent=2, ensure_ascii=False, sort_keys=True))
# From the shell:
# curl -s https://api.example.com/items | python3 -m json.tool
# python3 -m json.tool --indent 2 --no-ensure-ascii input.jsonjq and other command-line options
jq is the tool many developers reach for when they work with JSON in a terminal. The simplest filter, a single dot, pretty prints the input and colors it when the output is a terminal. Add -S to sort keys or --tab to indent with tabs, and use a path expression to print only part of the document. That last feature is particularly useful on large responses, since you can print .orders[0] instead of paging through everything.
curl -s https://api.example.com/orders | jq .
jq --tab . orders.json
jq '.orders[0]' orders.json
jq -r '.orders[].items[]' orders.json # raw strings, one per linePretty printing in the browser
If you are not at a terminal, or the text came from a chat message or a bug report, an online formatter saves time. In View My JSON, paste the JSON or drop a .json file into the editor and press Format. The text is parsed in your browser, so it is not uploaded for formatting, and no account is needed for Format and Validate.
Pretty printing is where reading starts, not where it ends. For a long array of similar objects, the Table view shows the records as rows, and the Tree view lets you fold branches you are not interested in. Both are free, as are Cards.
Indentation, key order, and diffs
Pick one indent and use it everywhere. Two spaces is common in JavaScript projects, four in Python projects, and either is fine. Mixing them in the same repository creates noisy diffs where every line is reported as changed because of whitespace. If your formatter and your editor disagree, an .editorconfig file or a pre-commit hook that runs the formatter keeps them aligned.
Key order is the other source of diff noise. JSON objects are unordered according to the spec, so tools are free to reorder, but humans see a reorder as a change. If you commit generated JSON, sort the keys during generation so that output is stable between runs. If you hand-edit a file, do not sort it, because the order was probably chosen to put the important fields first.
Pitfalls worth checking
Pretty printing cannot rescue invalid input. A trailing comma or a comment will make the formatter fail, and the error message tells you where the parser stopped. Fix that first, then format.
Large numbers are the quiet risk. In JavaScript, JSON.parse converts numbers to doubles, so an integer above 9007199254740991 loses precision when you parse and stringify again, and the formatted output will show a different number from the original. If exact IDs matter, format with a tool that works on the text, such as jq with its default handling for large integers in recent versions, or keep the IDs as strings in the source data. Also remember that pretty printed JSON is larger. Do not ship it in production responses unless people actually read them.