Loading JSON into pandas sounds simple — until you hit nested objects, the cryptic orient parameter, or the Expected object or value error. This guide covers the right way to read JSON into a DataFrame, including messy real-world cases.
The basic case
import pandas as pd
df = pd.read_json('data.json')This works when your JSON is an array of flat objects:
[
{"id": 1, "name": "Alice"},
{"id": 2, "name": "Bob"}
]The "Expected object or value" error
This comes from Python's json module, which pandas uses internally. Common causes:
- Malformed JSON (missing brackets, commas, quotes)
- The file is NDJSON (one object per line), not a single array
- The server returned an HTML error page instead of JSON
- Encoding issues or a partial download
First step: validate the file before blaming pandas.
Validate your JSON before loading it
Paste your JSON to confirm it is valid and find the exact error location. Free, private, runs in your browser.
Line-delimited JSON (NDJSON)
Many exports use NDJSON — one object per line, no enclosing array. Use lines=True:
df = pd.read_json('data.ndjson', lines=True)The orient parameter
records— list of dicts (most common)columns— pandas defaultindex— keyed by row indexsplit— separate keys for columns, index, datavalues— a 2D array
df = pd.read_json('data.json', orient='records')Flattening nested JSON
Real API responses are nested. Use json_normalize to flatten them into columns:
import json, pandas as pd
with open('data.json') as f:
raw = json.load(f)
df = pd.json_normalize(raw)Nested keys like user.name become flat columns. Control the record path and metadata:
df = pd.json_normalize(raw, record_path='items', meta=['id'])Reading from an API
import requests, pandas as pd
res = requests.get('https://api.example.com/data')
df = pd.json_normalize(res.json())Check res.status_code and the content type first — an HTML error page triggers "Expected object or value".
Writing back to JSON
df.to_json('output.json', orient='records', indent=2)