---
url: https://findutils.com/guides/json-flatten-guide
title: "JSON Flatten: Turn Nested JSON into Clean CSV Columns"
description: "Flatten nested JSON into a CSV with dot-path columns like address.city using the free FindUtils JSON Flatten tool. Arrays, depth, Excel tips and pitfalls."
category: converters
content_type: guide
guide_type: subtopic
cluster: data-conversion
locale: en
read_time: 9
status: published
author: "olgunozoktas"
published_at: 2026-10-20T08:08:00Z
excerpt: "Paste an API response and get a spreadsheet in which every nested key is its own column: address.city, address.zip, tags[0]. This guide covers where the rows come from, the three ways to write arrays, the depth limit, opening the file in Excel, and the pitfalls that silently change your data."
tag_ids: ["json", "csv", "json-flatten", "data-conversion", "api"]
tags: ["JSON", "CSV", "JSON Flatten", "Data Conversion", "API"]
primary_keyword: "flatten json to csv"
secondary_keywords: ["json flatten", "nested json to csv", "json dot notation columns", "flatten json array", "api json to excel", "json_normalize alternative"]
tool_tag: "json-flatten"
related_tool: "json-flatten"
related_tools: ["json-flatten", "json-to-csv", "ndjson-to-csv", "json-formatter", "json-path-finder", "csv-viewer", "csv-to-excel", "json-to-markdown-table"]
updated_at: "2026-10-20T08:08:00Z"
og_image: "/images/content/guides/json-flatten-guide-cover-20261020.webp"
image_alt: "A stack of nested boxes framed by curly braces unfolds along dotted guide lines into a flat grid of spreadsheet cells, each column topped by a colored label bar."
---

To flatten JSON to CSV, every nested value gets a column named after the path that leads to it. `{"address": {"city": "London"}}` becomes a column called `address.city` with the value London. Paste your JSON into the FindUtils [JSON Flatten](/convert/json-flatten/) tool and it does this for every record: it finds the array of rows, writes one dot-path column per nested key, and gives you a CSV you can open in Excel or Google Sheets. It is free, needs no signup, and on the page the JSON is processed in your browser.

This guide shows how the tool decides what a row is, compares the three ways it can write arrays, explains the depth limit, and lists the pitfalls that can quietly change your data on the way to a spreadsheet.

## What Does Flattening JSON Mean?

JSON nests: a user has an address object, an order has a customer and a list of lines. A spreadsheet has one value per cell and no nesting at all. Flattening walks down each record and writes every leaf value under its full path:

| Nested JSON | Flat column | Value |
|-------------|-------------|-------|
| `{"id": 1}` | `id` | 1 |
| `{"address": {"city": "London"}}` | `address.city` | London |
| `{"customer": {"geo": {"lat": 51.5}}}` | `customer.geo.lat` | 51.5 |
| `{"tags": ["admin", "beta"]}` | `tags` (or `tags[0]`, `tags[1]`) | admin\|beta (or admin, beta) |
| `{"note": null}` | `note` | empty cell |

Objects use a dot between keys. Array positions use square brackets, as in `tags[0]`, so a position can never be confused with an object key that happens to be a number. The header row is the union of the keys found in all records, in the order they first appear. A record that lacks a key gets an empty cell in that column.

## How to Use JSON Flatten

### Step 1: Paste or Drop the JSON

Open [JSON Flatten](/convert/json-flatten/) and paste the JSON into the Nested JSON panel, upload a `.json` file, or drop it on the panel. Click the sample button to try it with three example records first. The CSV appears as soon as the JSON parses. If it does not parse, the error shows the line and column of the problem; the [JSON Formatter](/developers/json-formatter/) helps you find and fix it.

### Step 2: Check Which Rows Were Found

The status line under the panels says what the tool used as rows, plus the row and column counts. The rules:

- A top-level array of objects gives one row per object.
- An object that wraps the array under `data`, `results`, `items` or `records`, or one level below one of them such as `data.items`, is unwrapped for you. The status line names the path, for example "3 objects at data".
- Anywhere else, type the path into Array path: `response.users` or `pages[0].rows`. Without it, `{"response": {"users": [...]}}` is treated as one object and the whole user list ends up as JSON text in one `response.users` cell.
- A single object with no such array becomes one row.
- An array of plain values such as `[1, 2, 3]` becomes one column named `value`.

If you are not sure of the path, test it in the [JSON Path Finder](/developers/json-path-finder/) first: when `$.response.users` returns your list, that is the path. Array path accepts it with or without the leading `$.`.

### Step 3: Choose How Arrays Are Written

This choice changes your columns the most, so it has its own section below. Join with | is the default.

### Step 4: Set the Depth

Depth caps how many levels a column name may have. All levels, the default, flattens everything. Choose 1 to 5 levels to stop earlier; anything below the limit stays as JSON text in its cell, and a note counts how many cells that affected.

### Step 5: Pick the Separator and Download

Choose Comma, Semicolon or Tab, decide whether you want a header row, and download. The file is named after your upload with `-flat.csv` added. For a big result the panel previews the first 200 lines once the CSV passes 2,000 lines; Copy and Download always use the whole file.

## The Three Array Modes Compared

Take three user records, the same ones the tool's sample uses:

```json
{
  "data": [
    { "id": 1, "name": "Ada Lovelace", "address": { "city": "London", "zip": "NW1 6XE" }, "tags": ["admin", "beta"] },
    { "id": 2, "name": "Grace Hopper", "address": { "city": "Arlington", "zip": "22202" }, "tags": ["editor"] },
    { "id": 3, "name": "Alan Turing", "address": { "city": "Wilmslow" }, "tags": [] }
  ],
  "page": 1
}
```

The tool finds the array at `data` and ignores `page`. Here is the `tags` part of the output in each mode:

| Mode | Columns for tags | Row 1 | Row 2 | Row 3 |
|------|------------------|-------|-------|-------|
| Join with \| (default) | `tags` | admin\|beta | editor | empty |
| JSON cell | `tags` | `["admin","beta"]` | `["editor"]` | `[]` |
| Indexed columns | `tags[0]`, `tags[1]` | admin, beta | editor, empty | empty, empty |

The rest of the columns are the same in every mode: `id`, `name`, `address.city`, `address.zip`. Alan Turing has no zip, so his `address.zip` cell is empty.

Which mode to use:

- **Join with |** when the list is short labels you want to read or filter with "contains". It is the most compact.
- **JSON cell** when the exact array must survive, for example to load it back into another system. It is lossless.
- **Indexed columns** when each position means something, such as `scores[0]` for the first attempt, or when you need every value in its own cell for formulas.

## Arrays of Objects: Order Lines and Similar Data

An array of objects needs more care. Take one order:

```json
[{ "order": "A-1", "customer": { "name": "Ada", "geo": { "lat": 51.5, "lng": -0.1 } },
   "lines": [{ "sku": "K-1", "qty": 2 }, { "sku": "K-2", "qty": 1 }], "note": null }]
```

Under Join with |, the `lines` cell keeps the whole list as JSON text, `[{"sku":"K-1","qty":2},{"sku":"K-2","qty":1}]`, and a note tells you how many cells hold an array of objects. Under Indexed columns the tool spreads it out:

| order | customer.name | customer.geo.lat | customer.geo.lng | lines[0].sku | lines[0].qty | lines[1].sku | lines[1].qty | note |
|-------|---------------|------------------|------------------|--------------|--------------|--------------|--------------|------|
| A-1 | Ada | 51.5 | -0.1 | K-1 | 2 | K-2 | 1 | |

That works for a few lines, but an order with 40 lines produces 80 columns. No mode turns array items into extra rows. If you need one row per order line, reshape the JSON first. With the [jq](https://jqlang.org/manual/) command-line tool:

```bash
jq '[.[] | . as $o | .lines[] | {order: $o.order, customer: $o.customer, sku, qty}]' orders.json
```

That writes one object per line with the order and customer copied onto each, and JSON Flatten then gives you `order`, `customer.name`, `sku` and `qty` columns with one row per line. In Python, pandas' [json_normalize](https://pandas.pydata.org/docs/reference/api/pandas.json_normalize.html) does the same with `record_path="lines"` and `meta=["order", ["customer", "name"]]`.

## How the Depth Limit Works

Depth counts the parts of a column name. `address.city` has two parts, `customer.geo.lat` has three. With the order above:

| Depth | Customer columns you get |
|-------|--------------------------|
| All levels | `customer.name`, `customer.geo.lat`, `customer.geo.lng` |
| 2 levels | `customer.name`, `customer.geo` holding `{"lat":51.5,"lng":-0.1}` |
| 1 level | `customer` holding the whole customer object as JSON |

Under Indexed columns, an array position counts as a level too: at 2 levels, `lines[0]` is a column holding `{"sku":"K-1","qty":2}`. Lower the depth when an API returns deep metadata you do not need as columns, and keep it at All levels when every field matters.

## Opening the CSV in Excel and Google Sheets

The output follows [RFC 4180](https://www.rfc-editor.org/rfc/rfc4180): a cell that contains the separator, a double quote or a line break is wrapped in double quotes, and a quote inside it is doubled. The value `x,"y"` is written as `"x,""y"""`, which every spreadsheet reads back as the original text.

- **Google Sheets and Numbers** read the UTF-8 file as it is.
- **Excel on Windows** can show accented letters as garbage in a plain UTF-8 CSV. Microsoft's [support article on CSV UTF-8 files](https://support.microsoft.com/en-us/office/opening-csv-utf-8-files-correctly-in-excel-8a935af5-3416-4edd-ba7e-3dfd2bc4a032) says such a file opens normally when it was saved with a byte order mark (BOM). Turn on Excel BOM before you download, or import it through Data > Get Data > From Text/CSV.
- **Excel set to a comma decimal separator**, common in much of Europe, expects semicolons between fields. Choose Semicolon.

To check the result before you share it, open it in the [CSV Viewer](/convert/csv-viewer/), or turn it into a real workbook with [CSV to Excel](/convert/csv-to-excel/).

## JSON Flatten vs Other Ways to Get a Table

| Method | Nested keys become columns | Finds the rows for you | One row per array item | Setup |
|--------|---------------------------|------------------------|------------------------|-------|
| FindUtils JSON Flatten | Yes, dot paths | Yes: data, results, items, records | No | None, in the browser |
| FindUtils [JSON to CSV](/convert/json-to-csv/) | No, nested values stay JSON text | No | No | None |
| FindUtils [NDJSON to CSV](/convert/ndjson-to-csv/) | Yes, for JSON Lines files | One line is one row | No | None |
| jq on the command line | With a filter you write | With a filter you write | Yes | Install and learn the syntax |
| pandas `json_normalize` | Yes, `sep="."` by default | With `record_path` | Yes | Python and pandas |

Use JSON Flatten for a quick, inspectable table from one JSON document. Use a script when the reshaping must be repeated on a schedule or needs one row per nested item.

## Common Mistakes to Avoid

### Mistake 1: Keys That Already Contain a Dot

Some APIs use keys such as `"a.b"`. If a record has both `"a.b": 2` and `"a": {"b": 3}`, both flatten to the same column name `a.b`, and only one value survives, here 3. Check the column count against what you expect, and rename such keys first if both forms appear.

### Mistake 2: Joining Values That Contain a Pipe

Join with | writes `["a|b", "c"]` as `a|b|c`, and nobody can tell afterwards that there were two values. If your values can contain `|`, choose JSON cell.

### Mistake 3: Long Numeric IDs

JSON itself allows any number of digits, but [RFC 8259](https://www.rfc-editor.org/rfc/rfc8259) notes that only integers between −(2^53)+1 and 2^53−1 are interoperable. Above [9,007,199,254,740,991](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER), JavaScript, and so this tool, rounds them: `12345678901234567890` comes out as `12345678901234567000`. Have the API send long IDs as strings, and format those columns as text in your spreadsheet so it does not round them either.

### Mistake 4: Pasting JSON Lines

A `.jsonl` or `.ndjson` file holds one JSON value per line, as described at [jsonlines.org](https://jsonlines.org/). It is not one JSON document, so it fails to parse here; the error message offers a link to [NDJSON to CSV](/convert/ndjson-to-csv/), which reads that format and uses the same dot paths.

### Mistake 5: Expecting null and Missing Keys to Look Different

CSV has no null. A `null` value and a key that is absent both become an empty cell, and an empty object produces no column at all. If the difference matters, keep the original JSON alongside the CSV or use JSON cell for that part.

## Tools Used in This Guide

- **[JSON Flatten](/convert/json-flatten/)**: Turn nested JSON into a CSV with one dot-path column per key
- **[JSON Formatter](/developers/json-formatter/)**: Pretty-print JSON and find syntax errors
- **[JSON Path Finder](/developers/json-path-finder/)**: Test the path to your array before you type it into Array path
- **[JSON to CSV](/convert/json-to-csv/)**: Convert JSON to CSV with nested values kept as JSON text
- **[NDJSON to CSV](/convert/ndjson-to-csv/)**: Convert JSON Lines files with one object per line
- **[CSV Viewer](/convert/csv-viewer/)**: Check the flat CSV as a table
- **[CSV to Excel](/convert/csv-to-excel/)**: Turn the CSV into a workbook
- **[JSON to Markdown Table](/convert/json-to-markdown-table/)**: Make a table for docs or a README instead of a spreadsheet

## FAQ

### How do I flatten nested JSON to CSV?

Paste the JSON into JSON Flatten. It finds the array of records, writes every nested key as a column named by its path, such as `address.city`, and gives you a CSV to download. Choose how arrays are written and the separator your spreadsheet expects before you download.

### What is dot notation in a CSV header?

It is the path to a nested value written with dots between the keys. The value at `{"customer": {"geo": {"lat": 51.5}}}` gets the column name `customer.geo.lat`. Array positions are written in brackets, such as `tags[0]`.

### How are arrays converted when flattening JSON?

You choose. Join with | puts a list of plain values in one cell, such as `admin|beta`. JSON cell keeps every array as JSON text. Indexed columns gives each position its own column, such as `tags[0]` and `tags[1]`, and spreads arrays of objects into columns like `lines[0].sku`.

### Can I get one row per item in a nested array?

Not in JSON Flatten. Reshape the JSON first, for example with jq or pandas' `json_normalize` and its `record_path` option, so each nested item becomes its own object, then flatten the result.

### Why is my whole API response in one cell?

The tool did not find your array. It looks under `data`, `results`, `items` and `records` and one level below them. For any other location, type the path into Array path, such as `response.users`.

### Is my JSON uploaded?

On the tool page, no. The JSON is parsed and the CSV is built in your browser, and the page accepts up to 10 MB of JSON.

### Is JSON Flatten free?

Yes. It is free, needs no account, and has no usage limits.

## Next Steps

- Convert JSON Lines files with the [NDJSON to CSV guide](/guides/ndjson-to-csv/).
- Find the exact path to an array with the [JSON Path Finder guide](/guides/json-path-finder-guide/).
- Learn why types get lost on the way to a database in [JSON to CSV to SQL: why data types get lost](/blog/why-json-to-csv-to-sql-loses-data-types/).
- See every JSON tool in the [complete guide to online JSON tools](/guides/complete-guide-to-online-json-tools/).

## Sources

- [RFC 8259: The JavaScript Object Notation (JSON) Data Interchange Format](https://www.rfc-editor.org/rfc/rfc8259)
- [RFC 4180: Common Format and MIME Type for Comma-Separated Values (CSV) Files](https://www.rfc-editor.org/rfc/rfc4180)
- [MDN: Number.MAX_SAFE_INTEGER](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER)
- [JSON Lines format](https://jsonlines.org/)
- [jq Manual](https://jqlang.org/manual/)
- [pandas documentation: json_normalize](https://pandas.pydata.org/docs/reference/api/pandas.json_normalize.html)
- [Microsoft Support: Opening CSV UTF-8 files correctly in Excel](https://support.microsoft.com/en-us/office/opening-csv-utf-8-files-correctly-in-excel-8a935af5-3416-4edd-ba7e-3dfd2bc4a032)
