Skip to content
DevTinyTools

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.

Escaping options
0 chars
0 chars

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

  1. 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.
  2. 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.
  3. 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.
  4. 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, \r and \t for five of the control characters.
  • \u00XX for 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:

How three serializers encode the same character by default
CharacterNodePythonRuby
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 &lt;. Applying it to JSON is a common cause of &quot; 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.

Frequently asked questions

What does escaping a JSON string actually do?
It converts a backslash, a double quote and every control character into a form that is legal between the quotes of a JSON string. JSON strings cannot contain a raw double quote, a raw newline or a raw backslash, so those three have to be written as \" , \n and \\ . Everything else in the document stays as it is.
Why does my JSON contain \u00e9 instead of é?
Because the library that produced it escapes non-ASCII by default. Python's json.dumps runs with ensure_ascii=True, so é becomes \u00e9, ☕ becomes \u2615 and 😀 becomes the surrogate pair \ud83d\ude00. Node's JSON.stringify and Ruby's JSON.generate leave those characters alone. If you want them readable, pass ensure_ascii=False in Python.
How do I escape double quotes for a JSON string?
Write \" for every quote that sits inside the value. The quotes that delimit keys and values are structural and must not be escaped. Pasting text that already contains quotes into this tool does it for you: the default output includes the surrounding quotes, so it can be dropped straight in as a value.
Is \/ valid JSON?
Yes. The JSON grammar allows a forward slash to be written as \/ , and every parser tested here accepts it and returns a plain slash. It is not required, though, and JSON.stringify never produces it. PHP's json_encode emits it by default unless you pass JSON_UNESCAPED_SLASHES, which is why so much PHP-generated JSON looks like it contains unnecessary backslashes.
Why does \x41 fail when JSON.parse reads it?
Because \x is a JavaScript string escape, not a JSON one. JSON defines exactly eight escape sequences: \" \\ \/ \b \f \n \r \t, plus \u followed by exactly four hex digits. V8 answers Bad escaped character in JSON, Python answers Invalid \escape, and JSON defines no such thing, so both are correct. If your data came from a C# or JavaScript source, that is where the extra backslash is coming from.
Does escaping make a JSON payload smaller?
No, it usually makes it larger, and the non-ASCII option makes it much larger. A string containing café, an emoji and two Chinese characters is 18 bytes as UTF-8 with the characters kept raw, and 31 bytes once every non-ASCII character is escaped. Escaping trades size for safety: an ASCII-only payload survives transport layers, form fields, headers and legacy systems that mangle multi-byte text.
Is escaping the same as encryption, URL encoding or HTML escaping?
No, and mixing them up is a common source of double-escaped data. JSON escaping protects a value inside a JSON string; URL encoding protects a value inside a query string or path; HTML escaping protects a value inside markup; base64 turns bytes into text and is reversible by anyone. Escaping is not a security measure and hides nothing.
Is the text I paste uploaded anywhere?
No. Both directions run in the tab using the browser's own JSON parser and a small local encoder. There is no request, and nothing is written to storage or analytics.