Migrate to RunxBuild and earn up to $50 in hosting credit on your first deposit.

Calculate your savings
unxBuild

json.loads in Python: loads vs load, and the Errors You Will Actually Hit

Sean

Platform Writer

Jul 21, 2026
7 min read

json.loads(s) takes a JSON string and returns the equivalent Python object - a dict, a list, a number, whatever the JSON described. The trailing s is for ‘string’, and that is the entire difference between loads and load: load reads from a file object, loads reads from a string already in memory. Everything else people get wrong about json.loads is really about the input - trusting it to be valid JSON when it is a network response that can be empty, malformed, or an HTML error page.

json.loads in Python: loads vs load, and the Errors You Will Actually Hit

It is one function call. The interesting part is the two-minute mental model of loads vs load and the handful of exceptions worth catching before they reach a user.

Table of contents

loads vs load: string vs file

import json

# loads - parse a STRING
data = json.loads('{"name": "Ada", "age": 36}')
data["name"]          # 'Ada' - it is a normal dict now

# load - parse a FILE object
with open("config.json") as f:
    config = json.load(f)

The mnemonic that sticks: the s is the string. loads/dumps work with strings; load/dump work with file objects. Mixing them up gives you a TypeError about expecting a string or a file, and once you have the mnemonic you never do it again.

How JSON types map to Python

  • JSON object -> Python dict
  • JSON array -> Python list
  • JSON string -> str
  • JSON number -> int or float
  • true / false -> True / False
  • null -> None

The two that surprise people: JSON null becomes None, so you check if value is None, not if value == 'null'. And JSON object keys are always strings, so {"1": "a"} parses to {'1': 'a'} with a string key - there is no integer-key JSON.

The errors worth catching

json.loads raises json.JSONDecodeError (a subclass of ValueError) when the input is not valid JSON. In real systems the input is often a response body, and response bodies are empty, truncated, or HTML far more often than anyone plans for.

import json

def safe_parse(text):
    try:
        return json.loads(text)
    except json.JSONDecodeError as e:
        # e.pos, e.lineno, e.colno tell you WHERE it broke
        print(f"Bad JSON at line {e.lineno}: {e.msg}")
        return None

Catch JSONDecodeError, not a bare except. The exception carries pos, lineno, and colno, which point at exactly where the parse failed - invaluable when the input is a 40 KB payload and character 12,003 is a stray comma.

Common real-world traps

  • Single quotes. JSON requires double quotes. json.loads("{'a': 1}") fails. If you are parsing Python-dict-looking text, that is not JSON.
  • Trailing commas. Legal in Python literals, illegal in JSON. [1, 2,] raises.
  • Empty string. json.loads("") raises; an empty response body is not valid JSON, so guard for it.
  • NaN and Infinity. Python’s parser accepts them by default, but they are not standard JSON and other systems will reject them - pass parse_constant or validate if strictness matters.

Do not use eval, ever

The old advice to parse dict-like strings with eval is a remote-code-execution hole. eval on attacker-controlled text runs that text as Python. If the string is Python-literal syntax rather than JSON (single quotes, None, True), use ast.literal_eval, which only evaluates literals and cannot call functions.

import ast
ast.literal_eval("{'a': 1, 'b': None}")   # safe: {'a': 1, 'b': None}

For actual JSON, use json.loads. For Python-literal text, use ast.literal_eval. There is no situation where eval is the right answer for parsing data.

How this fits the rest of the stack

Trusting an input to be well-formed is the quiet assumption that turns into a 3 a.m. incident - a partner API returns HTML during an outage and your parser throws in a code path nobody guarded. Robust services validate at the boundary, and they run where the logs make the failure obvious. The RunxBuild hosting calculator sizes the API, database, and bandwidth for a service like that as separate line items, and the RunxBuild dashboard is where the deploy logs show the exact exception and the request that caused it.

Useful related references:

FAQ

What is the difference between json.loads and json.load?

json.loads parses a JSON string that is already in memory; json.load reads and parses from a file object. The s stands for string. The same pairing applies to dumps (to a string) and dump (to a file). Passing a file to loads, or a string to load, raises a TypeError, so the mnemonic is worth memorizing.

What does json.loads return?

A native Python object matching the JSON structure: a JSON object becomes a dict, an array becomes a list, strings become str, numbers become int or float, true/false become booleans, and null becomes None. After parsing you work with ordinary Python data types, so a parsed object supports normal dict and list operations.

What error does json.loads raise on invalid input?

json.JSONDecodeError, which subclasses ValueError. It carries msg, pos, lineno, and colno so you can see exactly where parsing failed. Catch JSONDecodeError specifically rather than using a bare except, because the position information is what lets you diagnose a malformed payload quickly.

Why does json.loads fail on a dictionary with single quotes?

Because JSON requires double quotes around keys and string values; single quotes are not valid JSON. Text like {‘a’: 1} is Python literal syntax, not JSON. Parse that with ast.literal_eval, which safely evaluates Python literals. Reserve json.loads for genuine JSON with double quotes, true/false, and null.

Is it safe to parse JSON with eval in Python?

No. eval runs its argument as Python code, so on untrusted input it is a remote-code-execution vulnerability. Use json.loads for JSON, or ast.literal_eval for Python-literal strings, since literal_eval only evaluates literals and cannot call functions or run arbitrary code. There is never a good reason to use eval for parsing data.

#json.loads python#python#json#parsing#dev-infra