JSON Format & Validate
Validate, lint, format and explore JSON: strict RFC 8259 checking with exact error locations, a collapsible tree with copyable paths, and formatting that never changes a value. Runs in your browser.
Where your data goes
| Using this page | Using the API, MCP or A2A | |
|---|---|---|
| Where it runs | In your browser. The JSON is parsed, checked and re-formatted by JavaScript on this page; nothing you paste is sent anywhere. | In a Cloudflare Pages Function at the Cloudflare data centre nearest the caller. |
| What leaves your device | Nothing you enter. After the page and its scripts load, no request
carries your input. The browser enforces this: the page's Content-Security-Policy
sets connect-src 'none' and form-action 'none', which
block fetch, XHR, WebSocket, EventSource, beacons and form posts.
| The server receives the JSON text, processes it in memory inside the request, and returns the result. Your input is not stored or logged; only the per-call metadata row (operation, time, status, sizes) is recorded. The server makes no network requests. For confidential documents use the page, which does the same work in your browser and sends nothing. The server receives the JSON text, lints it in memory and returns the findings. Your input is not stored or logged; only the per-call metadata row (operation, time, status, sizes) is recorded. |
| Stored | Nothing leaves the page. No cookies, analytics or third-party scripts; the browser keeps only your View → Inline config choice. | Your inputs and results: never. What is stored is your client record (client ID, a SHA-256 hash of your secret, expiry, and the profile you registered with) and one metadata row per call: operation, time, transport, status, error code, duration, request and response sizes, and country. |
| Logged | Nothing (no requests are made). | Only the per-call metadata above, attributed to your client ID. Never parameters, bodies, results, or credentials. Cloudflare, as the host, also processes request metadata (IP address, full URL including the query string, headers) under its own policies, so put inputs in POST bodies. |
| Access | Open to anyone. | Requires a client ID and secret (how to get one). HTTPS only; TLS terminates at Cloudflare. |
Details and how to verify this yourself: Security & privacy.
User guide
The page is a small studio. The studio works like a desktop window. Its menu bar has File (open, load the sample, download, clear), Edit (format, copy), View (switch views, show the options inline, full screen), Settings (Options… opens the options in an overlay) and Help (jumps to these sections, and lists the keyboard shortcuts). The full-screen icon sits at the right of the same row: two arrows pointing out make the studio fill the window, and arrows pointing in (or Esc) bring it back.
Your Input is never overwritten: every result has its own view, so you can always flip back.
- Paste, type or open a file in the Input view, or choose File → Load sample (the File menu also has Open file… and Download input). The text is checked as you type. The status bar shows whether it is valid, a summary (type, keys, arrays, depth, size) and the number of findings. The line numbers on the left are coloured where there is a problem: red for errors, amber for warnings, blue for information.
- Edit → Format (or Ctrl+Enter) shows the result in the Formatted view, pretty-printed with your chosen indent and, if ticked, sorted keys. Edit → Minify shows the most compact form. Use Copy, Download, or Use as input to make the result your new input.
- Tree shows the document as a collapsible tree, like a JSON viewer. Click a node to see its type, size and value, and its path in three notations, ready to copy: JSONPath (
$.items[0].id), JSON Pointer (/items/0/id) and JMESPath (items[0].id). Filter finds keys and values and opens the branches that contain them. Expand all and Collapse all do what they say. - Problems lists every finding with its line, column and rule. Click one to jump to that spot in the input.
- For large documents, the full-screen icon (right end of the menu bar) makes the whole studio fill the window. Press Esc or the icon again to return.
Settings → Options… sets the indent (2 spaces, 4 spaces or tab), key sorting, and the line-length limit for the line-length rule (0 turns it off). The lint rules are listed in full under Lint rules below.
Duplicate keys. {"id": 1, "id": 2} is accepted by most parsers, but they disagree on which value wins, and JSON.parse silently keeps the last one. The page lists every repeated key with its JSON Pointer and where both copies are. The formatted output keeps the last value, at the position of the first, which is what JSON.parse does.
Formatting never changes your data. Only whitespace changes (and the order of keys, if you sort them). Strings and numbers are copied exactly as written: 1.0 stays 1.0, "é" keeps its escape, and a 20-digit ID is not rounded. Tools that re-serialise through JSON.parse lose all three.
Large numbers. An integer beyond 9,007,199,254,740,991 (2^53 − 1) is valid JSON, but JavaScript and many other languages read it as the nearest double, so 12345678901234567890 becomes 12345678901234567000. The page warns about such numbers; if every digit matters, send them as strings.
Specification
Grammar. RFC 8259 (identical to ECMA-404), with no extensions. A JSON text is exactly one value, surrounded by optional whitespace.
| Rule | Behaviour |
|---|---|
| Whitespace | Only space, tab, LF and CR. A no-break space or other Unicode space is an error. |
| Byte order mark | A leading U+FEFF is an error (RFC 8259 §8.1: senders must not add one). |
| Empty input | An error. |
| Top-level value | Any value, including a bare string, number, true, false or null. |
| Strings | Double quotes only. Characters U+0000–U+001F must be escaped. Escapes: \" \\ \/ \b \f \n \r \t \uXXXX. Any other escape is an error. |
| Unpaired surrogates | A \uD800–\uDFFF escape that is not part of a pair is accepted, with a warning: it is not valid Unicode. |
| Numbers | -? (0 | [1-9][0-9]*) (. [0-9]+)? ([eE] [+-]? [0-9]+)?. No + sign, leading zeros, bare .5 or 1., hexadecimal, NaN or Infinity. |
| Literals | true, false, null, lowercase only. |
| Comments, trailing commas, single quotes, unquoted keys | Errors. There is no JSON5 or “relaxed” mode: anything accepted here is accepted by every conforming parser. |
| Nesting | At most 500 levels of objects and arrays. |
Error location. Only the first error is reported, because after a syntax error the rest of the text has no reliable structure.
lineandcolumnare 1-based;offsetis 0-based from the start of the input.- Columns and offsets count Unicode code points, so an emoji counts as one character. LF, CRLF and a lone CR each end a line.
pointeris the RFC 6901 JSON Pointer of the value that was being read, for example/items/3/name;""is the root.excerptis the offending line, cut to about 40 characters either side of the error (cuts are marked…), followed by a line with^under the error. Tabs and control characters are shown as one-character placeholders so the caret lines up.
Duplicate keys. Keys are compared after decoding escapes, so "a" and "a" are duplicates. Each repeat is listed with the JSON Pointer of the member, its line, column and offset, and the line and column of the first occurrence. RFC 8259 says names should be unique, so duplicates do not make the syntax invalid, and json_format accepts them. The linter (the studio and json_lint) reports them as errors, as eslint-plugin-jsonc and jsonlint’s strict mode do: the status bar then says the JSON parses but has lint errors. The formatted output keeps the last value at the position of the first occurrence.
Warnings. At most 100 are listed, then one line with the number left out.
- An integer (no fraction or exponent) whose magnitude exceeds 2^53 − 1, with the value a double-based parser would read.
- A number that overflows a double (for example
1e400), which JavaScript reads asInfinity. - An unpaired surrogate escape.
Formatting.
- Indentation:
2or4spaces, or a tab, per level. Each member and element is on its own line; a colon is followed by one space. Empty objects and arrays print as{}and[]. - Minify: no whitespace at all between tokens.
- Content: strings, keys and numbers are copied byte for byte from the input. The output has no trailing newline.
- Sort keys: recursive, by UTF-16 code units (
"B"before"a"before"é"), the order RFC 8785 (JSON Canonicalization Scheme) uses. Arrays keep their order. Because numbers and string escapes are kept as written, sorted and minified output is not full RFC 8785 canonical form.
Statistics (valid input only).
| Field | Meaning |
|---|---|
| Depth | 0 for a scalar; 1 for a flat object or array; one more per level of nesting. |
| Objects, arrays | Number of each, at every level. |
| Members | Object members, duplicates included. |
| Strings, numbers, booleans, nulls | Values of each type. Keys are not counted as strings. |
| Input size, output size | UTF-8 bytes. An unpaired surrogate counts as 3 bytes (it is encoded as U+FFFD). |
API. POST /api/json/lint returns every lint finding. POST /api/json/format with input (the JSON text, as a string field or a raw text/plain body), indent (2, 4, tab or none) and sort_keys. Invalid JSON is a 200 response with valid: false and the error object, not an HTTP error. input may be at most 500,000 characters; the page has no limit.
Lint rules
| Rule | Severity | What it checks | Fixed by Format |
|---|---|---|---|
syntax | error | Strict RFC 8259 JSON: no comments, trailing commas, single quotes, unquoted keys, NaN/Infinity or leading zeros. | no |
duplicate-keys | error | An object repeats a key. JSON.parse keeps only the last value, so earlier ones are silently lost. | no |
number-precision | warning | A number cannot be represented exactly as a 64-bit float (e.g. integers beyond ±2^53), so most parsers change it. | no |
string-encoding | warning | A string contains a lone UTF-16 surrogate escape, which is not valid Unicode. | no |
indent-style | warning | Indentation mixes tabs and spaces. | yes |
indent | warning | Indentation is not a consistent multiple of the indent unit. | yes |
trailing-spaces | warning | No spaces or tabs at the end of a line. | yes |
final-newline | warning | The text ends with exactly one line break. | yes |
line-endings | warning | All line breaks use the same style (LF or CRLF). | yes |
byte-order-mark | warning | No byte-order mark (U+FEFF) at the start. | yes |
line-length | info | Lines are at most the configured length (default 120 characters). | no |
Errors make the document invalid. Warnings and information are style findings; the document still parses.
API & MCP
The same logic is available over HTTP and as MCP tools. Unlike this page, these run on the server: see where your data goes.