Debugging a JSON API response is not always difficult because of the code.
Sometimes the real problem is simply finding the information you need.
Imagine receiving a response containing hundreds of nested objects, arrays, user records, and metadata fields — all compressed into one long line.
You need to identify one unexpected value, confirm the JSON is valid, and decide whether the problem is with the API response or your application.
How do you investigate without wasting time?
Let's work through a practical example.
1. The problem: A difficult-to-read API response
Imagine you're testing an order-management API.
The endpoint returns the following response:
{"orderId":"ORD-1024","customer":{"id":42,"name":"Alex","address":{"city":"Hyderabad","postalCode":"500001"}},"items":[{"sku":"KB-101","quantity":2,"price":1499},{"sku":"MS-202","quantity":1,"price":799}],"payment":{"status":"completed","amount":3797},"delivery":{"status":"pending","estimatedDays":3}}
This response is valid JSON.
But it's not particularly convenient to inspect.
Suppose your API test fails because the expected delivery status is shipped, while the actual response contains pending.
You know something is different, but navigating a large one-line response can be frustrating.
When responses become significantly larger, the problem grows.
Before writing more debugging code, the first useful step is to make the response readable.
2. Format the JSON before investigating
The same response becomes easier to understand when formatted with indentation.
{
"orderId": "ORD-1024",
"customer": {
"id": 42,
"name": "Alex",
"address": {
"city": "Hyderabad",
"postalCode": "500001"
}
},
"items": [
{
"sku": "KB-101",
"quantity": 2,
"price": 1499
},
{
"sku": "MS-202",
"quantity": 1,
"price": 799
}
],
"payment": {
"status": "completed",
"amount": 3797
},
"delivery": {
"status": "pending",
"estimatedDays": 3
}
}
Now you can quickly identify:
- The order identifier.
- The customer's information.
- The array containing purchased items.
- The payment status and amount.
- The delivery status.
Formatting doesn't change the underlying data. It changes how easily a developer can inspect its structure.
For a single response, this may save only a little time.
For repeated debugging, readable payloads can make investigations much easier.
3. Inspect nested values using JavaScript
Formatting is useful for visual inspection.
But automated tests need a repeatable way to access specific values.
Suppose the previous response has already been parsed into a JavaScript object named order.
You can inspect individual properties:
console.log(order.orderId);
console.log(order.customer.address.city);
console.log(order.payment.status);
console.log(order.delivery.status);
Expected output:
ORD-1024
Hyderabad
completed
pending
You can also access array values:
console.log(order.items[0].sku);
// KB-101
console.log(order.items[1].quantity);
// 1
For an API tester, this is often the next step after understanding the payload structure.
But there is an important distinction:
Formatting JSON helps you understand the data. Assertions help you verify its behavior.
You need both when debugging a real API.
4. Identify the actual cause of a failed assertion
Consider this Playwright API test:
const { test, expect } = require("@playwright/test");
test("order delivery status", async ({ request }) => {
const response = await request.get("/api/orders/ORD-1024");
expect(response.ok()).toBeTruthy();
const order = await response.json();
expect(order.delivery.status).toBe("shipped");
});
This test assumes that a configured API endpoint exists and returns the illustrated response.
If the API returns:
{
"delivery": {
"status": "pending"
}
}
The assertion fails.
That failure does not automatically prove the API is defective.
You need to investigate the expected business behavior.
For example:
- Should a newly created order already be shipped?
- Is the test running before the delivery update finishes?
- Does the test use the correct order identifier?
- Is the response stale or cached?
- Is
pendingactually the correct state?
This is where good API testing goes beyond checking individual field values.
A failing assertion is evidence of a mismatch between expectations and actual behavior. It is not, by itself, evidence of a backend defect.
5. What if the response is invalid JSON?
Now consider a different response:
{
"orderId": "ORD-1024",
"status": "pending",
}
The trailing comma makes this invalid JSON.
JavaScript's JSON.parse() will throw an error:
const responseText = `{
"orderId": "ORD-1024",
"status": "pending",
}`;
try {
const order = JSON.parse(responseText);
console.log(order);
} catch (error) {
console.error("JSON parsing failed:", error.message);
}
A parsing error means the text does not follow JSON syntax.
However, it doesn't tell you whether the original problem came from the backend, a copied sample, a test fixture, or some other source.
First identify where the invalid text originated.
Then correct the responsible system or test data.
Avoid automatically modifying an API response merely to make it parse successfully. Doing so can hide genuine defects.
6. A practical browser-based debugging workflow
For JSON that is safe to inspect locally, a browser-based formatter can reduce the need to write temporary formatting scripts.
I maintain ToolZenHub, a collection of practical online utilities.
One of its developer tools is the free JSON Formatter & Validator:
https://toolzenhub.in/tools/json-formatter/
Here's how you can use it with the earlier order response.
Step 1: Copy the JSON payload
Take the one-line response from the first example.
Make sure you are copying the actual JSON response body rather than headers or unrelated console output.
Step 2: Paste it into the tool
Open the JSON Formatter & Validator and paste the response into its input field.
Step 3: Select Format
The tool formats valid JSON using two-space indentation.
This makes nested objects and arrays easier to inspect.
Step 4: Inspect the relevant fields
In our example, look for:
"delivery": {
"status": "pending",
"estimatedDays": 3
}
You can now focus on the property involved in the failing assertion.
Step 5: Validate suspicious JSON
If the payload fails to parse, select Validate.
For malformed JSON, the tool reports the error location, including line and column information.
You can use its "Go to error" option to navigate to the problematic location.
The tool identifies syntax problems; it does not silently repair or rewrite invalid JSON.
Step 6: Copy the formatted result when needed
If you need a readable sample for documentation or a test fixture, use Copy output.
For a compact representation, use Minify instead.
These operations serve different purposes:
| Operation | Purpose |
|---|---|
| Format | Improve readability |
| Minify | Remove unnecessary whitespace |
| Validate | Check JSON syntax |
| Copy output | Reuse the processed text |
The tool processes JSON in your browser rather than uploading it to ToolZenHub.
Still, follow your organization's data-handling policies. Do not paste confidential production data, credentials, tokens, or personal information into tools unless your security requirements permit it.
7. An important limitation: Valid JSON can still contain wrong data
Suppose the API returns:
{
"orderId": "ORD-1024",
"payment": {
"status": "completed",
"amount": "3797"
}
}
This is valid JSON.
A syntax validator should accept it.
But notice that amount is a string.
If the API contract requires a number, this response may be incorrect.
For example:
const order = {
orderId: "ORD-1024",
payment: {
status: "completed",
amount: "3797"
}
};
console.log(typeof order.payment.amount);
// string
To validate the value's type:
if (typeof order.payment.amount !== "number") {
throw new Error("Payment amount must be numeric");
}
This is a separate type check.
JSON formatting and syntax validation do not replace schema validation, application logic, or automated assertions.
A useful debugging workflow should distinguish these different responsibilities.
8. Calculate expected values instead of trusting the response
Let's examine the order items:
const items = [
{
sku: "KB-101",
quantity: 2,
price: 1499
},
{
sku: "MS-202",
quantity: 1,
price: 799
}
];
We can independently calculate the total:
const total = items.reduce((sum, item) => {
return sum + item.quantity * item.price;
}, 0);
console.log(total);
// 3797
For this example:
2 × 1499 = 2998
1 × 799 = 799
Total = 3797
This gives the tester an independent expected result.
We can compare that result with the returned payment amount.
const actualPaymentAmount = 3797;
if (total !== actualPaymentAmount) {
throw new Error("Payment amount mismatch");
}
For real payment systems, also account for discounts, taxes, shipping, currency, refunds, and the application's rounding rules.
A simple item-total calculation should not be treated as a universal payment-validation formula.
9. Avoid logging sensitive API responses
A common debugging habit is to print the entire response:
console.log(order);
That may be convenient in a local test.
But production API responses can contain:
- Authentication credentials.
- Session tokens.
- Personal information.
- Addresses and contact details.
- Internal identifiers.
- Confidential application data.
Instead, log only the fields required for investigation.
console.log({
orderId: order.orderId,
deliveryStatus: order.delivery.status
});
Even selected identifiers should be checked against your organization's logging policies.
When sharing debugging examples publicly, use fabricated test data rather than real customer information.
10. When should you use a formatter and when should you write code?
Both approaches are useful.
Use a JSON formatter when:
- You want to visually inspect a response.
- A nested structure is difficult to read.
- You need to locate a JSON syntax problem.
- You want a formatted sample for documentation.
- You need to remove unnecessary whitespace.
Use automated validation and assertions when:
- You're checking API behavior repeatedly.
- The API contract specifies required fields.
- You need to verify data types.
- You need to test calculations or business logic.
- You want failures detected automatically in CI.
For large or sensitive datasets, local command-line tools and approved development environments may be more appropriate than a browser utility.
ToolZenHub's formatter has defined input-size and nesting limits, so it is not intended to process arbitrarily large responses.
The two approaches complement each other rather than compete.
11. A repeatable API debugging checklist
The next time an API test fails, work through these questions:
- Did the HTTP request reach the expected endpoint?
- Was the HTTP status what you expected?
- Is the response body valid JSON?
- Is the response structure readable?
- Does the expected property exist?
- Is the property using the correct data type?
- Does the returned value match the API contract?
- Are there timing, authentication, or test-data issues?
- Can you independently verify the important calculation or result?
- Are your debugging logs safe to retain or share?
This sequence helps distinguish syntax issues from data issues and actual application defects.
Final thoughts
Debugging APIs efficiently is not about using the most tools or writing the most code.
It's about reducing uncertainty.
Start by understanding the response. Make the structure readable. Check syntax. Inspect relevant properties. Verify the contract. Then investigate the behavior that caused the test to fail.
If formatting or validating JSON is slowing down your workflow, you can try the browser-based utility I maintain at ToolZenHub:
https://toolzenhub.in/tools/json-formatter/
The goal is simple: spend less time untangling raw payloads and more time identifying the actual problem.
Disclosure: I maintain ToolZenHub, the website referenced in this article. This tutorial was prepared with AI assistance and requires editorial and technical review before publication.
Top comments (0)