JSON Escape & Unescape
Turn raw text into a JSON string value, or decode backslashes back into readable text. When a string is not valid JSON, the tool still decodes it but tells you exactly which sequences were invented.
Runs entirely in your browser — your JSON is never uploaded.
Both directions run here in the tab. Byte counts are UTF-8 sizes, which is what a file or a network response would contain; escaping non-ASCII usually makes that number larger, not smaller.
How to escape or unescape a string
- Pick a direction Escape turns plain text into a legal JSON string body. Unescape goes the other way and accepts input with or without the surrounding quotes.
- Paste into the left panel Conversion happens as you type; there is no run button and no upload. Use Load example to see both directions on real-looking text.
- Set the two optional rules Escaping non-ASCII mimics Python's default. Escaping the forward slash mimics PHP's. Leave both off to match JSON.stringify byte for byte.
- Check the notice before you trust the output If the input was not valid JSON, the decoded result is shown with a list of every substitution made, so you know what your own code will do with the same string.
Escaping is about the container, not the data
A JSON string is delimited by double quotes, so it cannot contain a raw double quote. It also cannot contain a raw newline, a raw tab or a raw backslash, because those bytes would either end the string early or start an escape sequence. Escaping rewrites exactly those characters and nothing else.
It helps to see which characters actually get touched. Run any text through JSON.stringify and you get:
\",\\and\/for the quote, the backslash and the optional slash.\b,\f,\n,\rand\tfor five of the control characters.\u00XXfor the remaining nineteen control characters, which have no short form.- Nothing else. Accented letters, emoji, CJK and the del character U+007F are left exactly as they are by V8.
That last point is where implementations disagree, and it is the reason this page carries a measured table instead of a description.
Why your JSON suddenly contains \\u00e9
Hand the same four characters to three serializers and you get three different answers. This is measured on Node 22.22.2 (V8), Python 3.13.12 (json) and Ruby 2.6.10 (json 2.1.0), each with default arguments:
| Character | Node | Python | Ruby |
|---|---|---|---|
| Accented letter | é | \u00e9 | é |
| Emoji | 😀 | \ud83d\ude00 | 😀 |
| Forward slash | / | / | / |
| Vertical tab | \u000b | \u000b | \u000b |
Python is the outlier. Its json.dumps escapes every non-ASCII character by default, so a document that looked fine in development turns into a wall of \u sequences as soon as it is produced by Python. That is not corruption and nothing was lost — json.loads restores it exactly — but it is unreadable, and it is the single most common reason people go looking for an unescape tool.
The cost is measurable. The string café ☕ 用户 is 18 bytes in UTF-8 with the characters kept raw, and 31 bytes once all of it is escaped: same data, 72% larger, and pure ASCII. That trade is worth making when a value has to survive a header, a form field or a legacy system that mangles multi-byte text. It is a bad trade for a payload you are paying to transfer.
One further disagreement, small but real: the character U+007F (delete). V8 leaves it raw, Python writes \u007f and Ruby writes \u007F with an upper-case hex digit. All three outputs parse back to the same string, so nothing breaks — but a byte-for-byte comparison of two “identical” files can fail for this reason alone.
Sequences that are not JSON, and what each parser does
This is the part that turns a five-minute bug into an afternoon. JSON defines eight escape sequences. Everything else is invalid — but parsers disagree about what to do with invalid input, and the disagreement is not theoretical. Each cell below is the literal output of running that sequence through the parser:
\x41Write the letter A using the JavaScript short form- Node
- Bad escaped character in JSON at position 2 (line 1 column 3)
- Python
- Invalid \escape: line 1 column 2 (char 1)
- Ruby
- Accepted, produces x41
\0Write a null byte with the C-style NUL escape- Node
- Bad escaped character in JSON at position 2 (line 1 column 3)
- Python
- Invalid \escape: line 1 column 2 (char 1)
- Ruby
- Accepted, produces 0
\'Write a single quote as if this were JavaScript or SQL- Node
- Bad escaped character in JSON at position 2 (line 1 column 3)
- Python
- Invalid \escape: line 1 column 2 (char 1)
- Ruby
- Accepted, produces '
\u12Write a Unicode character but one digit short- Node
- Bad Unicode escape in JSON at position 5 (line 1 column 6)
- Python
- Invalid \uXXXX escape: line 1 column 3 (char 2)
- Ruby
- unexpected token at '"\u12"'
\u{1F600}Write an emoji using the ES2015 braced form- Node
- Bad Unicode escape in JSON at position 3 (line 1 column 4)
- Python
- Invalid \uXXXX escape: line 1 column 3 (char 2)
- Ruby
- unexpected token at '"\u{1F600}"'
\ud800Write half of a surrogate pair- Node
- Accepted, keeps the lone surrogate
- Python
- Accepted, keeps the lone surrogate
- Ruby
- incomplete surrogate pair at '\ud800"'
\/Escape a forward slash the way PHP's json_encode does- Node
- Accepted, produces /
- Python
- Accepted, produces /
- Ruby
- Accepted, produces /
Two rows deserve a second look, because they point in opposite directions. Ruby 2.6 accepts \x41, \0, \' and any other unknown escape by silently dropping the backslash: \x41 comes back as the three characters x41, not as A. Node and Python both reject all four. Then the same Ruby rejects a lone surrogate that Node and Python both accept.
So “is my JSON valid?” has no single answer at the edge. A string can pass in one runtime and fail in another, and the failure message will not mention that the other parser was happy. If a value travels between services, validate it with the same parser that will eventually read it — the JSON validator uses V8, which is what a browser and Node will use.
The practical rule is narrow and worth memorising: inside a JSON string, a backslash may only be followed by ", \, /, b, f, n, r, t or u plus exactly four hex digits. Anything else is a bug, even where a parser forgives it.
Why this tool decodes input that JSON rejects
Most unescapers just replace backslash sequences and hand you a result, which hides the only fact you needed: your string was never valid JSON. This one tries the browser's own parser first, so a valid string is decoded by the same code your application uses and cannot drift from it.
Only when that parser refuses the input does the tool fall back to a lenient pass, and then it reports what it changed: \x41 became A once, \q lost its backslash once. You get the text you were after, plus the exact reason your code threw.
That distinction matters when the input came from somewhere you cannot change — a log line, a queue message, a third-party export. If the tool says repaired, the fix belongs upstream, not in a bigger try/catch.
Escaping is not encoding, and neither is security
Four different transformations get called “escaping”, and applying the wrong one twice is how payloads end up as \\\\u00e9:
- JSON escaping protects a value inside a JSON string. Defined by RFC 8259, eight sequences, handled by this page.
- URL encoding protects a value inside a query string or a path. Spaces become
%20, and it is applied to the whole value, not to individual characters inside it. - HTML escaping protects a value inside markup.
<becomes<. Applying it to JSON is a common cause of"appearing where a quote was expected. - Base64 is not escaping at all. It converts bytes to ASCII text and hides nothing — anyone can reverse it, which is why it is not encryption either.
None of these is a security control. Escaping changes how a value is written down for one specific container; it does not authenticate it, and it does not stop anyone from reading it.