Convert Excel (XLSX) to Markdown tables

Convert Excel spreadsheets to Markdown tables with the Mdkit API — a table an LLM can actually read, without cell-by-cell parsing code.

Last updated

Output shape One '## sheet name' heading plus one Markdown table per sheet
Accepted format .xlsx only (application/vnd.openxmlformats-officedocument.spreadsheetml.sheet)

Spreadsheets convert to Markdown tables — the representation LLMs handle far better than raw XML or a CSV blob whose headers went missing. Upload the .xlsx to POST /v1/convert like any other document: the synchronous lane walks the workbook sheet by sheet and emits, for each one, a ## <sheet name> heading followed by a single Markdown table. No cell-by-cell parsing code, no spreadsheet library in your own service, and no format parameter — the endpoint takes the type from the upload.

curl

curl -F "file=@figures.xlsx;type=application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" \
  https://api.mdkit.online/v1/convert

The markdown field comes back looking like this:

## Q3
| Region | Revenue | Growth |
| --- | --- | --- |
| EMEA | 412000 | 0.08 |
| APAC | 260500 | 0.13 |

## Q4
| Region | Revenue | Growth |
| --- | --- | --- |
| EMEA | 448900 | 0.09 |

Python — index each sheet on its own

import httpx

XLSX = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"

with open("figures.xlsx", "rb") as fh:
    resp = httpx.post(
        "https://api.mdkit.online/v1/convert",
        files={"file": ("figures.xlsx", fh, XLSX)},
        headers={"X-API-Key": "YOUR_KEY"},  # optional — raises the rate limit
        timeout=60,
    )
resp.raise_for_status()
markdown = resp.json()["markdown"]

# Every sheet is a "## <name>" section — split it back apart so each sheet can
# be embedded and cited on its own instead of as one giant blob.
sheets: dict[str, str] = {}
for block in markdown.split("\n## "):
    name, _, table = block.lstrip("# ").partition("\n")
    sheets[name.strip()] = table.strip()

for name, table in sheets.items():
    print(name, "→", table.count("\n") + 1, "rows")

Watch the header row

The first row of each sheet becomes the table header. That is right for a clean data sheet and wrong for a formatted report: if the real header sits on row 3 under a title and a blank line, the title becomes the header and the real header lands in the first data row. Either trim the preamble before uploading, or send the workbook through the async lane and read the format=json output, which gives you table cells with their row/column positions instead of a flattened Markdown grid. Merged cells and stacked multi-row headers flatten the same way in the fast lane.

Limits

  • Cell values are converted, not the spreadsheet: formula results come out as the file stored them, and charts, images, conditional formatting and styling are dropped. The output is the data.
  • Sync uploads are capped at 8 MB — a workbook past that belongs on the async lane (50 MB).
  • Anonymous calls run at 30 requests per minute per IP; an X-API-Key header raises the limit. Each synchronous conversion costs 1 credit.

Try it

Upload a workbook to the free converter — no signup, up to 4 MB, same conversion as the API.

FAQ

How do I turn a spreadsheet into a Markdown table?
POST the .xlsx to /v1/convert and the sheets come back as Markdown tables — the pipe-delimited form models understand well in a prompt.
Are formulas converted or evaluated?
You get the cell values as the file stores them, not a recalculated spreadsheet. Mdkit is a conversion API, not a spreadsheet engine.
What happens with multiple sheets?
The workbook is converted as a whole: every sheet becomes a '## sheet name' heading followed by its own Markdown table, in workbook order. For very large workbooks, use the async lane — its 50 MB cap is far above the 8 MB synchronous limit.
Does Mdkit accept the legacy .xls format?
No — only .xlsx. A legacy .xls upload is rejected with 415, so re-save it as .xlsx (or export it from Excel, LibreOffice or Google Sheets) before uploading.