Why it happens
Python raises JSONDecodeError: Expecting value: line 1 column 1 (char 0) when the first character of the text cannot start a JSON value. Three kinds of input cause it, most common first.
1. An HTML page. The server sent an error, login or block page, like the Flask 500 page in the checker above. Some sites block the default python-requests User-Agent and answer with an HTML 403 page.
2. Nothing at all. json.loads("") fails at char 0. Empty text comes from a 204 No Content response, a HEAD request, an empty file, or a file object that was already read:
import json
with open("settings.json") as f:
print(f.read()) # consumes the file
json.load(f) # reads "" -> Expecting value: line 1 column 1 (char 0)
3. Text that is not JSON. A plain-text body such as Internal Server Error, a streamed line that still starts with data: , a file name passed to json.loads("settings.json") instead of the file’s content, or a UTF-8 byte order mark decoded as .
A column above 1 means the start was fine: json.loads("[None]") fails at line 1 column 2 (char 1), because JSON writes it null.
How to fix JSONDecodeError: Expecting value
Look at what arrived before parsing it. If you copy a response by hand, the JSON validator names HTML, empty input and invisible characters instead of only giving a position.
requests
import requests
resp = requests.get("https://api.example.com/v2/invoices/7781", timeout=10)
resp.raise_for_status() # 4xx and 5xx error pages stop here
if not resp.content: # 204 No Content or an empty 200
invoice = None
elif "json" not in resp.headers.get("Content-Type", ""):
raise ValueError(f"Expected JSON, got {resp.headers.get('Content-Type')}: {resp.text[:200]!r}")
else:
invoice = resp.json()
httpx
r.json() is plain json.loads(r.content), so httpx raises the standard json.JSONDecodeError. Log the response when it fails:
import json
import httpx
r = httpx.get("https://api.example.com/v2/invoices/7781")
try:
invoice = r.json()
except json.JSONDecodeError:
print(r.status_code, r.headers.get("content-type"), r.text[:200])
raise
Files
import json
from pathlib import Path
text = Path("settings.json").read_text(encoding="utf-8-sig") # drops a BOM if present
settings = json.loads(text) if text.strip() else {}
utf-8-sig reads files with or without a byte order mark. With plain utf-8, a BOM raises Unexpected UTF-8 BOM (decode using utf-8-sig) instead. On Windows, Python 3.14 and older open files in the ANSI code page by default, which turns the BOM into  and gives this error.
Flask
request.get_json() answers a body that does not parse with 400 Bad Request (debug mode adds Failed to decode JSON object: ...) and a non-JSON Content-Type with 415. A form post leaves request.data empty, because the fields go to request.form, so json.loads(request.data) fails at char 0. Reply with a clear 400 instead:
from flask import Flask, abort, request
app = Flask(__name__)
@app.post("/invoices")
def create_invoice():
payload = request.get_json(silent=True)
if payload is None:
abort(400, description="Send a JSON body with Content-Type: application/json")
return {"received": payload}, 201
Django
json.loads(request.body) fails the same way when the client sends form data or nothing. Check the content type first:
import json
from django.http import HttpResponseBadRequest, JsonResponse
def create_invoice(request):
if request.content_type != "application/json":
return HttpResponseBadRequest("Expected Content-Type: application/json")
try:
payload = json.loads(request.body)
except json.JSONDecodeError as exc:
return HttpResponseBadRequest(f"Invalid JSON: {exc}")
return JsonResponse({"received": payload}, status=201)
Django REST framework’s JSON parse error - Expecting value response means the client sent Content-Type: application/json with a body that is not JSON: fix the client.
Same error in other languages
- JavaScript: HTML gives Unexpected token ‘<’, “<!DOCTYPE “… is not valid JSON; an empty string gives Unexpected end of JSON input.
- Go: invalid character ‘<’ looking for beginning of value.
- Safari: JSON Parse error: Unrecognized token ‘<’.
Other parser messages, grouped by language: common JSON errors.