Skip to content
pdfconversionapi

XLSX to JSON API

Post a workbook to POST /v2/convert/xlsx/to/json and get one worksheet back as a JSON array: an object per row, its keys taken from the first row. The sample's Loans sheet becomes 12 objects with dates, amounts and TRUE/FALSE flags typed.

Endpoint
POST /v2/convert/xlsx/to/json
Accepts
xlsx
Sample timing
under 1 ms median of 5 runsHow we measure
Request size
Up to 10 MB on the Free plan or 100 MB on paid plans, all files together

Request and response

Every sample below ran against the API before this page was published. Set PDFCONVERSIONAPI_KEY to your key and run it as is.

curl -sS -X POST \
  https://api.pdfconversionapi.com/v2/convert/xlsx/to/json \
  -H "Authorization: Bearer $PDFCONVERSIONAPI_KEY" \
  -F File=@tool-library-loans.xlsx \
  -o response.json

# FileData is base64. Save the first file in converted/,
# or print the JSON error.
if jq -e '.Files' response.json > /dev/null; then
  mkdir -p converted
  name=$(jq -r '.Files[0].FileName' response.json)
  jq -r '.Files[0].FileData' response.json \
    | base64 --decode > "converted/$name"
else
  cat response.json
fi
200 OKunder 1 ms median1 file, 2.8 KB
{
  "ConversionCost": 1,
  "ConversionTime": 0,
  "Files": [
    {
      "FileName": "tool-library-loans.json",
      "FileExt": "json",
      "FileSize": 2902,
      "FileData": "WwogIHsKICAgICJDYXRlZ29y…"
    }
  ]
}

Response from our test run on 2026-11-03. ConversionTime shows the median of 5 runs. FileData is shortened here; the real response carries the whole file as base64.

The sample, converted

The same file the code sends. Download the input and the output and compare them yourself.

tool-library-loans.json, first 30 of 146 lines

[  {    "Category": "Power tools",    "Days": 7.0,    "Deposit": 25.0,    "Due": "2026-09-08",    "Item": "Cordless drill",    "Loan": "L-1041",    "Member": "M-208",    "Out": "2026-09-01",    "Returned": true,    "Shelf": "B2"  },  {    "Category": "Garden",    "Days": 7.0,    "Deposit": 10.0,    "Due": "2026-09-09",    "Item": "Hedge trimmer",    "Loan": "L-1042",    "Member": "M-114",    "Out": "2026-09-02",    "Returned": true,    "Shelf": "D4"  },  {    "Category": "Ladders",    "Days": 3.0,    "Deposit": 15.0,    "Due": "2026-09-06",
Input
tool-library-loans.xlsx (11 KB)
Output
tool-library-loans.json (2.8 KB)
Source
A fictional tool library's September loans workbook (three sheets, one of them hidden, with formulas and a chart) that we wrote for this site (CC0-1.0). How it was made
Made by
2026-11-04, API build 9eeeb0640aba

Parameters

Send these as form fields next to the file. Names are case-insensitive. If you set one of the documented options below that we don't support yet, the request fails with a 400 that names it instead of returning a file made without it.

ParameterWhat it does
WorksheetNamestringThe sheet to read, matched without regard to case (summary finds Summary). Without a sheet option the first sheet is read, whichever tab was open. An unknown name returns 400 with "no worksheet named 'Nope'".
WorksheetIndexintegerThe sheet's position, from 1, counting hidden sheets: 3 reads the sample's hidden Deposits sheet. For this 3-sheet file, 4 returns 400 with "WorksheetIndex is 4, but the workbook has 3 worksheets".Values: 1 to the number of sheets
ActiveWorksheetOnlyboolean, default falseRead the tab that was open when the workbook was saved instead of the first one. For the sample that is Summary.
FirstRowIsHeaderboolean, default trueWith false, every row, the header included, becomes an array of values in column order, and the file shrinks from 2,902 to 1,938 bytes. Anything other than true or false returns 400.Values: true, false
PasswordstringOpens a workbook encrypted with a password to open. The locked copy of the sample gave the same 2,902-byte JSON with it and 422 "the input file is password-protected" without it.
FileNamestringName of the JSON file: FileName=september returns september.json.

What carries over, and what doesn't

Kept

  • Every row of the chosen sheet below the header: 12 objects for the sample's Loans sheet, L-1041 to L-1052
  • Number cells as JSON numbers and TRUE/FALSE cells as JSON booleans
  • Dates as ISO 8601 text: the cell that shows 9/1/2026 arrives as "2026-09-01"
  • Formula results as stored in the file: Days (=F2-E2) is 7.0 and Deposit, looked up on the hidden Deposits sheet, is 25.0
  • Empty cells as null, and a key for every column, so all objects have the same keys

Lost or not yet supported

  • Column order: keys inside each object are sorted alphabetically, so Category comes before Loan. Use FirstRowIsHeader=false to keep the order as arrays.
  • Number formats: $25.00 arrives as 25.0. XLSX stores every number as a decimal, so whole numbers keep a .0.
  • Hidden rows and columns are not filtered out: loan L-1047 (a hidden row) and the hidden Shelf column are both in the sample's output.
  • Formulas themselves, charts, styles and the other sheets. Only one sheet is read per request.
  • A formula saved without a result comes out as null. Re-save the workbook with /v2/convert/xlsx/to/xlsx first: in our test the four null totals became 260, 150, 54 and 464.

Limits

  • One sheet per request; run one request per sheet to export several.
  • Requests up to 10 MB on the Free plan or 100 MB on paid plans. A conversion that takes more than 120 seconds returns 504.
  • A file that isn't a spreadsheet returns 422 "the input file could not be read: Cannot detect file format".

Where teams use it

Importing a spreadsheet someone emailed into your database

Read the rows as objects, validate each field, and insert them. Typed values save a parsing step: amounts arrive as numbers, flags as true or false, dates as 2026-09-01.

Giving a language model or a search index the data, not the layout

One JSON object per row is a compact, unambiguous form for prompts and embeddings; there are no merged cells or print settings to explain.

Checking an exported report in tests

Convert the workbook your code generated and assert on the JSON, without a spreadsheet library in the test suite.

Questions

Why are the keys in alphabetical order instead of my column order?

Each row is written as a JSON object, and the objects are written with sorted keys. JSON readers don't depend on key order, but if your code does, send FirstRowIsHeader=false: rows then come as arrays in column order, with the header as the first array.

My sheet has a title above the headers. What do the keys look like?

The first row of the sheet's used range is taken as the header. In the sample's Summary sheet that row holds the title, so the keys became "Loans by category, September 2026", "column2" and "column3", and the real headers turned into a data row. Empty header cells are named column1, column2 and so on. Move the title out of the sheet, or read the data with FirstRowIsHeader=false and pick the header row yourself.

Are dates and times converted?

Yes. Cells with a date format become text in ISO 8601: "2026-09-01" for a date, and "2026-03-02 13:00:00" when there is a time of day. Midnight is written as the date alone.

Which sheet is read?

The first one in tab order, even if another tab was open when the file was saved. Choose another with WorksheetName, WorksheetIndex or ActiveWorksheetOnly=true.

Can I send an XLS or XLSB file to this route?

The reader recognizes the format from the file's content, so an XLS sent to xlsx/to/json was read correctly in our test. Use the route named after your format anyway: /v2/convert/xls/to/json and /v2/convert/xlsb/to/json give the same output.

Convert your first XLSX file in a minute.