Free, no signup. Change the text in any PDF.Edit a PDF
XLS → JSON Converter

Convert XLS to JSON - Old Excel Workbooks to JSON Records

Upload an Excel 97-2003 .xls workbook and get JSON records, one per row, with numbers, dates and TRUE/FALSE cells keeping their type.

Select your file to convert

or drop your file here

Expert Tips

What the pros know

Pro Tip

Remove any title rows above the table before you upload. The first row that isn't empty becomes the keys, so a report title in row 1 would become a key and the real headings would turn into the first record.

Common Mistake

Leaving a hidden sheet of notes or lookups in the workbook. Every sheet that holds data is converted, hidden ones too, so those notes end up in the JSON.

Best For

Exports from older systems that still write .xls, including the ones that are really an HTML table saved as .xls, when the data has to go into an app or an API.

Not Recommended

Workbooks where the formulas, colours or cell formats are the point. The JSON holds values only: a formula cell gives its result, and no formatting survives.

FAQ

Frequently Asked Questions

Upload the file in the box at the top of this page, keep JSON as the output, and download the result. Each row becomes one record, with the column headings as the keys.

Yes, that's the format this page is for. LibreOffice opens the .xls on our server and rewrites it as .xlsx before the cells are read, so workbooks saved by those versions convert.

Each sheet with data becomes its own list of records, and the output is an object keyed by the sheet names. With a single sheet, the output is just the list.

JSON has no date type, so dates are written as ISO text, which most programming languages parse directly. A date with a time comes out as 2024-01-15T09:30:00.

No. An empty cell gives an empty string, "", so every record has the same keys and no value is ever null.

Yes. The file is uploaded and converted on our servers, and then you download the JSON. The download link expires after 30 minutes.

Yes, the JSON to XLS page does the reverse. Numbers go back as number cells and text as text cells, each record becomes a row, and the keys become the header row.

Convert XLS to JSON online free

Turn an old Excel workbook into JSON

An .xls file is the workbook format Excel used until 2007, and files in that format still turn up: an export from an older accounting or stock system, or a form that only accepts .xls. Convert XLS to JSON and that data can go into a web app or a script, which read JSON directly but can't open a binary spreadsheet. This page reads your XLS workbook and writes a JSON file with one record per row, using the column headings as the keys.

This page is built for the old Excel to JSON case. If your file ends in .xlsx, use XLSX to JSON instead, which follows the same rules but is written for the newer format.

How to convert XLS to JSON

  1. Upload your XLS file - Drag it into the box at the top of this page, or browse for it. You can add up to 5 files at once, and each one can be up to 20 MB.
  2. Check the output - JSON is already picked on this page, so there's nothing to change.
  3. Download your file - Save the .json file when it's ready. In the backend team's production runs, small files took 11 to 18 seconds from upload to download.

The conversion runs on our servers, not in your browser, so there's nothing to install and no account to make. The download link works for 30 minutes. Our security page explains what happens to the file after that.

How the converter reads an .xls file

XLS is a binary format from the 1990s, and the converter doesn't read it directly. LibreOffice opens the file on our server and saves it again as .xlsx, and that copy is read cell by cell. Two things follow from that: the JSON comes out exactly as it would from an .xlsx, and a file that is really an HTML table or an Excel 2003 XML spreadsheet saved as .xls converts too, because LibreOffice opens those as well.

From the cells, the JSON is built like this:

  • If one sheet holds data, the output is a plain list of records. If more than one does, it's an object keyed by sheet name, such as {"Orders": [...], "Customers": [...]}. Hidden sheets are included, and empty sheets are left out.
  • The first row that isn't empty is the header row, and its cells become the keys. Headings are trimmed, a blank heading becomes column_3 (its position), a repeated heading gets _2 added, and a number used as a heading becomes text, like "5". A column with no heading and no values is dropped.
  • Each row below the header is one record, with the keys in column order. An empty cell gives "", never null, and a row with nothing in it is skipped.
  • Number cells stay JSON numbers. They're rounded to the 15 digits Excel keeps, so a cell holding 0.1+0.2 gives 0.3, and whole numbers up to 15 digits come out exactly.
  • Text cells stay text, so a ZIP code stored as text keeps its leading zero: "01100".
  • TRUE and FALSE cells become true and false.
  • A cell formatted as a date becomes ISO text rather than Excel's serial number. A date at midnight gives "2024-01-15", a date with a time gives "2024-01-15T09:30:00", and a time on its own gives "14:05:00".
  • A formula cell gives its result, never the formula text. If the file was saved without results, LibreOffice works them out while it rewrites the file.
  • An error cell such as #N/A comes out as that text. In a merged range, the top-left cell has the value and the others give "". Hidden rows are included.

The file is written with 2-space indentation, as UTF-8 without a byte order mark. Accented and non-Latin text is written as it is, not as \u escape codes.

A real example from our tests

On 13 November 2026 we uploaded a 6,144-byte .xls file to our live converter. It's the workbook our JSON to XLS page had made earlier that day from a two-record JSON file, so we knew what was in it: one sheet named Data, holding these cells.

idnameaddress.citytagszip
1AnaLisbona; b01100
2ZoëZürich8001

About 13 seconds after the upload started, this 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 id cells were numbers in the sheet and are numbers here, 1 and 2 without quotes. Both ZIP codes were text cells, so "01100" kept its zero and 8001 is in quotes too. The empty tags cell became "", and Zoë and Zürich kept their accents. Notice that address.city is a flat key. The original JSON had a city nested inside an address record, which JSON to XLS flattened into a dotted heading, and this converter doesn't rebuild the nesting from the dot. You can download the XLS we uploaded and the JSON it produced to check them yourself.

The backend team ran a second .xls on production the same day, with number, text and TRUE/FALSE columns:

idnameemailzipbalanceactive
1Ana Silvaana@example.com011001520.75TRUE
2Ben Okaforben@example.com100010FALSE
3Chen Wei02134-42.5TRUE
[
  {
    "id": 1,
    "name": "Ana Silva",
    "email": "ana@example.com",
    "zip": "01100",
    "balance": 1520.75,
    "active": true
  },
  {
    "id": 2,
    "name": "Ben Okafor",
    "email": "ben@example.com",
    "zip": "10001",
    "balance": 0,
    "active": false
  },
  {
    "id": 3,
    "name": "Chen Wei",
    "email": "",
    "zip": "02134",
    "balance": -42.5,
    "active": true
  }
]

The balance column came through as 1520.75, 0 and -42.5, all numbers. The TRUE and FALSE cells in the active column became true and false, and Chen Wei's empty email cell became "".

Before launch, on our test server, a .xls with 65,000 rows converted in 68 seconds. An .xls sheet holds at most 65,536 rows, so that is close to a full sheet, and well inside the 960-second time limit.

Things to check before you upload

  • Only real TRUE/FALSE cells become JSON booleans. If someone typed the word true into a text cell, the JSON gets "true" in quotes, which a script will treat as text.
  • Dotted headings don't become nested records. A column called address.city stays a key called address.city, so if you need {"address": {"city": ...}}, you'll have to rebuild that yourself.
  • Error cells like #N/A and #DIV/0! arrive as text, not as null, so a script that expects a number in that column needs to handle those strings.
  • Excel keeps 15 significant digits for a number, so a 16-digit ID stored as a number has already lost its last digit before the file reaches us. Store long IDs as text in the sheet. A very large number like 1e16 is written as 1e+16.

Limits, and files that won't convert

  • Each file can be up to 20 MB. The upload box accepts bigger files, but the converter stops anything over that with the message This file is larger than 20 MB. Try splitting it into smaller files.
  • A 0-byte file gives This file is empty. A workbook with sheets but no data in any of them gives This spreadsheet has no data to convert.
  • A damaged file, or a file that isn't really a spreadsheet, gives We couldn't read this spreadsheet. The file may be damaged or password-protected. The backend team confirmed this on production by uploading a file of random bytes named .xls.
  • A workbook can hold at most 5,000,000 filled cells across all its sheets, and the reader stops at 50,000,000 cells including empty ones. One .xls sheet can hold 65,536 rows by 256 columns, which is 16.7 million cells, so a few packed sheets can hit these limits. The messages ask you to split the file.
  • LibreOffice gets 180 seconds to rewrite the file and the reader gets 90 seconds of processing time after that. Past either, you see This spreadsheet took too long to read. Try splitting it into smaller files. The whole conversion stops at 960 seconds, and a JSON file that would be larger than 200 MB is refused.

Last updated: 13 November 2026 · ChangeMyFile

What changed: New page, added when JSON support launched.

Tested: 13 November 2026, on our live converter: a 6,144-byte .xls with one sheet and two rows came back as JSON in about 13 seconds, with id as the numbers 1 and 2, the ZIP code 01100 as text with its zero, and the empty tags cell as an empty string.

RelatedAlso convert to JSON from