The Problem: Real JSON Is Never Flat
If you’ve ever tried to load a nested JSON into a pandas DataFrame, you’ve probably seen something like this:
A column filled with dictionaries. Useless for analysis. Enter json_normalize() — but it’s not magic. This guide tackles the five most frustrating failure modes, with fixes you can copy-paste today.
1. Nested Dictionaries: The Basic Case (and the Trap)
Problem: A simple nested dict seems easy, but json_normalize() flattens only one level by default.
Example:
Fix: The function works out of the box for one level of nesting.
Trap: If your nested dict has variable keys, json_normalize will still work but may produce NaN for missing keys. That’s expected.
2. Lists of Dictionaries: Use record_path
Problem: When your data has an array of objects, e.g., orders with line items.
Fix: Specify record_path to the array key.
Problem: Parent fields (like order_id) are lost.
Fix: Add meta parameter.
Trap: If meta key doesn’t exist in every record, you’ll get a KeyError. Use errors='ignore'.
3. Multiple Levels: Nested Arrays Inside Arrays
Problem: Imagine a structure like:
Fix: You cannot flatten more than one record_path directly. Instead, flatten the innermost first, then merge.
Note: record_path accepts a list of keys to navigate deeper: ['sales', 'details'].
4. Missing Keys: KeyError and NaN Handling
Problem: JSON from APIs often has optional fields.
Fix: json_normalize handles missing keys gracefully — it fills with NaN. But if you use meta with a missing key, you get KeyError.
Trap: If the missing key is in a nested dict, e.g., address.street, the errors='ignore' parameter does NOT help. You must handle missing nested values with .get() in a pre-processing step.
5. Arrays That Need explode()
Problem: When record data is not objects but simple values (e.g., tags).
Fix: json_normalize with record_path works for lists of dicts, not simple lists. Use explode() after normalization.
Better: If you have multiple columns to keep, normalize first then explode.
6. API Response with a results Key
Problem: APIs commonly wrap data in results.
Fix: Access the list directly.
But wait — parent metadata? Use meta from the outer dict.
Correct: meta pulls from the same record, not the outer dict. You need to attach status to each record manually.
Data Table: Common Error Patterns
Key Takeaways
- Use
record_pathfor lists of dictionaries. Without it, you get one row per outer record, with dicts in cells. - Use
metato preserve parent fields. Pass the list of parent keys you want. - Handle missing keys early. Use Python
.get()ortry/exceptbefore normalizing if you expect missing nested keys. - For multiple nesting levels, flatten innermost first, then merge. One
json_normalizeper level. - Simple arrays need
explode(), notrecord_path. - Wrapped API results require manual attachment of outer metadata.
metaonly works within the same record. max_levelcontrols depth. Default is None (unlimited). Set to 1 to prevent deep flattening.
How to Apply This
Next time you fetch data from an API:
- Inspect the JSON structure — print keys and types.
- If there’s a list of objects, that’s your
record_path. - List the keys you want to keep from parent levels as
meta. - Run
pd.json_normalize(data, record_path=..., meta=...). - Check for NaN — if unexpected, add error handling.
FAQ
1. Why am I getting a KeyError even though the key exists?
Check if the key is in every record. If one record is missing the key, record_path fails. Use errors='ignore' or filter out those records first.
2. How do I flatten deeply nested JSON with multiple arrays?
Flatten each level sequentially. For example, first flatten the outermost array, then flatten the inner arrays and merge using pd.merge() on a common key.
3. Does json_normalize work with JSON strings?
No, it expects Python dictionaries/lists. Use json.loads() first.
4. Can I flatten only certain fields?
Yes, use max_level to limit depth, then manually select columns. Alternatively, specify meta to include only desired parent fields.
5. What’s the difference between json_normalize and pd.json_normalize?
pd.json_normalize is the modern API (pandas 1.0+). json_normalize is legacy but still works. Use pd.json_normalize.
Conclusion
pd.json_normalize() is powerful but unforgiving. By understanding how record_path, meta, and missing keys interact, you can turn any nested JSON into a clean DataFrame. The next time your API returns a nested mess, you’ll know exactly how to flatten it — without the Google search.
Want more? Check out the official pandas documentation for advanced parameters like sep and max_level.









