Why Does My JSON Have Backslashes? — How to Fix Double-Escaped JSON
Seeing "{"name":"Alice"}" instead of {"name":"Alice"}? Your JSON is double-escaped — the JSON has been serialized twice. This is one of the most common API bugs encountered by developers, especially when connecting frontend applications to backend APIs. This guide explains exactly why it happens, how to diagnose it quickly, and how to fix it permanently at the source — or work around it client-side when you can't.
Double-encode
JSON serialized twice — the most common cause
JSON.parse()
parses the outer string to get the real object
Back-end bug
usually caused by calling JSON.stringify() twice
API response
fix by removing extra serialization at source
What Does Double-Escaped JSON Look Like?
The difference between normal JSON and double-escaped JSON is subtle but critical. When you look at a raw API response or log it to your console, the format reveals the problem immediately.
Visual comparison
Normal JSON: {"name":"Alice","age":30} — parses to an object.
Double-escaped: "{"name":"Alice","age":30}" — parses to a string.
The outer quotes make the entire payload a JSON string containing escaped JSON. Parsing once gives you a string. You must parse a second time to get the actual object.
The telltale signs of double-escaped JSON are: the response body starts and ends with a double quote character, every internal quote is preceded by a backslash (\"), and typeof response === 'string' in JavaScript when you expected an object. In Postman or Chrome DevTools, the Response tab will show the entire body as a quoted string.
What you see vs what you expect
Root Cause: JSON.stringify() Called Twice in JavaScript
The overwhelming majority of double-escaped JSON issues trace back to a single mistake: calling JSON.stringify() on a value that is already a JSON string, or using a framework method that performs automatic serialization on data you've already serialized.
The double-stringify mistake in Node.js
JSON.stringify() called on already-stringified value
// Backend code — accidentally serializing twice
const user = { name: "Alice", age: 30 };
// First serialization — produces a string
const jsonString = JSON.stringify(user);
// jsonString = '{"name":"Alice","age":30}'
// Second serialization ❌ — serializing the string again
res.json(JSON.stringify(jsonString));
// Client receives: '"{\"name\":\"Alice\"}"'
// Because res.json() runs JSON.stringify() internally,
// wrapping your already-stringified string in another layer of quotesUse res.json(object) OR res.send(string) — never both
// Option 1: Pass the object directly to res.json()
// res.json() handles all serialization automatically
const user = { name: "Alice", age: 30 };
res.json(user); // ✅ Correct — serialized exactly once
// Option 2: If you must build the string manually and send it raw
const jsonString = JSON.stringify(user);
res.setHeader('Content-Type', 'application/json');
res.send(jsonString); // ✅ send the raw string (already valid JSON)
// Do NOT call res.json() when data is already a stringRoot Cause: Python Flask and Django Double Serialization
Python web frameworks have the same trap. The jsonify() function in Flask andJsonResponse in Django both serialize your data automatically. Passing an already-serialized string to them results in double encoding.
Python Flask double serialization
json.dumps() + jsonify() = double serialized
import json
from flask import jsonify
user = {"name": "Alice", "age": 30}
# Manually serialize first
json_string = json.dumps(user) # → '{"name": "Alice", "age": 30}'
# Then pass the string to jsonify ❌
# jsonify() calls json.dumps() on the string again!
return jsonify(json_string)
# Response body: '"{\"name\": \"Alice\", \"age\": 30}"'Pass a dict to jsonify() OR use Response() with raw string
from flask import jsonify, Response
import json
user = {"name": "Alice", "age": 30}
# Option 1: Pass dict directly to jsonify ✅
return jsonify(user)
# Option 2: Return raw JSON string with explicit content-type ✅
json_string = json.dumps(user)
return Response(json_string, mimetype='application/json')AWS Lambda and API Gateway Double Encoding
AWS API Gateway is a particularly common source of double-escaped JSON because it can apply its own serialization to Lambda return values. Understanding the flow helps you identify where the extra encoding is introduced.
How Lambda + API Gateway interacts
Lambda returns a JavaScript object. API Gateway serializes it based on the Integration Response settings. If your Lambda function returns an already-serialized JSON string instead of an object, API Gateway may serialize it again.
The Lambda handler mistake
Returning JSON.stringify(data) from a Lambda handler instead of returning data directly causes API Gateway to treat the string as a regular string value and wrap it in additional serialization.
Lambda Proxy Integration
When using Lambda Proxy Integration, the body field of the response object should be a string. But if you're also wrapping this in JSON.stringify(), the body becomes double-encoded.
Content Handling setting
API Gateway has a "Content Handling" option in integration responses. If set to "Convert to text," it may apply additional encoding. Check this setting and the mapping templates if double encoding persists.
// ❌ Common mistake — returns double-encoded JSON
exports.handler = async (event) => {
const data = { name: "Alice", age: 30 };
return {
statusCode: 200,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(JSON.stringify(data)) // ❌ double stringify
};
};
// ✅ Correct — stringify the body once
exports.handler = async (event) => {
const data = { name: "Alice", age: 30 };
return {
statusCode: 200,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data) // ✅ stringify exactly once for the body field
};
};Client-Side Fix — Parse Twice When You Can't Fix the Server
Sometimes you're consuming a third-party API or legacy service that you can't modify. In that case, you need to handle double-encoded responses client-side. The approach is straightforward: detect whether the parsed value is a string, and if so, parse again.
// When you receive double-escaped JSON and can't fix the server
const response = await fetch('/api/user');
const data = await response.json();
// data might be a string instead of an object due to double encoding
if (typeof data === 'string') {
const actualData = JSON.parse(data); // parse the inner JSON
console.log(actualData.name); // "Alice"
} else {
console.log(data.name); // already an object — normal case
}
// Generic helper to safely unwrap potentially double-encoded JSON
function unwrapJson(data) {
if (typeof data === 'string') {
try {
return JSON.parse(data);
} catch {
return data; // not JSON, return the string as-is
}
}
return data;
}
// Usage
const raw = await response.json();
const result = unwrapJson(raw);
// result is always an object (or primitive), never a JSON stringIdentify the encoding layer using curl
curl -v https://your-api.com/endpoint and look at the raw response body. If the body starts with " and contains \" throughout, it's double-encoded. Compare with your framework's output directly (bypass the framework and return raw) to isolate which layer adds the extra encoding.Diagnosing Where the Double Encoding Happens
Check the raw API response
Use curl or Postman to see the raw HTTP response body. If it starts with a quote character, it's double-encoded. This rules out client-side parsing issues.
Add logging at the serialization point
Log the value just before sending the response on the server. If the logged value already contains escaped quotes, the serialization is happening upstream — in middleware or a data access layer.
Check middleware stack
Many Express/Fastify middleware components serialize responses automatically. If you have JSON serialization middleware AND you call res.json(), you get double encoding. Audit your middleware pipeline.
Test framework defaults
Create a minimal test endpoint that returns a hardcoded object with res.json(). Compare its output format to the problematic endpoint. If the minimal endpoint works correctly, the issue is in your data handling code, not the framework.
Check ORM and database layers
Some ORM libraries return stringified JSON for JSON/JSONB database columns. If your database stores {"name":"Alice"} in a JSON column and your ORM returns it as a string, then calling JSON.stringify() on that string double-encodes it.