Skip to content

JSON Formatter

A JSON formatter re-indents machine-minified JSON into something a human can read, and tells you precisely where invalid JSON goes wrong. This one parses with the browser's own strict JSON.parse(), reports the exact line and column of a syntax error with the offending source line, and can minify or sort keys.

Parsed with the browser’s own JSON.parse() — the same strict RFC 8259 parser your app uses. Nothing is uploaded.

Paste JSON above — it is validated and formatted as you type.

What counts as valid JSON

JSON (RFC 8259) is a much smaller language than most people assume, and nearly every "invalid JSON" error comes from importing a habit from JavaScript or YAML that JSON simply does not have.

  • Strings use double quotes only. 'single' is a syntax error. So is a backtick template literal.
  • Object keys must be quoted strings. {name: "x"} is valid JavaScript and invalid JSON.
  • No trailing commas. [1, 2, 3,] fails. This is the single most common cause of a rejected config file.
  • No comments. Neither // nor /* */. Formats that allow them — JSON5, JSONC, tsconfig.json — are extensions, not JSON.
  • No literal newlines inside strings. A line break must be written \n. Tabs must be \t.
  • Numbers are decimal only. No hex (0xFF), no leading +, no leading zeros, no NaN, no Infinity, no trailing decimal point.
  • `undefined` does not exist. Only null. And the literals are lowercase — True is not true.

The values themselves are just six things: object, array, string, number, boolean, and null. A valid JSON document may have any of them at the top level — a bare 42 or "hello" is legal JSON, even though most APIs return an object.

JSON does not define an integer type. Everything numeric is a double, so values beyond 2⁵³ lose precision — a 64-bit database ID or a Twitter/X snowflake ID silently rounds. Serialise large IDs as strings.

Reading the error message

Browsers disagree about how they report parse failures. V8 (Chrome, Edge, Node) gives a byte offset — "Unexpected token } in JSON at position 42" — and newer versions add a line and column. Firefox reports a line and column directly. Safari often reports neither. This tool prefers the byte offset when one is available and computes the line and column from your input itself, so the number you see is always derived from the text in the box rather than trusted from a message format that varies by browser.

Read the full explanation of json formatter results ↓

The reported position is where the parser *noticed* the problem, which is often one token after where you made the mistake. A missing comma on line 12 is usually reported at the start of line 13, because the parser only realises something is wrong when it encounters a key where it expected a comma. Always look at the line above the one flagged.

Message fragmentAlmost always means
Unexpected token } in JSONA trailing comma before the closing brace
Unexpected token ] in JSONA trailing comma before the closing bracket
Unexpected end of JSON inputTruncated data — an unclosed brace, or a response that got cut off
Unexpected token ' in JSONSingle-quoted strings; JSON requires double quotes
Unexpected token o in JSON at position 1You parsed the string "[object Object]" — something was stringified with String() rather than JSON.stringify()
Unexpected non-whitespace character after JSONTwo JSON documents concatenated, or NDJSON parsed as a single document
Unexpected token < in JSON at position 0The response is HTML, not JSON — usually an error page or a login redirect

That last one deserves emphasis because it is the most misdiagnosed. Unexpected token < means your code asked for JSON and received <!DOCTYPE html>. The JSON is not malformed; there is no JSON. Check the HTTP status code and the Content-Type header — the HTTP headers checker will show you what the server is actually returning.

Formatting, minifying, and sorting

Pretty-printing adds whitespace, which is semantically meaningless in JSON — the parsed value is identical either way. Two spaces is the near-universal convention for configuration files and API examples; four is common in Python-adjacent codebases; tabs suit anyone who wants readers to control the visual width.

Minifying strips all insignificant whitespace. For a response served over the wire this matters less than it once did, because gzip and brotli compress repeated indentation to almost nothing — the real saving is usually 1–3% after compression, not the 20–30% the raw byte counts suggest. Minify because a system requires a single line (a Docker label, an environment variable, a DNS TXT record), not as a performance optimisation.

Sorting keys is the underrated feature here. JSON objects have no defined ordering, so two semantically identical documents can differ byte-for-byte purely by key order — which makes diff useless and produces noisy commits when a tool re-serialises a config file. Sort both sides first and the diff shows only real changes. It is also the fastest way to compare two API responses that should match.

Command-line equivalents

jq is the tool worth learning properly; python3 -m json.tool is the one guaranteed to be present.

# Pretty-print (jq indents with 2 spaces by default)
jq . response.json
jq --indent 4 . response.json
curl -s https://api.example.com/status | jq .

# Minify
jq -c . response.json

# Sort keys recursively — makes two responses diffable
jq -S . a.json > a.sorted.json
jq -S . b.json > b.sorted.json
diff a.sorted.json b.sorted.json

# Validate only: exit status 0 = valid, non-zero = invalid, with an error on stderr
jq empty config.json && echo "valid"

# No jq available? Python is everywhere
python3 -m json.tool config.json
python3 -c "import json,sys; json.load(open(sys.argv[1]))" config.json

# Extract a value rather than eyeballing the whole document
jq -r '.records[] | select(.type=="MX") | .value' zone.json

# NDJSON / JSON Lines — one document per line, so parse per line
jq -c . < events.ndjson | head
Formatting and validating JSON in a shell

jq empty is the cleanest validator: it parses the document, produces no output, and exits non-zero if parsing fails. That makes it ideal in a CI step or a pre-commit hook, where you want a fast fail rather than a formatted dump.

Where JSON shows up in DNS and mail work

DMARC aggregate reports are XML rather than JSON, but almost every tool that processes them emits JSON, and the nested record arrays are exactly the kind of structure that is unreadable minified and obvious formatted. RDAP — the structured successor to WHOIS — returns JSON directly, with the registrar, status codes, and event dates in nested objects; the WHOIS lookup presents that for you, but if you query an RDAP endpoint yourself you will want to format the response.

DNS-over-HTTPS also speaks JSON. Cloudflare and Google both expose a JSON API where the Status field carries the DNS response code (0 = NOERROR, 3 = NXDOMAIN) and Answer is an array of records with name, type, TTL, and data. Being able to read that quickly is the difference between "the domain is broken" and "the domain exists but has no records of that type" — a distinction the DNS lookup tool makes explicit.

curl -s -H 'accept: application/dns-json' \
  'https://cloudflare-dns.com/dns-query?name=example.com&type=MX' | jq .

# Status 0 = NOERROR, 3 = NXDOMAIN. An empty Answer with Status 0
# means "the name exists but has no records of this type".
curl -s -H 'accept: application/dns-json' \
  'https://cloudflare-dns.com/dns-query?name=example.com&type=MX' \
  | jq '{status: .Status, answers: (.Answer // [] | length)}'
A DoH query, formatted

One last habit: never build JSON by string concatenation. A domain name or an error message containing a quote or a backslash will produce invalid output, and the failure surfaces far from the cause. Serialise with your language's JSON library, which handles escaping, and use this page to check the result when something downstream rejects it.

JWT Decoder
Inspect JWT header and payload — locally, no signature check

Frequently asked questions

Why does JSON reject my trailing comma?
Because RFC 8259 does not allow one, even though JavaScript, Python, and most other languages do. [1, 2, 3,] is invalid. JSON5 and JSONC permit trailing commas and comments, but they are separate formats — a standard parser will reject both.
Can I put comments in JSON?
No. Neither // nor /* */ is valid. The usual workaround is a dedicated key such as "_comment": "…", which parsers ignore because it is just data. Files like tsconfig.json accept comments only because their specific tooling uses a JSONC parser.
What does "Unexpected token < in JSON at position 0" mean?
The response was HTML, not JSON — typically a 404 page, a 500 error, or a login redirect. There is nothing wrong with your parser. Check the HTTP status and the Content-Type header of the actual response.
Why does the reported line seem to be one past my mistake?
The parser reports where it detected a problem, not where you made one. A missing comma at the end of line 12 is only noticed when the parser reaches the key on line 13. Always inspect the line above the one flagged.
Does formatting change the data?
No. Whitespace between tokens is insignificant in JSON, so formatting and minifying produce identical parsed values. Sorting keys does not change the value either, since JSON objects are unordered — though it does change the bytes, which is exactly why it makes diffs readable.
Is my JSON uploaded anywhere?
No. Parsing and formatting run in your browser with the built-in JSON.parse() and JSON.stringify(). The page makes no network request, which matters given how often the JSON people need to format contains credentials or customer data.
Why did my large ID number change?
JSON numbers are IEEE-754 doubles, so integers above 2⁵³ (about 9 quadrillion) cannot be represented exactly. A 64-bit ID rounds silently. Any system that must round-trip such IDs should serialise them as strings.
How do I handle a file with one JSON object per line?
That is NDJSON / JSON Lines, and it is not a single JSON document — parsing the whole file at once fails with "unexpected non-whitespace character after JSON". Parse each line separately, or use jq -c . which reads a stream of documents natively.