JSON Formatter
Validate JSON against RFC 8259, pretty-print it with 2 or 4 spaces or tabs, or minify it. Errors come with line and column. Nothing is uploaded.
Up to 500,000 characters. Your JSON never leaves your device.
Orders object keys alphabetically by Unicode code point, at every level.
Writes characters such as ş, ü or emoji as \uXXXX sequences.
Paste JSON in the input box to validate and format it.
Everything runs in your browser; nothing is uploaded.
Paste JSON in the input box and the tool validates it, shows it in the indentation you choose and lists what it contains. Everything is processed on your device; the text is never uploaded.
How it works
The tool checks the text against the grammar of RFC 8259 (the same grammar as ECMA-404) with its own parser. It does not call JSON.parse, for two reasons. The error messages of JSON.parse differ between engines and give no reliable line and column, and JSON.parse converts every number to a double, which silently changes some values. The parser keeps its own stack instead of using recursion, so deep nesting cannot overflow the call stack, and nothing you paste is ever passed to eval.
What the grammar requires:
- Whitespace between tokens is exactly space, tab, line feed and carriage return. A no-break space or any other Unicode space is a syntax error.
- Strings use double quotes. Control characters U+0000 to U+001F must be escaped, so a raw line break or tab inside a string is an error. Allowed escapes are the quote, backslash, slash,
b,f,n,r,tandufollowed by four hex digits. - Numbers have an optional minus, an integer part without leading zeros, an optional fraction with digits on both sides of the point and an optional exponent.
+1,.5,1.,01,0x10,NaNandInfinityare not numbers in JSON. - Literals are only
true,falseandnull, in lowercase. - A document is one value. Anything after it is an error.
If the text is valid, the output uses your options. Indentation can be 2 spaces, 4 spaces, a tab, or none at all (minified, no whitespace outside strings). Empty objects and arrays are written as {} and []. Object members keep their source order unless you switch on Sort keys, which orders keys by Unicode code point at every level; arrays are never reordered. Escape non-ASCII characters writes every character from U+0080 upward as a \uXXXX escape, with an emoji written as a surrogate pair of two escapes.
Numbers are copied as raw text, so 0.10 stays 0.10, 1.0E+2 stays 1.0E+2 and integers above 2^53 are not rounded. Strings are re-escaped minimally: \/ and \u0041 come out as / and A, and a lone surrogate escape that cannot be written as UTF-8 is kept as an escape.
Below the result the tool lists statistics: objects, arrays, keys, string values (keys are not counted), numbers, booleans, nulls, maximum nesting depth and the UTF-8 size of input and output in bytes. A scalar document has depth 0 and [] has depth 1.
Examples
Error positions. For invalid JSON the tool shows the line, the column, the offending text and the line itself with a caret. These positions come from the tool's own tests:
| Input | Code | Line | Column | Found |
|---|---|---|---|---|
| [1,2,] | TRAILING_COMMA | 1 | 5 | , |
| ['a'] | SINGLE_QUOTES | 1 | 2 | ' |
| [01] | INVALID_NUMBER | 1 | 2 | 01 |
| [.5] | INVALID_NUMBER | 1 | 2 | .5 |
| [NaN] | INVALID_NUMBER | 1 | 2 | NaN |
| [True] | INVALID_LITERAL | 1 | 2 | True |
| [1 "a"] | UNEXPECTED_TOKEN | 1 | 4 | " |
| [1,2 | UNEXPECTED_END | 1 | 5 | (end of input) |
| [1]] | EXTRA_DATA_AFTER_VALUE | 1 | 4 | ] |
Across several lines the position follows the line breaks. In this document the error is on line 3, column 8, because tru is not a literal (the tool reports INVALID_LITERAL, found tru):
{
"a": 1,
"b": tru
}
A comment on its own line, such as // c at the start of line 3, is reported as COMMENT at line 3, column 1. {"a":1} // x gives COMMENT at line 1, column 9. For ["😀", x] the column is 8, not 7, because the emoji occupies two UTF-16 code units.
Turkish text and escapes. The input {"şehir":"İstanbul","ad":"Çağrı","sayı":3} with Sort keys on and minify gives:
{"ad":"Çağrı","sayı":3,"şehir":"İstanbul"}
ş (U+015F) sorts after s and y because the order is by code point, not by the Turkish alphabet. With Escape non-ASCII characters on, the same output becomes:
{"ad":"\u00c7a\u011fr\u0131","say\u0131":3,"\u015fehir":"\u0130stanbul"}
Code point order also puts uppercase before lowercase: {"b":1,"a":{"z":1,"y":2},"B":3} is sorted to {"B":3,"a":{"y":2,"z":1},"b":1}.
Statistics. [true,false,null,1,2.5,"a","b",{"k":"v"},[]] has 2 booleans, 1 null, 2 numbers, 3 string values (a, b and v), 1 object with 1 key and 2 arrays, with a maximum depth of 2.
Large numbers. In {"id": 9007199254740993, "ok": 1234567890123456} the first integer is reported as one that JSON.parse would round to 9007199254740992. The second has 16 digits but fits a double exactly, so it is not counted. A number such as 1E400 is written back unchanged, although JSON.parse turns it into Infinity.
Things to watch for
- JSON is stricter than JavaScript. Single quotes, unquoted keys, comments, trailing commas,
+1,.5,NaN,undefinedand Python-styleTrueandNoneare all errors. Formats called JSONC and JSON5 permit some of these, but they are different formats and this tool does not accept them. Remove the extras, then paste again. - Integers beyond 9007199254740991 are at risk elsewhere. Identifiers from databases and APIs are often this large. This tool keeps them, but a browser or Node.js script that calls
JSON.parseon the same text will round them. Look at the big integer note in the statistics, and treat such IDs as strings if you control the format. - Duplicate keys are accepted but are a smell. Two parsers can read the same text as two different documents. The tool lists up to 20 repeated keys with their position and counts all of them; keys that are equal after unescaping (
"a"and"a") count as duplicates, and only keys inside the same object are compared. - Sorting changes the text, not the data. Use it to compare two documents line by line. Duplicate keys keep their relative order when sorted.
- Byte order mark. A BOM at the very start is reported as
BOM_FOUND, becauseJSON.parserejects it as well. A U+FEFF anywhere else is an unexpected character, and inside a string it is plain data. - Depth limit. Nesting up to 200 levels is accepted; the 201st opening bracket is reported as
MAX_DEPTH_EXCEEDED. - Size limit. Up to 500,000 characters. Blank or whitespace-only input shows a prompt instead of an error.
- Privacy. The text is processed in your browser and is not uploaded, stored or sent anywhere.
Frequently asked questions
Last updated: 10/11/2026
Related tools
Encode text to Base64 or decode it back. Text is converted as UTF-8, so ş, ğ and emoji work. Choose the URL-safe alphabet, padding and line breaks.
Percent-encode or decode text for URLs as a component, a whole URL or form data, and see the parts of any http or https address. Nothing is fetched.