JSON to Excel Converter

Drop a .json or .jsonl file, or paste the response you already have open. Nested objects become dot-notation columns and an XLSX comes back. The whole conversion happens in this tab, so nothing is uploaded and there is nothing to sign up for.

Need to rename columns or filter records first? Open the app

Who needs the spreadsheet, and why

This conversion almost always exists because of a person, not a system. Somebody who does not read JSON needs to look at what is inside it:

  • An API response a product manager wants to sort. You pulled 400 orders from an endpoint. They want to sort by total, filter to one city, and count the web-channel ones. Curl output is not an answer; a workbook is.
  • A document store export nobody can read. MongoDB and Firestore dump JSON. Finance wants columns, filters and a pivot table, today.
  • Webhook and event logs during an incident. A day of JSON Lines payloads becomes a sheet you can sort by timestamp, and the pattern in the failures turns up in a minute.
  • Config review with the people who own the values. Feature flags, pricing rules, routing tables. A spreadsheet catches the wrong number in a way that reading a config file rarely does.
  • Evidence attached to a ticket or an audit. Auditors, support leads and account managers all accept an .xlsx attachment. None of them want a code block pasted into a comment.

Worked example: nested orders become flat columns

Two orders in the shape an API actually returns: a customer object, an address inside it, a list of line items, a tag array, a null, and a field only the second record has.

[
  {
    "id": "SO-1041",
    "placed": "2026-02-03",
    "customer": {
      "name": "Ada Byrne",
      "address": { "city": "Bristol", "postcode": "BS1 4DJ" }
    },
    "total": 41.5,
    "lines": [{ "sku": "TP-118", "qty": 2 }, { "sku": "LB-204", "qty": 1 }],
    "tags": ["priority", "gift"],
    "notes": null
  },
  {
    "id": "SO-1042",
    "placed": "2026-02-04",
    "customer": {
      "name": "Ravi Menon",
      "address": { "city": "Kochi", "postcode": "682001" }
    },
    "total": 12,
    "lines": [{ "sku": "RB-009", "qty": 1 }],
    "tags": [],
    "notes": "leave at reception",
    "channel": "web"
  }
]

Paste that in and the workbook comes back with these ten column headers:

id
placed
customer.name
customer.address.city
customer.address.postcode
total
lines
tags
notes
channel

And these two rows, shown here with a pipe between cells:

SO-1041 | 2026-02-03 | Ada Byrne  | Bristol | BS1 4DJ | 41.5 | [{"sku":"TP-118","qty":2},{"sku":"LB-204","qty":1}] | ["priority","gift"] |                    |
SO-1042 | 2026-02-04 | Ravi Menon | Kochi   | 682001  | 12   | [{"sku":"RB-009","qty":1}]                          |                      | leave at reception | web

Every decision the flattener made is visible there. The address two levels down produced customer.address.city, spelled out in full rather than shortened to city, so two nested objects that both hold a name never collide. The channel field appears only on the second order, so it becomes a column at the far right with the first row blank. notes: null and the empty tags array both give empty cells. And the line items stayed as JSON text in one cell rather than exploding into extra rows, with a warning above the preview saying so.

That last choice is deliberate. Turning one order into two rows changes what the total column means, and a converter that silently doubles your revenue is worse than one that hands you a cell to deal with.

Four shapes of JSON, no configuration

Most failed conversions elsewhere are not about size. They are about a file that is not the neat array of objects the tool assumed. Each of these is read without a setting to change:

  • An array of objects. One object becomes one row. Column order follows the order keys were first seen, so the first record sets the layout and later ones append whatever is new.
  • A single object. One record from a detail endpoint becomes a one-row sheet with its keys as the header.
  • A wrapper with the rows inside it. Responses shaped like {"count": 2, "page": 1, "results": [...]} are unwrapped automatically: the rows come from the array, and a note names the top-level fields left out so the metadata cannot vanish unnoticed.
  • JSON Lines and NDJSON. One object per line, no commas, no wrapping array. If the file will not parse as a single document, each line is tried on its own and a note confirms it was read that way. Log exports land here.
  • Even an array of plain strings or numbers. That comes back as a single column named value, which is better than an error message.

When the input genuinely cannot be read, the message names what was wrong. Malformed JSON reports the parser's own position, so you can go straight to the character that broke it.

How deep the flattening goes

A column name may be up to four dot-separated segments long, so customer.address.geo.lat is fine. A fifth level stops the descent: that branch is written into the cell as JSON text and the widget reports how many places it happened in. {"a":{"b":{"c":{"d":{"e":1}}}}} yields one column called a.b.c.d holding {"e":1}.

The cap is not laziness. Six levels across a wide response can produce hundreds of columns, at which point you have swapped one unreadable format for another. Four covers what people actually paste in, and the cells that hit the ceiling keep their contents.

Arrays never expand into columns or rows. A list of scalars such as ["priority","gift"] lands as that text in one cell, ready for a formula to split. A list of objects lands the same way and raises a warning, since that is the case where somebody was probably hoping for one row per item. Empty arrays give empty cells.

What lands in the cells

  • Identifiers keep their leading zeros. A value is typed as a number only where turning that number back into text returns every original character, so "007" and "02138" stay whole. This is the failure people expect from Excel, which is why typing is decided before the file is written.
  • Numeric-looking strings without a leading zero do become numbers. An Indian PIN code of "682001" is written as the number 682001, because nothing marks it as a label. A price stored as "2.40" stays text, since a number would print it back as 2.4.
  • Long identifiers survive intact. A 17-digit reference stays text rather than being rounded off at the fifteenth digit, which is what happens when a spreadsheet decides it is a quantity.
  • Dates arrive as text. JSON has no date type, so "2026-02-03" is written as those ten characters. Apply a date format to the column when you need date maths. ISO strings sort correctly as text in the meantime.
  • Booleans become the words true and false. Filtering and sorting on them works. Excel's own TRUE and FALSE cell type is not used, so a formula wanting a real boolean needs a text comparison.
  • Null and missing both give an empty cell. An explicit null and an absent key look identical in the workbook, so keep the source JSON if that difference matters.
  • One sheet, named Sheet1. The download takes your file's name with the extension swapped, so orders.json comes back as orders.xlsx. Pasted text has no name to borrow and downloads as data.xlsx.

Frequently Asked Questions

What happens to nested objects?

They become columns whose names are the key path joined with dots, so a customer holding an address holding a city gives you customer.address.city. Four segments is as far as the path goes. A fifth level of nesting is written into the cell as JSON text and the widget reports the number of places where that happened, which beats losing the data or refusing the file.

Is the JSON uploaded anywhere?

No. The parsing and the workbook writing both happen in your tab, which is the honest answer to the usual objection that browser tools cannot cope with real payloads. There is no upload endpoint on this page and nothing survives a reload.

My JSON is an object, not an array. Will it still work?

Yes. Four shapes are accepted: an array of objects, a single object which becomes a one-row sheet, an API-style wrapper whose one array property holds the rows, and JSON Lines with one object per line. With a wrapper, the rows come from the array and a note tells you which top-level fields were left behind.

What about arrays inside a record, like an order with line items?

The array stays in one cell as JSON text and the parent row is not duplicated. A list of objects also raises a warning above the preview so the choice is visible rather than silent. If you need one row per line item, convert the line items separately with the parent id repeated on each one.

Will my product codes and ZIP codes keep their leading zeros?

Yes. Nothing is typed as a number unless turning that number back into text returns every original character, so 007 and 02138 are written as text and survive the trip. The limit is honest: a postcode like 682001 has no leading zero to protect and is indistinguishable from a quantity, so it lands as a number.

Do dates and booleans arrive as real Excel dates and checkboxes?

No, both arrive as text. JSON has no date type, so 2026-02-03 is written as the characters 2026-02-03; select the column in Excel and apply a date format to convert it. JSON booleans are written as the words true and false, which sort and filter fine but will not drive a checkbox.

Turn your JSON into a workbook

Paste it or drop the file. Nested fields flatten, leading zeros hold, and the XLSX is built without anything leaving your browser.

Back to the converter