Free online XLSX to JSON converter
Excel to JSON with the types intact
Open an .xlsx and get JSON back, free and without an account: the header row becomes the keys, numbers stay numbers, booleans stay true and false, and Excel’s serial day counts are converted into ISO 8601 strings. You choose whether an empty cell is null or simply absent, whether dotted headers like user.address.city are rebuilt into nested objects, and whether the result is an array, an object keyed by one of the columns, or NDJSON. A 10 MB package with 100,000 rows a sheet is the ceiling, and every byte of it stays on your machine.
- 100% free
- No signup
- Up to 10 MB
- Typed output
- ISO 8601 dates
Drop an .xlsx here, use Open .xlsx, or paste a file with Ctrl+V
Up to 10 MB, up to 100,000 rows per sheet. Salary tables and customer lists are parsed in this tab and never sent anywhere.
How to convert Excel to JSON
Three steps: load the workbook, name the header row, pick the JSON shape.
Load the workbook
Drop an .xlsx onto the panel, press Open .xlsx, or drop one in with a plain Ctrl+V; ⌘V on a Mac. The ZIP is inflated in the tab and the sheets appear as tabs with their dimensions; pick the one you want. Nothing is uploaded, which is the reason this page exists rather than the twelve that ask you to sign in first.
Say which row holds the keys
Header row is 1 by default; set it to 3 when the sheet starts with a title and a blank line, or to 0 to key the objects by column letter instead. Duplicate headers get a numeric suffix so no key is lost, and the Keys dropdown rewrites them as camelCase or snake_case when the sheet has 'Order ID' where your code wants orderId.
Choose the JSON you actually want
Pick the output shape — an array of objects, an object keyed by any column, an array of arrays, or NDJSON with one object per line — then decide whether an empty cell becomes null, an empty string or a missing key, and whether dates come out as ISO 8601, the raw serial number or Unix milliseconds. Indent with 2 spaces, 4 spaces or a tab, or minify, then Copy or Download.
Technical specifications
| Accepted input | .xlsx packages from Excel 2007+, LibreOffice, Google Sheets, Numbers and openpyxl — shared strings, inline strings, formula results, boolean and error cells all read |
|---|---|
| Maximum input | 10 MB of package, 100,000 rows and 16,384 columns per sheet; a 0.94 MB workbook of 20,001 rows × 8 columns converts in about 227 ms |
| Output shapes | Array of objects, object keyed by any column, array of arrays, or NDJSON; 2-space, 4-space, tab or minified |
| Type preservation | Numbers keep their stored literal, booleans stay true/false, error values become strings, and empty cells become null, "" or a missing key |
| Date handling | Serial numbers converted to ISO 8601, the raw serial, or Unix milliseconds; the 1900 epoch (day 1 = 1900-01-01, with the phantom 1900-02-29 at serial 60) and the 1904 Mac epoch are both supported |
| Nesting | Dotted headers rebuilt into objects, numeric segments into arrays, with index gaps filled by null |
| Keys | Trimmed, deduplicated with a numeric suffix, optionally rewritten as camelCase or snake_case; header row selectable from 0 to 50 |
| Processing location | Your browser — the ZIP entries are inflated by the page itself and no request ever carries the file |
Frequently asked questions
How does the header row become object keys?
Each cell of the chosen header row becomes the key for its column, trimmed of surrounding whitespace. An empty header cell falls back to the column letter, so a stray gap gives you "D" rather than an empty key, and a header that repeats gets _2, _3 and so on appended — an object cannot carry the same name twice with any predictable outcome, and RFC 8259 explicitly leaves the behaviour undefined. Set the header row to 0 when the sheet has no header at all and every key becomes its column letter.
Should an empty cell be null or should the key be missing?
Use null when the JSON feeds a schema or a database and every record must have the same shape, and omit the key when payload size matters or when your consumer distinguishes 'absent' from 'known to be empty'. There is a real difference between the two in the file as well: a cell that was never touched does not exist in the worksheet XML at all, while a cell someone cleared may still be there carrying an empty string. All three choices — null, the empty string, or leaving the key out — are one dropdown away, and the default is null because it is the one that keeps records rectangular.
Why is my date 1900-01-01?
Because the cell holds a very small serial number and a date format, and serial 1 is 1 January 1900. It is the classic sign of a column whose real content is a duration, a time of day or a zero: 0 with a date format shows as 1900-01-00 in Excel and comes out here as a time-only value, 0.5 is midday with no date at all, and 1 is the first day of the epoch. If the source cell should have held a real date, what usually went wrong upstream is that a blank or a 0 was written into a date-formatted column — switch the Dates dropdown to Excel serial to see the raw number and confirm it.
Can I rebuild nested JSON from dotted column headers?
Yes — leave "Rebuild nesting from dotted headers" ticked and a column called user.address.city produces {"user":{"address":{"city":"…"}}} rather than one long key. Numeric path segments make arrays, so items.0.sku and items.1.sku come back as a two-element list, and gaps in the indexes are filled with null so the positions still line up. Untick it when your headers legitimately contain dots — a column named 3.5mm jack should stay a single key — and the status line reports any path where a header is used both as a value and as the parent of another header.
How do I get an object keyed by id instead of an array?
Set Output to 'Object keyed by a column' and pick the column whose values should become the property names. That gives you {"1001":{…},"1002":{…}}, the shape a lookup table wants and the shape pandas calls orient="index". The key column stays inside each record as well, because removing it would make the export lossy if two rows happened to share a key — and if any do, the status line counts them so you can pick a column that is actually unique.
Does it keep numbers as numbers?
Yes, and it keeps the literal the file stored rather than a re-printed approximation of it. A cell holding 0.1 comes out as 0.1, not 0.1000000000000000055511151231257827; a cell holding 1.5e+21 keeps its exponent. Booleans come out as true and false rather than the strings "TRUE" and "FALSE", and the eight Excel error values — #N/A, #DIV/0!, #REF! and the rest — come out as strings, because there is nothing else in JSON to map them to. Untick 'Keep numbers and booleans typed' when the consumer wants every field as a string.
Why does my old .xls file not work here?
Because .xls is a completely different format — an OLE2 compound document full of binary BIFF records, with no XML anywhere in it — and this page reads the XML-based .xlsx that replaced it in Office 2007. Rather than producing garbage, the file is identified by its magic bytes (D0 CF 11 E0 A1 B1 1A E1) and named. The same check catches .xlsb workbooks, OpenDocument .ods files, password-protected packages and CSVs that were renamed to .xlsx; in every case the fix is to open the file once and re-save it as .xlsx.
About converting Excel to JSON
Dates are the hard part, and the reason is a bug from 1983. Lotus 1-2-3 treated 1900 as a leap year — it is not; the rule since 1582 is that century years are leap years only when divisible by 400 — and so its day counter includes a 29 February 1900 that never happened. When Microsoft built Excel in 1985 it copied the behaviour deliberately, to stay bug-compatible with the file format everyone already used, and the decision is now frozen into the standard: ECMA-376 §18.17.4.1 specifies serial 60 as 29 February 1900. The practical effect is that in the 1900 date system serial 1 is 1 January 1900, serials 1 through 59 are one day ahead of a naive count, serial 60 is a date that does not exist, and everything from 61 onward lines up again. This reader uses a different epoch offset either side of serial 60 and flags the phantom day when it meets it, which is why 1 March 1900 comes back as 1900-03-01 and not 1900-02-29.
The second epoch is the one nobody expects. Excel for Macintosh shipped with a 1904 date system — day 0 is 1 January 1904 — partly to sidestep the leap-year problem, and workbooks created that way still circulate. The setting lives in a single attribute, <workbookPr date1904="1"/>, and a converter that ignores it puts every date in the file four years and a day off. Both systems are handled here, and neither carries a timezone: a serial number is a day count and a fraction of a day, with nothing in it about where it was recorded, so the ISO strings come out without an offset rather than pretending to a UTC they cannot claim.
Everything else follows from the fact that a worksheet cell is a value plus a pointer to a display format. A formula cell carries both its expression and the number Excel last calculated, and JSON gets the number; a text cell usually carries an index into the shared string table rather than the text itself; a boolean is stored as 0 or 1 with a t="b" marker. Reading those correctly is the difference between JSON you can feed to an API and JSON full of stringified integers — and once you have it, the JSON viewer will let you walk the result, or JSON to Excel will take it back to a grid once you have changed something.
Where your spreadsheet is processed
The workbook is unzipped, parsed and re-serialised entirely inside this tab; no bytes leave your machine and there is no server to leave them on. Spreadsheets are where organisations keep salaries, customer records and unreleased numbers, and a converter that asks you to upload one is asking for a copy of all three.