Home / Guides / How to Convert a JSON to CSV

How to Convert a JSON to CSV

Converting JSON to CSV means turning an array of objects into a table: one row per object, one column per key, with the keys written as a header row. Everything else on this page is about the two places that mapping has to make a decision — nested objects and arrays — and the one place your spreadsheet will undo your work.

The short answer

Put your JSON into a converter, press Convert, download the .csv. There is nothing to install and nothing to configure if your JSON is a plain array of flat objects — the default settings are already correct.

Convert JSON to CSV now

Runs entirely in your browser. Your data is never uploaded, so there is no file size limit and no account.

You only need to change a setting in two cases: if your objects contain nested objects (turn on Flatten nested to get one column per leaf value), or if your spreadsheet expects semicolons instead of commas.

Your JSON
[
  { "id": 1, "name": "Alice",
    "user": { "city": "NYC" } },
  { "id": 2, "name": "Bob",
    "user": { "city": "LA" } }
]
Your CSV
id,name,user.city
1,Alice,NYC
2,Bob,LA

That is the whole conversion. The rest of this page is the detail that decides whether the file you download is the file you actually wanted.

On this page
  1. What the conversion actually does
  2. Step 1 — Get the JSON in
  3. Step 2 — The two settings that change the output
  4. Step 3 — Download it and open it without wrecking it
  5. What actually comes out: 24 input shapes, measured
  6. A 60-second check before you trust the file
  7. When a converter is the wrong tool
  8. Questions people ask next

What the conversion actually does

CSV has no nesting, no types and no metadata other than an optional header row. JSON has all three. So a conversion is not a translation between equals — it is a flattening, and a flattening has to make decisions. There are only three, and they are always the same three:

  1. What is a row? Each element of the array is one row. If your JSON is a single object rather than an array, it becomes a single row.
  2. What is a column? Each key is one column. Nested keys get joined into a path with a dot, so {"user":{"city":"NYC"}} produces a column named user.city.
  3. What happens when objects differ? The header row is the union of every key the converter sees, in the order the keys first appear. An object that is missing a key leaves an empty cell in that column.
A JSON array of two objects, each with an id and a nested user.city, mapping to a CSV file with the header row id,user.city and two data rows.
Two objects in, two rows out. Nested keys become dot-path columns; a key missing from one object leaves an empty cell rather than shifting the rest of the row.

Everything that surprises people about JSON-to-CSV conversion is one of those three rules being applied in a way they did not expect. The most common surprise is rule two, because arrays are not covered by it: an array value never becomes columns. There is no sensible way to turn "tags":["red","blue"] into a fixed grid, so a converter writes the array into one cell as JSON text. If your JSON is full of arrays of objects, a single CSV is the wrong target format and no converter will make it right — more on that under when a converter is the wrong tool.

Step 1 — Get the JSON in

There are three ways in, and they all end up at the same place:

Four input shapes are accepted, and the converter works out which one you have:

Input shapeExampleResult
Array of objects[{"a":1},{"a":2}]Two rows. The normal case.
Single object{"a":1,"b":2}One row, wrapped in a table for you.
Object wrapping one array{"data":[{"a":1}]}The wrapper is unwrapped, the array becomes the rows.
JSONLines / NDJSON{"a":1}
{"a":2}
Detected when every non-empty line parses as JSON. One row per line.

What is rejected. Empty input, an empty array [], and anything that is not strict JSON. Trailing commas, single quotes, unquoted keys, comments and NaN are all invalid JSON even though JavaScript and Python will happily parse them — the converter will stop with the parser's own message and the character position, which is usually enough to find the problem in a few seconds.

An empty array is worth calling out separately: it produces an error rather than an empty file. That is deliberate, because a zero-byte CSV that opens as a blank spreadsheet looks like a bug in your pipeline rather than a bug in your data.

Step 2 — The two settings that change the output

Flatten nested: on or off

This is the setting that decides whether your CSV is usable. Both answers are correct in different situations, so it is worth seeing both outputs side by side.

Flatten nested ON — one column per leaf
id,user.city,user.zip
1,NYC,10001
Flatten nested OFF — one cell holds the object
id,user
1,"{""city"":""NYC"",""zip"":""10001""}"

With flattening on, every nested object is expanded to its leaves and the depth is unlimited — {"a":{"b":{"c":1}}} becomes a single column named a.b.c. That is what you want for analysis, sorting and pivot tables.

With flattening off, the nested value is written into its cell as a JSON string. That is the right choice when you intend to convert the CSV back to JSON later and need the structure preserved exactly, because the cell round-trips. It is the wrong choice for anything you want to read: notice the doubled quotes in the example above. Every " inside a CSV field has to be escaped by doubling it, so {"city":"NYC"} is stored as "{""city"":""NYC""}". That is correct CSV, and it is unreadable in a spreadsheet. If you see doubled quotes all over your output, that is the signal that flattening is off and should be on.

Delimiter

Comma is the default and is what almost everything expects. Change it in one situation: if your computer's regional settings use a comma as the decimal separator, Excel expects a semicolon-delimited file and will dump a comma-separated one into a single column. Tab and pipe are also offered, and a custom character is available for the rare file that needs one.

The delimiter also decides when a field has to be quoted. With a comma delimiter, the value Berlin, DE is written as "Berlin, DE" because it contains the delimiter. Switch to semicolons and the same value needs no quotes at all, because a comma is no longer special. That is not a bug in either case; it is the rule being applied correctly to two different files.

Header row and force quotes

Leave Header row on unless the file is being consumed by something that has its own schema and expects bare data rows. Force quotes wraps every field — including numbers — in double quotes. It is useful when a downstream parser is happier with a uniform file, and it is the wrong default because it turns numbers into text for strict importers.

Step 3 — Download it and open it without wrecking it

The conversion is done. What happens next is where most people conclude that "the converter produced a broken file", when in fact the CSV was correct and the spreadsheet changed it on the way in.

The file is written as UTF-8 with no byte order mark, with CRLF line endings and RFC 4180 quoting. Those are the right choices for the file itself — no BOM is what makes a CSV safe to read in Python, psql, jq and every other tool that would otherwise choke on three invisible bytes at the start of the header. It does mean Excel on Windows has to guess the encoding, and it guesses from your system locale.

So the fix is not to change the file. The fix is to stop double-clicking it:

  1. Excel (Windows or Mac): go to Data → From Text/CSV, choose the file, set the delimiter, then click Transform Data. In the power query editor, set the columns that hold identifiers to type Text instead of Whole Number, then load. The encoding dropdown on the first screen is where you pick UTF-8 if your data has accents or emoji.
  2. Google Sheets: File → Import → Upload, then untick "Convert text to numbers, dates and formulas". Leaving it ticked is what silently turns a ZIP code into a number and a 17-digit order ID into scientific notation.
  3. Anything else: open the file in a text editor first. If the header row and the first two data rows look right as plain text, the file is right, and any weirdness after that belongs to the program you opened it in.

The three things a spreadsheet does to a correct CSV. It strips leading zeros, because it decides 02116 is the number 2116. It converts any integer longer than 15 digits into scientific notation and zeroes the tail, because that is the limit of its numeric storage. And it converts date-like text such as 2026-09-25 into a date, which is usually fine until an ambiguous 03/04/2026 gets flipped by the locale.

None of these are the converter's doing, and none of them can be fixed by the converter — the file is already correct. Import as text and all three disappear.

What actually comes out: 24 input shapes, measured

Conversion rules are easy to describe and easy to get wrong in the details. So rather than describe them, here is the exact output produced by the converter on this site, run against 24 input shapes. The middle column is the literal output, byte for byte.

InputOutputWhy it matters
Rows with different keys[{"a":1,"b":2},{"b":3,"c":4}]
→
a,b,c
1,2,
,3,4
Columns are the union of all keys, ordered by first appearance. Missing values leave empty cells; nothing shifts out of alignment.
Ragged rows[{"id":1,"email":"[email protected]"},{"id":2}]
→
id,email
1,[email protected]
2,
The short row gets a trailing empty field. Row count is preserved, which is what you want for a diff against the source.
Nested object[{"id":1,"user":{"city":"NYC","zip":"10001"}}]
→
id,user.city,user.zip
1,NYC,10001
Dot notation, one column per leaf, flattened to unlimited depth.
Array value[{"id":1,"tags":["red","blue"]}]
→
id,tags
1,"[""red"",""blue""]"
Arrays are never split into columns, in either flatten mode. The cell holds JSON text, so quotes are doubled.
Array of objects[{"id":1,"items":[{"sku":"A","qty":2}]}]
→
id,items
1,"[{""sku"":""A"",""qty"":2}]"
This is the case that needs a different shape of output entirely. See when a converter is the wrong tool.
null[{"id":1,"note":null}]
→
id,note
1,
JSON null becomes an empty field, which is what CSV means by "no value".
boolean[{"id":1,"active":true}]
→
id,active
1,true
Lowercase true/false, not TRUE/FALSE and not 1/0.
Comma inside a string[{"addr":"Berlin, DE"}]
→
addr
"Berlin, DE"
Quoted because it contains the delimiter. With a semicolon delimiter the same value needs no quotes.
Quote inside a string[{"name":"He said \"hi\""}]
→
name
"He said ""hi"""
RFC 4180 escaping: quotes are doubled. Every spreadsheet reads it back correctly.
Newline inside a string[{"note":"line1\nline2"}]
→
note
"line1
line2"
A quoted field can contain a real newline, so "one line per row" is not a safe way to parse CSV. Use a parser.
Accents and emoji[{"name":"Jos\u00e9 \ud83d\ude80"}]
→
name
José 🚀
Written as raw UTF-8, never as \u escapes. This is the case where a BOM-less file and a locale-guessing Excel disagree.
Leading-zero string[{"zip":"02116"}]
→
zip
02116
Survives the conversion intact because it is a JSON string. Excel will still remove the zero on open unless you import as text.
Integer above 253[{"order_id":9007199254740993}]
→
order_id
9007199254740992
The last digit changed. A JSON number cannot represent this value exactly, so the damage is done before any CSV is written.
19-digit integer[{"id":12345678901234567890}]
→
id
12345678901234567000
Same cause, larger error. Quote long IDs in the source JSON — "12345678901234567890" — and they come out exact.
Array of scalars[1,2,3]
→
_value
1
2
3
Non-object rows get a placeholder column name. Valid JSON, but check the header before you ship the file.
Wrapper object{"data":[{"a":1},{"a":2}]}
→
a
1
2
The wrapper is dropped. If you needed the wrapper name as a column, it has to be added downstream.
JSONLines{"a":1,"b":2}
{"a":3,"b":4}

→
a,b
1,2
3,4
Auto-detected when every non-empty line parses as JSON, so an NDJSON log drops straight in.
Trailing comma[{"a":1},]
→
Invalid JSON
Rejected with the parser's position. Fix the JSON, do not work around the converter.
Empty array[]
→
No rows to convert
An error rather than a zero-byte file, so an empty result is loud instead of silent.

Three invariants hold across all 24 cases and are worth memorising, because they are what you check when an output looks wrong:

A 60-second check before you trust the file

Open the downloaded CSV in a text editor, not a spreadsheet, and look at the header row and the first two data rows. Six things cover almost every real problem:

  1. Count the header columns and compare with the widest row. They should match. If a data row has more fields than the header, your JSON has a key the header does not, which usually means two different object shapes are mixed in one array.
  2. Look for doubled quotes. Any "" in the output means a nested object or array was written as JSON text. Turn flattening on if you wanted columns.
  3. Check the longest numeric ID. If it ends in zeros it was never a safe JSON number. Quote it in the source and convert again.
  4. Check the first character of every field if the JSON came from users or an untrusted API. A cell starting with =, +, - or @ is treated as a formula by Excel and Google Sheets. This is the CSV injection class of bug, and quoting does not prevent it — a quoted "=1+1" is still evaluated. Strip or prefix those characters in the source data, or import the file as text.
  5. Check the row count against the length of your source array. If they differ, the input was not the shape you thought — most often a wrapper object that was auto-unwrapped.
  6. Check for a stray BOM if another tool is reading the file. There is none, which is deliberate; if a downstream tool demands one, add it there rather than here.

When a converter is the wrong tool

A converter is the right answer for one-off and occasional conversions, for inspecting an API response, and for anything up to a few hundred thousand rows. It is the wrong answer in three situations, and recognising them early saves a lot of frustration:

Questions people ask next

How do I convert a JSON file to CSV?

Open the file in a JSON to CSV converter, choose how nested keys are handled, and download the .csv. A JSON array of objects becomes a table with one row per object and one column per key. The header row lists the keys in the order they first appear.

Can I convert nested JSON to CSV?

Yes. With Flatten nested on, every nested object is expanded into its own column using dot notation, at any depth, so {"user":{"city":"NYC"}} becomes the column user.city. With it off, the whole nested value is written into a single cell as a JSON string.

Why does my CSV look wrong when I open it in Excel?

Excel guesses the data type and encoding of every column when you double-click a CSV. It strips leading zeros, turns long IDs into scientific notation, converts date-like text, and misreads UTF-8 accents unless the file starts with a byte order mark. Import the file with Data → From Text/CSV and set the columns to Text instead of double-clicking it.

Does converting JSON to CSV lose data?

The shape changes, so information can be lost in three places. Nested objects can be flattened into separate columns or crammed into one cell as text. Arrays have no column equivalent and are always written as JSON text in a single cell. Integers larger than 9,007,199,254,740,991 cannot be represented exactly as a JSON number, so quote long IDs in the source JSON to keep them intact.

Can I convert JSON to CSV without uploading my file?

Yes. A browser-based converter runs the whole conversion in JavaScript on your machine, so the JSON never leaves your device. There is no upload, no account and no server-side size limit, and the tool keeps working if you disconnect from the internet after the page has loaded.

How do I keep leading zeros in a CSV?

Store the value as a JSON string — "02116" rather than 02116, which is not valid JSON anyway. The converter then writes 02116 unchanged. Excel still removes the zeros when you double-click the file, so import it with Data → From Text/CSV and set that column's type to Text.

What is the difference between JSON and CSV?

JSON is a nested, typed, self-describing format where every value carries its own key. CSV is a flat grid of rows and columns with no types, no nesting and no metadata beyond an optional header row. Converting between them means flattening a tree into a table, which is why nested objects and arrays need a rule.