Reproduce the mismatch with a harmless example
The following body begins with an HTML doctype. A JSON parser rejects it immediately because a JSON value cannot start with <. Error wording varies by browser and JavaScript runtime; the first character is the useful clue.
In the browser Network panel, select the failing request and inspect Response, not only Preview. Record its original URL, final URL, status and Content-Type. Redact tokens and cookies before sharing evidence.
Received body:
<!doctype html><title>Sign in</title>
Expected body:
{"items":[],"next":null}
JSON.parse("<!doctype html>");
// SyntaxError: wording depends on the runtimeUse headers and redirects to identify the cause
A 200 status does not prove JSON was returned. An authentication redirect can end on a successful HTML login page, and a single-page application can serve index.html for an unknown /api route. A gateway can also return its own HTML error document.
Use HTTP Headers and Redirect Checker for a public endpoint without credentials. Those checks are separate requests from Cloudflare; they do not reproduce your browser’s authenticated session or POST body. For private APIs, inspect your own request locally.
| Evidence | Likely explanation | Next check |
|---|---|---|
| Final URL is /login | Authentication flow | Inspect session/auth handling locally |
| 200 plus text/html at /api/items | Fallback route or wrong host | Inspect API routing and deployment base URL |
| 502 plus HTML | Gateway or upstream failure | Check proxy and application logs |
| 404 plus HTML | Missing API route | Confirm method, path and deployed version |
Handle the response deliberately
Fix the route, authentication contract or upstream problem rather than replacing < with another character. An Accept header expresses the representation you want; it cannot force an HTML-only route to become a JSON API.
Check both response status and representation in client code. The example below omits logging the body because it could contain private data. An API can return JSON errors on non-success statuses; handle those according to its documented contract.
After changing the server or request, retry with the same method and credentials. A formatter successfully parsing a manually corrected sample is not proof that the live response is fixed.
const response = await fetch("/api/items", {
headers: { Accept: "application/json" }
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const type = response.headers.get("content-type") || "";
if (!/\bapplication\/(?:[\w.-]+\+)?json\b/i.test(type)) {
throw new Error("Expected a JSON response");
}
const data = await response.json();References
Use the linked specifications and product documentation to check behavior in your own environment.
Continue troubleshooting
Related tools
Frequently asked questions
Does setting Accept guarantee a JSON body?
No. The server must implement the requested representation and error contract.
Can a 200 response contain a login page?
Yes. The final redirected response can be a successful HTML login page.
Can PageUtil inspect my authenticated request?
No. Public network checks do not carry your browser session or private request body.