Turn an Excel sheet into JSON records
This page converts XLSX to JSON. Upload an Excel workbook and you get back a .json file in which every row of your sheet is one record and every column heading is a key. If you searched for "Excel to JSON", this is the page: an XLSX file is what Excel has saved to since 2007, and the older .xls kind has its own page.
A spreadsheet is where people keep lists by hand. A program would rather read JSON, because it has types built in: a number is a number, true is true, and there's no guessing. The converter reads the value and the type of each cell, so the JSON it writes keeps numbers as numbers and text as text, and a date cell comes out as a readable date rather than Excel's serial number.
How to convert XLSX to JSON
- Upload your Excel file - Drag the .xlsx into the box at the top of this page, or browse for it. You can add up to 5 files at once.
- Check the output - JSON is already picked on this page, so there's nothing to change.
- Download your file - Save the .json file when it's ready. The download link works for 30 minutes.
The workbook is uploaded and converted on our servers, not in your browser, so there's nothing to install and no account to make. Our security page explains what happens to the file after that.
How your sheet becomes JSON
The converter has a fixed set of rules for sheets, headers and cell types. Knowing them tells you what the JSON will look like before you open it.
Sheets
- When only one sheet has data, the output is a plain array of records.
- When more than one sheet has data, the output is an object keyed by sheet name, like
{"Orders": [...], "Customers": [...]}, so the sheets don't get mixed together. - Hidden sheets and hidden rows are included, because they still hold data. Empty sheets are left out. A workbook with no data at all is refused with the message
This spreadsheet has no data to convert.
The header row
- The first row that isn't empty is the header, so empty rows above the table are skipped. The keys in each record follow the header order.
- Header text is trimmed. A number used as a header becomes text, so a column headed
5gives the key"5". - A blank header cell gets the name
column_N, where N is the column's position. A heading that appears twice gets_2on the second one, so no value is lost. - A sheet with a header and no rows under it gives
[].
Cell types
- Numbers stay JSON numbers, rounded to the 15 digits Excel itself keeps. That's why a cell holding
0.1+0.2comes out as0.3and not 0.30000000000000004. Whole numbers up to 15 digits are kept exactly, and a very large one like 1e16 is written as1e+16. - TRUE and FALSE cells become
trueandfalse. Text that says "true" stays the string"true", because that's what the cell holds. - Text stays text, even when it looks like a number.
- JSON has no date type, so a cell formatted as a date becomes ISO text, not Excel's serial number. A date at midnight gives just the date,
"2024-01-15". Any other time gives both,"2024-01-15T09:30:00". A time-only cell gives"14:05:00". - A formula cell gives its result, never the formula text. If the workbook was saved without results, or asks to be recalculated on open, LibreOffice recalculates it on our server first.
- An error cell such as
#N/Acomes out as that text. - In a merged range, the value sits in the top-left cell and the other cells give
"", which is also what any empty cell gives. There's never anull.
The file is written with 2-space indentation, as UTF-8 with no BOM. Accented and non-English text is written as it is, not as \u escape codes.
A real example from our tests
On 13 November 2026 we ran a small workbook through our live converter. It's the file our JSON to XLSX converter made from a two-record JSON file, so it has one sheet, named Data, with the id cells stored as numbers and everything else, the ZIP codes included, stored as text:
| id | name | address.city | tags | zip |
|---|---|---|---|---|
| 1 | Ana | Lisbon | a; b | 01100 |
| 2 | Zoë | Zürich | 8001 |
About 10 seconds after we started the upload, the JSON came back:
[
{
"id": 1,
"name": "Ana",
"address.city": "Lisbon",
"tags": "a; b",
"zip": "01100"
},
{
"id": 2,
"name": "Zoë",
"address.city": "Zürich",
"tags": "",
"zip": "8001"
}
]
The ids are numbers 1 and 2, the ZIP code "01100" kept its zero because the cell was text, and the empty tags cell became "". Notice that the column named address.city stayed a flat key with a dot in it, because the converter doesn't rebuild dotted column names into nested objects, so a round trip from JSON to Excel and back flattens the data. You can download the workbook we uploaded and the JSON it produced to check them yourself.
The backend team's production test on the same day used a richer workbook, with three sheets. The sheet Orders has a blank header cell, the heading qty twice, date-time cells, and a total column of formulas saved without their results:
| orderId | date | qty | qty | total | |
|---|---|---|---|---|---|
| A-1001 | 2026-11-11 00:00 | gift | 2 | 3 | =D2*12.5 |
| A-1002 | 2026-11-12 14:30 | 5 | 1 | =D3*1 |
A second sheet, Customers, holds three columns (name, city, since) with plain dates, and a third sheet, Empty, has no cells. The output:
{
"Orders": [
{
"orderId": "A-1001",
"date": "2026-11-11",
"column_3": "gift",
"qty": 2,
"qty_2": 3,
"total": 25
},
{
"orderId": "A-1002",
"date": "2026-11-12T14:30:00",
"column_3": "",
"qty": 5,
"qty_2": 1,
"total": 5
}
],
"Customers": [
{
"name": "Ana",
"city": "Lisbon",
"since": "2020-05-01"
},
{
"name": "Ben",
"city": "Lagos",
"since": "2021-08-09"
}
]
}
Orders and Customers each got their own key, and the empty sheet was left out. The blank header became column_3, the second qty became qty_2, midnight dates lost their time part, and the formula =D2*12.5 was recalculated to 25.
Before launch, a bigger workbook of 380,000 rows was run on our test server. The conversion itself took 48 seconds.
Things to know before you convert
- Only cell values come across, so colours, bold and column widths are not in the output, and a column you expect to be all numbers can hold a string when one of its cells is an error like
#N/A. - Numbers of 16 or more digits lose precision in Excel itself, which keeps 15 significant digits. Store a long ID as text in the sheet if every digit matters, and it'll come through as a string.
- A CSV file renamed to .xlsx is read as a CSV. A CSV has no cell types, so in that case every value stays a string, numbers included. If that's your file, CSV to JSON is the right page for it.
Limits, and files that won't convert
- Each workbook can be up to 20 MB. The upload box takes bigger files, but the converter stops anything over 20 MB with the message
This file is larger than 20 MB. Try splitting it into smaller files.A 0-byte file getsThis file is empty. - A password-protected workbook can't be read, and neither can a cut-off or damaged one. Both give
We couldn't read this spreadsheet. The file may be damaged or password-protected.Remove the password in Excel first, then upload again. - A sheet can have up to 1,048,576 rows and 16,384 columns, which are Excel's own limits, and all sheets together can have up to 5,000,000 cells with data in them. A workbook over any of these gets a message naming the limit it hit.
- The converter reads at most 50,000,000 cells, empty ones included, and an .xlsx that unpacks to more than 1 GB is refused. Both messages say the spreadsheet is too large and ask you to split it.
- Reading has a time limit of 90 seconds, or 180 seconds when LibreOffice has to recalculate the workbook first, and the whole conversion has 960 seconds. The JSON itself can't be larger than 200 MB.