JSON to CSV Converter
Convert JSON arrays to CSV format instantly
Columns
APIs and configuration files speak JSON; spreadsheets, database imports, and most reporting tools speak CSV. This converter bridges the two: paste a JSON array of objects, and it produces a flattened, properly escaped table with a header row. Nested objects become dot-notation columns, values with commas or quotes are wrapped correctly, and the result opens cleanly in Excel, Google Sheets, or an import script.
How to Use
- Paste a JSON array of objects into the input box, or click the sample button to load a three-row example.
- Press convert. The errors panel appears if the JSON is malformed or not an array; otherwise the output fills in.
- Check the stats. A row count and column count are shown, along with a chip for every column name.
- Copy the CSV with the copy button and paste it into a spreadsheet or a
.csvfile.
What the Converter Accepts
The top level must be an array. Each element should be an object; if an element is a primitive — a string or a number — it contributes an empty row and no columns. An empty array is a valid JSON document but produces an “Input array is empty” message, because there is no header to build. Malformed JSON is reported with the parser’s own error text. The converter never guesses at a wrapper object, so {"data": [...]} must be unwrapped first.
Values keep their JSON types until the moment they are written. A number is serialized with JavaScript’s default number-to-string conversion, so very large integers can lose precision before they ever reach the CSV — a 20-digit ID may come out rounded or in exponential notation. Dates arrive as strings and pass through unchanged, so whatever ISO format the API used is what the column contains. If either matters, convert the value to a string in the source JSON first. Deeply nested objects are fine; each additional level simply lengthens the dotted key.
Worked Example: Nested API Data
Input:
[
{ "id": 1, "name": "Alice", "address": { "city": "Austin", "zip": "78701" } },
{ "id": 2, "name": "Bob", "address": { "city": "Denver" }, "tags": ["admin", "beta"] }
]
The first row flattens to id, name, address.city, and address.zip. The second has no address.zip but adds tags. Columns therefore appear in the order id, name, address.city, address.zip, tags, and the output is:
id,name,address.city,address.zip,tags
1,Alice,Austin,78701,
2,Bob,Denver,,"admin,beta"
Note the trailing empty cell for Alice’s missing tags, and the quoted "admin,beta" because the joined array contains a comma.
How Flattening and Escaping Work
Flattening recurses into plain objects only. Each nested level appends its key to the parent with a dot, so user.contact.email captures three levels. Arrays are treated as leaf values. Escaping follows RFC 4180: a field is wrapped in double quotes if it contains a comma, a double quote, a carriage return, or a newline, and any internal double quote is doubled. So the value He said "hi" becomes "He said ""hi""" in the output. Fields that need none of this are written bare.
The header row is escaped with exactly the same function as the data, which matters when a key itself contains a comma or a quote — rare from APIs, but possible with user-generated field names. Columns are keyed by their flattened path, so two source objects that each contain address.city merge into one column even if the objects are otherwise unrelated. Because the union is computed from every row before any data is written, a key that appears only in the very last record still becomes a column, and it will sit near the end of the list if it is not seen elsewhere.
Common Mistakes and Fixes
- Passing a wrapper object. Unwrap
{ "results": [...] }to just the array before converting. - Expecting arrays to expand into columns. They collapse to a comma-joined string; pre-process array fields into named columns if you need each item separated.
- Letting one record bloat the schema. A single row with an extra nested field adds a column that is blank for every other row.
- Forgetting the JSON-vs-CSV null trap. Missing keys,
null, and empty strings all render as empty cells, so downstream logic cannot tell them apart. - Opening the copy with Excel’s default double-click. For files containing non-ASCII text, use Data → From Text/CSV so you can choose UTF-8 and avoid garbled characters.
- Assuming numbers survive intact. Large IDs can be rounded by JavaScript’s number handling; quote them in the source JSON to keep them exact.
- Expecting a UTF-8 byte-order mark. The output has none, which is usually correct for code and databases but can make Excel misread non-ASCII text unless imported through the Text/CSV dialog.
When CSV Is the Wrong Target
CSV is a flat table, so it cannot represent recursion or repeated groups without duplication. If your JSON is deeply nested or contains arrays of varying length, a spreadsheet will either lose structure or become unreadable. In those cases, keep the data in JSON, load it into a database, or flatten only the fields you actually need into separate tables. CSV is the right choice when every record has roughly the same flat shape and a human or a spreadsheet will read it.
One practical middle ground is to emit two related CSVs — one for parent records and one for the nested collection — sharing an ID column, then join them in the spreadsheet or database. That preserves the repeating structure a single flat table would otherwise flatten beyond recognition. If you only need a quick human-readable view of a nested API response, a JSON formatter or viewer will serve you better than any CSV will.
Related Tools
Frequently Asked Questions
Why does my conversion fail with a non-array error?
The input must be a JSON array of objects, and a very common shape from APIs is a wrapper object such as {"results": [...]}. The converter rejects that because the top level is not an array. Unwrap it first — either copy out just the inner array, or run it through a JSON formatter to expose the array, then paste that.
How are arrays inside a row handled?
Nested objects are recursively flattened with dot-notation keys, but arrays are left as single values. An array such as ["admin", "beta"] is stringified by the CSV escaper as admin,beta, and because it contains a comma the whole cell is quoted: "admin,beta". An array of objects stringifies to the unhelpful [object Object], so flatten or join those before converting.
What happens when rows have different keys?
The converter gathers every unique key across all rows in the order it first appears, and that union becomes the header. A row missing a key gets an empty cell in that column. This makes optional fields easy to handle, but it also means one stray key in a single record adds a mostly empty column to the whole sheet.
Are null, missing, and boolean values written differently?
Yes. null and undefined become empty cells. Numbers and booleans are written as their literal text, so true stays true and 42 stays 42. An empty string also becomes an empty cell, which means a present-but-empty value and a missing value look identical in the CSV — something to keep in mind if the distinction matters downstream.
Can I download a .csv file, or only copy the text?
This tool gives you a copy button for the generated CSV; it does not include a file-download control. Paste the result into a blank text file with a .csv extension, or paste directly into a spreadsheet cell and use its text-to-columns import. The output is standard comma-separated text with a header row.
Does the converter sort or reorder my columns?
No. Column order follows first appearance across the array of objects, and row order is preserved exactly as it appears in the input. If you need alphabetical headers or rows sorted by a field, sort the JSON before converting rather than expecting the converter to do it.
Reviewed by the HandyTools Hub team · Last updated