When you fetch data from an API and parse it with JSON.parse() or response.json(), one of the most common errors you’ll hit is:
SyntaxError: Unexpected token < in JSON at position 0
The position 0 is the clue: the very first character of the string is <. That character opens an HTML tag, which means the string you are parsing is HTML, not JSON. This guide explains why it happens and how to fix it.
A valid JSON string can only start with a few characters: {, [, ", a digit, t/f/n, or -. The character < never starts valid JSON. So when JSON.parse() sees < at position 0, it knows immediately that the input isn’t JSON — it is almost always the beginning of an HTML document like <!DOCTYPE html> or <html>.
When a server returns a 404 or 500, many frameworks send back an HTML error page. Your code assumes the response is JSON, but it’s actually HTML:
const res = await fetch('/api/user/9999'); // user doesn't exist -> 404
const data = await res.json(); // ❌ Unexpected token < in JSON at position 0
If an endpoint requires authentication and you’re not logged in, the server often redirects to an HTML login page instead of returning a JSON 401:
const res = await fetch('/api/account'); // redirected to /login (HTML)
const data = await res.json(); // ❌
A typo in the URL can return a generic HTML page (or a single-page app’s index.html) instead of a JSON endpoint:
const res = await fetch('/api/users'); // ❌ should be /api/user
A WAF, CDN, or corporate proxy can intercept the request and return an HTML “blocked” or “challenge” page.
Check the status and Content-Type before parsing:
const res = await fetch('/api/user');
if (!res.ok) {
throw new Error(`Request failed: ${res.status}`);
}
const contentType = res.headers.get('content-type');
if (!contentType?.includes('application/json')) {
throw new Error(`Expected JSON, got ${contentType}`);
}
const data = await res.json();
Log the raw body to see what you actually received:
const res = await fetch('/api/user');
const text = await res.text();
console.log(text.slice(0, 200)); // inspect the first 200 characters
If the output starts with <!DOCTYPE html> or <html>, you’ve found the problem: the server sent HTML.
Fix the root cause — correct the URL, handle 401s by logging in first, or configure the server to return JSON error responses instead of HTML pages.