Escape JSON in Python — The Complete Guide to dumps, loads, and ensure_ascii
- json.dumps handles all required escapes automatically — you rarely need to escape by hand.
- ensure_ascii=True (the default) escapes non-ASCII as \uXXXX. Set False to keep unicode raw.
- Common bug: double json.dumps on something that was already a JSON string.
Table of Contents
Python's built-in json module is well-designed — json.dumps escapes everything JSON requires and json.loads unescapes correctly. The problems people hit are almost always either misunderstanding ensure_ascii, hitting the double-stringify bug, or trying to hand-escape when they should let the library handle it.
json.dumps Handles Escaping For You
Pass a Python dict, list, string, number, bool, or None to json.dumps and you get back a valid JSON string with all required escapes applied:
import json
data = {"name": "Alex", "quote": 'She said "hi"', "path": "C:\\Users"}
print(json.dumps(data))
# {"name": "Alex", "quote": "She said \"hi\"", "path": "C:\\Users"}
Double quotes inside strings get escaped. Backslashes get doubled. Newlines, tabs, and control characters get their named escapes. You do not need to manually pre-process your strings — that would double-escape them.
If you find yourself calling replace('"', '\\"') before json.dumps, you have a bug. Remove the replace.
ensure_ascii=True — The Default Nobody Expects
Python's json module defaults to ensure_ascii=True, which escapes every non-ASCII character as \uXXXX:
json.dumps({"msg": "café"})
# '{"msg": "caf\u00e9"}'
That's valid JSON and parses back correctly, but it's not what you usually want — the output is less readable, larger in bytes, and breaks diff tools that expect UTF-8 text.
Set ensure_ascii=False to keep unicode raw:
json.dumps({"msg": "café"}, ensure_ascii=False)
# '{"msg": "café"}'
When you write to a file, also pass encoding='utf-8' on the file open. Both steps are needed for clean UTF-8 output.
Most modern Python code uses ensure_ascii=False. The default is legacy — it predates ubiquitous UTF-8 and survives for backward compatibility.
json.loads Handles Unescaping
The reverse direction is json.loads:
import json
raw = '{"msg": "She said \\"hi\\""}'
obj = json.loads(raw)
print(obj['msg'])
# She said "hi"
All the escape sequences get converted back to the characters they represent. Unicode escapes become the original characters. You get a Python dict back, ready to work with.
Common error: json.decoder.JSONDecodeError: Invalid \escape. This means the input has a backslash followed by something that isn't a valid JSON escape — usually a Windows path that was stored raw. See bad escape sequence errors for the full debugging checklist.
Common Python JSON Escape Bugs
1. Double json.dumps. Calling dumps on something that's already a JSON string gives you a double-escaped string:
raw = json.dumps({"a": 1}) # '{"a": 1}' — string
wrong = json.dumps(raw) # '"{\"a\": 1}"' — double-escaped
# Use raw directly, or load first:
ok = json.dumps(json.loads(raw))
2. Forgetting indent for readability. By default dumps produces a single line. Pass indent=2 for pretty-printed output when writing to files or logs for human consumption.
3. Non-JSON-safe types. datetime, Decimal, UUID, sets, and custom classes throw TypeError. Use the default= parameter to provide a serializer, or convert to str first.
from datetime import datetime
json.dumps({"now": datetime.now()}, default=str)
4. Bytes vs strings. In Python 3, dumps returns str. loads accepts either str or bytes. Mixing dump/dumps (file vs string) with the wrong mode on file open causes encoding errors.
When Python Developers Still Use a Browser Tool
For debugging log lines or API responses outside a Python script, our browser JSON escape tool is faster than spinning up a Python REPL. Common cases:
- A log line shows escaped JSON and you want to read the original
- You need to paste a JSON body into a bash curl command (Python's not involved)
- You want to verify that a string is valid JSON before feeding it to
json.loads - Quick sanity check on escape sequences during code review
For production code, stick with json module. For ad-hoc debugging, the browser tool is the faster keystroke.
Escape JSON Outside Python
For log debugging and one-off JSON checks. No REPL needed.
Open Free JSON Escape / Unescape ToolFrequently Asked Questions
Why does json.dumps escape my é as \u00e9?
Default is ensure_ascii=True. Set ensure_ascii=False to keep unicode raw. Both forms are valid JSON.
How do I json.dumps a datetime?
datetime is not JSON-native. Use default=str for ISO format, or write a custom encoder class.
Why does my json.loads fail on a Windows path?
Paths like C:\Users need the backslashes doubled when stored as JSON. If they were stored raw, you get invalid escape errors on parse.
What is the difference between dumps and dump?
dumps returns a string. dump writes to a file object. Same for loads vs load.

