JSON is the default API response format. CSV is what spreadsheets, databases, and data pipelines want. Converting between them is routine — but nested JSON makes it less simple than it first appears.
The Simple Case: Flat Arrays
If your JSON is a flat array of objects with uniform keys, conversion is trivial:
``json
[
{"id": 1, "name": "Alice", "role": "engineer"},
{"id": 2, "name": "Bob", "role": "designer"}
]
`
Becomes:
<code>csv
<p>id,name,role</p>
<p>1,Alice,engineer</p>
<p>2,Bob,designer</p>
</code>
Every JSON-to-CSV tool handles this case. The challenge is what comes next.
Nested Objects: Flattening with Dot Notation
Real API responses usually have nested objects:
<code>json
<p>[{"id": 1, "user": {"name": "Alice", "email": "*Emails are not allowed*"}, "total": 99.99}]</p>
</code>
The standard approach is dot-notation flattening: user.name, user.email become column headers.
<code>csv
<p>id,user.name,user.email,total</p>
<p>1,Alice,*Emails are not allowed*,99.99</p>
</code>
This preserves the data and works well with most analysis tools.
Arrays Inside Objects
When a field contains an array, you have three choices:
- Join — tags: ["a","b","c"]
→ "a,b,c" in one cell (most common) - Expand — Create one row per array item (right for one-to-many relationships)
- Index — tags[0]
, tags[1]... as columns (breaks when lengths vary)
Joining works for most cases. Expanding is correct for structured one-to-many data (e.g., order → line items).
CSV Quoting Rules
A common mistake is forgetting that CSV has strict quoting requirements:
- Values containing commas must be quoted: "New York, NY"
- Quotes inside quoted values must be doubled: "He said ""hello"""
- Newlines inside values must be quoted
- The first row is the header by convention
Naive concatenation with commas will break on real-world data.
JavaScript Implementation
`javascript
function flatten(obj, prefix = '') {
return Object.keys(obj).reduce((acc, key) => {
const fullKey = prefix ? ${prefix}.${key} : key;
const val = obj[key];
if (typeof val === 'object' && !Array.isArray(val) && val !== null) {
Object.assign(acc, flatten(val, fullKey));
} else {
acc[fullKey] = Array.isArray(val) ? val.join(', ') : val;
}
return acc;
}, {});
}
function jsonToCSV(data) {
const flat = data.map(row => flatten(row));
const headers = [...new Set(flat.flatMap(Object.keys))];
const escape = v => {
const s = String(v ?? '');
return s.includes(',') || s.includes('"') || s.includes('\n')
? "${s.replace(/"/g, '""')}" : s;
};
return [
headers.join(','),
...flat.map(row => headers.map(h => escape(row[h] ?? '')).join(','))
].join('\n');
}
`
Python Implementation
`python
import csv, json
def flatten(obj, prefix=''):
out = {}
for k, v in obj.items():
key = f"{prefix}.{k}" if prefix else k
if isinstance(v, dict):
out.update(flatten(v, key))
elif isinstance(v, list):
out[key] = ', '.join(str(x) for x in v)
else:
out[key] = v
return out
data = json.load(open('input.json'))
flat = [flatten(row) for row in data]
headers = list(dict.fromkeys(k for row in flat for k in row))
with open('output.csv', 'w', newline='') as f:
w = csv.DictWriter(f, fieldnames=headers, extrasaction='ignore')
w.writeheader()
w.writerows(flat)
`
Quick Conversions in the Browser
For one-off jobs — converting an API response you've copied, or a file from a colleague — writing code is overkill. SnappyTools' JSON to CSV converter handles flat and nested JSON, offers dot-notation flattening, and downloads the result as .csv`. No data is uploaded; processing is client-side.
Summary
- Flat JSON → CSV: trivial, any tool handles it
- Nested objects: flatten with dot notation (most compatible)
- Arrays in objects: join with comma (simplest) or expand to rows (for one-to-many)
- Always quote values with commas, quotes, or newlines
- Browser tool for one-off use: snappytools.app/json-to-csv-converter/
Originally published at https://snappytools.app/json-to-csv-converter/