Word, PowerPoint and Excel to Markdown
A spec in Word, a slide deck or a spreadsheet has to become Markdown for a docs site, a Git repository or an AI knowledge base, with its headings, lists and tables intact. The free tools that do it, the two places to check (merged table cells and speaker notes), and a hosted option for many files.
Third-party documentation checked and our tool's runs made on 28 October 2026.
Copy and paste from Word into a Markdown file loses the structure: headings turn into plain lines, numbered lists lose their numbers, and tables fall apart into tabs. A converter that reads the file itself keeps that structure, because a .docx, .pptx or .xlsx file is a ZIP archive of XML that names its headings, list levels and table cells. This guide starts with the free tools, which are enough for most people, then shows our own hosted tool with real output.
Disclosure: Quarrywise, the publisher of this site, makes and sells the converter described near the end. The free tools need nothing from us.
The quick answer: Pandoc for Word, MarkItDown for the rest
- One Word file, or a handful: use Pandoc. It is free, installs on Windows, macOS and Linux, and converts on your own computer. It is the right first choice.
- Slides and spreadsheets too, from Python: MarkItDown (Microsoft, MIT licence) reads Word, PowerPoint and Excel, among other formats. Recent Pandoc versions (3.8.3 and later) also read .pptx and .xlsx.
- Tables with merged cells: Markdown tables cannot express them. Decide up front whether you want an HTML table in your Markdown or a flattened table (see below).
- Hundreds of files, a workflow tool such as n8n or Make, or no command line at all: a hosted converter saves you installing and running anything. Ours costs USD 0.003 per file plus USD 0.0004 per 3,000 characters of output (per slide for decks).
- Old .doc and .ppt files: open them in Word or PowerPoint and save as .docx or .pptx first. Most converters, ours included, only read the newer formats.
Word to Markdown with Pandoc
Install Pandoc from its installing page, then run, in the folder that holds your file:
pandoc -f docx -t gfm --extract-media=. --wrap=none handbook.docx -o handbook.md
What each part does, from the Pandoc User's Guide:
-f docx -t gfmreads Word and writes GitHub-Flavored Markdown, the dialect most docs sites and Git hosts render. Use-t commonmarkor-t markdown(Pandoc's own dialect) if your tool expects those.--extract-media=.copies the images out of the .docx and points the image links in the Markdown at those files. Pandoc keeps the path the images have inside the .docx, which starts withmedia/(we read this in its source code), so with.they land in amediafolder next to your Markdown. With--extract-media=mediathey would end up inmedia/media/.--wrap=nonekeeps each paragraph on one line. The default wraps lines at 72 columns, which makes Git diffs noisy when someone later edits a sentence.- Tracked changes: by default (
--track-changes=accept) Pandoc reads the document as if all insertions and deletions were accepted. Accept or reject them in Word first if that is not what you want.
Headings come from styles, not looks. The manual says the docx reader "only
reads those styles that it can convert into pandoc elements". In practice: text formatted with
Word's built-in Heading 1, Heading 2 and Heading 3 styles becomes #,
## and ###; a line that is merely bold and large stays a paragraph. If
headings are missing in the Markdown, fix the styles in Word and convert again. Add
-f docx+styles if you want every Word style kept as a custom-style
attribute, for a later script to act on.
Check the title line. Pandoc's docx reader treats a paragraph in Word's Title style as document metadata, not as text (we read this in its source code). So the first line of a document that uses the Title style may be missing from the Markdown; look at the top of the output and add it back if you need it.
We did not run Pandoc or MarkItDown for this guide. Their commands come from their own documentation, and what we say below about how they handle merged cells, speaker notes and hidden sheets comes from reading their source code (Pandoc 3.13 and MarkItDown's main branch, checked on 28 October 2026), not from running them. Our own tool's output for the same kind of file is shown further down.
Tables with merged cells: why Markdown breaks and the HTML fallback
A Markdown table (the "pipe table" that GitHub and most docs tools use) has exactly one cell per column in every row. The GFM specification says "The header row must match the delimiter row in the number of cells", fills short rows with empty cells and ignores extra ones. It has no syntax for a cell that spans two columns or two rows.
Word tables often have merged cells: a day name over two time slots, a team name that covers two rows. A converter then has three choices, and you should know which one yours makes:
- Flatten: put the text in the first cell and leave the covered cells empty. The table stays a Markdown table, but "Saturday" now looks as if it only applies to the morning column.
- Repeat: copy the text into every covered cell. Readable, but a reader or a language model may count the value twice.
- Write HTML: emit a
<table>withcolspan. GFM allows raw HTML blocks, so a renderer that follows the spec shows it as a table. The cost is that the table is no longer editable as Markdown, and some docs tools sanitise or strip HTML, so check yours.
What Pandoc does: its docx reader reads merged cells in both directions, and
when it writes GFM and a table has merged cells, it falls back to an HTML table with
colspan and rowspan. That is from its source code (version 3.13); we did
not run it. So Pandoc should keep merges down rows too, which ours does not (see below).
A quick way to see which one you get: convert a small file with a merged cell and look at the result. Here is ours. We made harvest-rota-merged-cells.docx (a 4 by 4 table: "Saturday" merged across two columns, "Team" merged down two rows, and one "Sorting" cell across two columns) and ran it through our tool on 28 October 2026. The Markdown it returned, unchanged:
# Harvest Week Rota
A sample written by Quarrywise to test merged table cells. Everything in it is invented.
<table>
<tr><th>Team</th><th colspan="2">Saturday</th><th>Sunday</th></tr>
<tr><td></td><td>Morning</td><td>Afternoon</td><td>All day</td></tr>
<tr><td>Blue</td><td>Picking</td><td>Sorting</td><td>Farm shop</td></tr>
<tr><td>Green</td><td colspan="2">Sorting</td><td>Picking</td></tr>
</table>
Cells merged across columns keep their span (colspan="2"), so no value lands in the
wrong column. Note what it does with the cell merged down two rows: "Team" stays in the top row
and the cell below is empty; there is no rowspan. Pandoc, from its source, would write
a rowspan here, so for tables with cells merged down rows it is likely the better
choice. Tables without merged cells stay ordinary Markdown tables in our output.
If you need a Markdown-only table, split the merged header into two rows by hand ("Saturday morning", "Saturday afternoon") before converting. That is usually clearer for readers too.
PowerPoint to Markdown, with speaker notes
A slide deck converts well when each slide has a title: the title becomes a heading, the text boxes become paragraphs and bullet lists, and tables become tables. Pictures and charts have no text to convert. What decides whether the result is useful for a knowledge base is often the speaker notes, because that is where the explanation lives.
- MarkItDown: install the PowerPoint extra and run it on the file:
Usepip install "markitdown[pptx]" markitdown slides.pptx -o slides.md"markitdown[all]"to install Word, PowerPoint and Excel support at once (double quotes work in bash, zsh, PowerShell and the Windows command prompt). Its README says it needs Python 3.10 to 3.14 and that it "may not be the best option for high-fidelity document conversions for human consumption": it is aimed at text for language models. It does read speaker notes: its PowerPoint converter adds each slide's notes under a### Notes:heading (from its source code; we did not run it). - Pandoc 3.8.3 and later: its
3.8.3 release notes add .pptx as
an input format (
pandoc -f pptx -t gfm slides.pptx -o slides.md). The notes do not mention speaker notes, and its PowerPoint reader has no code for them as of version 3.13, so expect them to be left out. If the notes matter, use another tool or check one of your decks first.
Our tool writes each slide title as a heading and adds the speaker notes after the slide
content, as a quote that starts with "Speaker notes:" (turn this off with
includeSpeakerNotes). The last slide of our sample
volunteer-day-slides.pptx, from the run described
below:
# Next steps
- Sign up on the shift board by Friday
- Bring closed shoes and a water bottle
> Speaker notes: Remind teams to bring gloves.
Every slide title in that deck became a level 1 heading (# Agenda,
# Tasks by team), and the second-level bullet on the agenda slide kept its
indentation.
Excel to Markdown tables (blank cells, dates, several sheets)
A spreadsheet maps naturally to a Markdown table, with three things to watch:
- Dates. Microsoft's page on date systems explains that a date is stored as a serial number, the days since 1 January 1900 (older Mac workbooks may use the 1904 date system instead, which the same page covers). A converter that ignores the cell's date format writes that number instead of the date. Check a date column in your output.
- Blank cells. An empty cell must stay an empty cell, or every value after it in that row shifts one column to the left. Also check that the converter does not write "nan" or "None" into empty cells. MarkItDown builds its Excel tables with the pandas library, whose HTML output writes "NaN" for a missing value by default, so expect that in blank cells (from its source code; we did not run it).
- Several sheets, and hidden ones. Each sheet needs its own table and a heading
with the sheet name. Hidden sheets often hold drafts or lookups you do not want published. Pandoc's
3.8.3 release notes say "Each worksheet turns into a section containing a table"
(
pandoc -f xlsx -t gfm budget.xlsx -o budget.md); MarkItDown reads Excel with its[xlsx]extra, and older .xls files with[xls]. MarkItDown reads every sheet, hidden ones included (from its source code), so remove hidden sheets you do not want before converting.
Formulas are another catch: converters that read the file directly, ours included, use the result Excel saved with the formula. A workbook written by a script and never opened in Excel may have no saved results; our tool leaves those cells blank and adds a warning.
Our sample budget.xlsx has two visible sheets and one hidden
sheet called "Scratch". Our tool returned the two visible sheets, each under its sheet name, and
skipped the hidden one (the quality report says "hidden_sheets_skipped": 1). The first
sheet, as returned:
# Budget
| Category | Q1 | Q2 | Q3 | Q4 | Notes |
|---|---|---|---|---|---|
| Seedlings | 1200 | 800 | | 400 | No planting in Q3 |
| Tools | 350 | 150 | 150 | 600 | |
| Energy | 2100 | 1700 | 1650 | 2300 | Heat pumps from Q2 |
| Training | | 900 | | | Moved to June |
Last updated | 2026-10-01
The empty Q3 cell for seedlings and the empty cells for training stay empty, so the 900 for training is still under Q2. The "Last updated" date is stored in the file as a date and comes out as 2026-10-01. That two-cell row sits apart from the table in the sheet, below a blank row, and came out as one line of text rather than a second table.
Many files, or no command line: our Actor on Apify
Our PDF to Markdown & RAG Chunks: Word, Excel, PowerPoint runs on the Apify platform. You give it links to files (or upload them) and it returns one row per file with the Markdown, in the Apify Console, through the API, or from n8n, Make or Zapier. It reads Word (.docx), PowerPoint (.pptx), Excel (.xlsx and .xls), PDF, HTML, Markdown, plain text and CSV in the same call. It uses no language model, so the same file gives the same Markdown every time.
- Word: heading styles, bullet and numbered lists, tables (merged cells as HTML), bold, italic and links. Word headers and footers are left out.
- PowerPoint: slide titles as headings, text and bullet levels, tables, speaker notes.
- Excel: each visible sheet as tables; blank cells stay empty; dates as ISO
dates. Hidden sheets only when you ask (
includeHiddenSheets). - Clear failures, free: a legacy .doc or .ppt, a damaged file or a link that cannot be read gets an error row with the reason, and is not charged.
A real run
This is the input we sent on 28 October 2026 through the Apify API (run
rM2UltmX7VIuJtdpu). The three files are three of the four public samples the tool's
form is prefilled with:
{
"documentUrls": [
"https://quarrywise.com/samples/volunteer-handbook.docx",
"https://quarrywise.com/samples/volunteer-day-slides.pptx",
"https://quarrywise.com/samples/budget.xlsx"
],
"outputFormat": "markdown",
"includeSpeakerNotes": true,
"includeHiddenSheets": false
}
All three files converted (status ok). The run took about 8 seconds from start to
finish, under 4 of them converting. The Word handbook's row (some fields left out, the Markdown
shortened):
{
"row_type": "document",
"source": "https://quarrywise.com/samples/volunteer-handbook.docx",
"file_name": "volunteer-handbook.docx",
"document_id": "8c2f0631a6fb2200bb37",
"status": "ok",
"format": "docx",
"title": "Volunteer Handbook (sample)",
"billable_pages": 1,
"markdown": "# Volunteer Handbook\n\n...\n\n## 1. Welcome\n\nThank you for helping at the orchard. This handbook explains how shifts work, **what to bring** and who to ask when something is unclear. The latest version is on [the sample page](https://quarrywise.com/).\n\n### 1.1 Who we are\n\n...",
"warnings": []
}
The middle of that Markdown, as returned: a numbered list and a table without merged cells, which stays a plain Markdown table.
## 2. Shifts
Shifts last four hours. To sign up for a shift:
1. Pick a free slot on the shift board.
1. Write your team name in the slot.
1. Confirm with the shift lead on the day.
| Day | Morning | Afternoon |
|---|---|---|
| Saturday | Farm shop | Open day tours |
| Sunday | Packing hall | Farm shop |
| Wednesday | Packing hall | |
Heading levels follow the Word heading styles, shifted down one level: the first line uses
Word's Title style and became #, so Heading 1 to 3 became ## to
#### (#### Notice period further on is a Heading 3). Every numbered item is written as 1.: Markdown renderers
number the list 1, 2, 3 themselves. The bold text and the link survive.
What the run cost at the listed prices: the handbook produced 1,253 characters of Markdown (1 billable page), the deck 4 slides (4 pages), the workbook 1 page. That is 3 documents × USD 0.003 + 6 pages × USD 0.0004 = 0.009 + 0.0024 = USD 0.0114, plus Apify's start event.
A second run the same day (JSl3nfbbA7rUGY3fV, about 3 seconds) sent two files as
base64 in the input: the merged-cell rota shown above, and
old-minutes.doc, a stand-in that holds only the header bytes of an old binary Office
file. Those bytes, plus the .doc name, are what the tool checks to recognise the format. The rota converted (1 page); the .doc
came back as a free error row:
{
"row_type": "error",
"file_name": "old-minutes.doc",
"status": "unsupported",
"error_reason": "unsupported-format",
"error_message": "Legacy Word (.doc) files are not supported yet. Save the file as .docx and run again."
}
Cost: 1 document × USD 0.003 + 1 page × USD 0.0004 = USD 0.0034. The .doc cost nothing.
Price and worked examples
Price when this guide was written: pay per event, USD 0.003 per document converted and USD 0.0004 per page converted, plus Apify's standard Actor start event, a fraction of a cent per run. Word and Excel files have no fixed pages, so every 3,000 characters of Markdown output count as one page (rounded up per file; in tables only filled cells count). A slide counts as one page. The Store page always shows the current price.
| Batch | Math | Cost |
|---|---|---|
| A Word file with 12,000 characters of output | 12,000 / 3,000 = 4 pages: 0.003 + 4 × 0.0004 | USD 0.0046 |
| A 40-slide deck | 0.003 + 40 × 0.0004 | USD 0.019 |
| A workbook with 45,000 characters of filled cells | 45,000 / 3,000 = 15 pages: 0.003 + 15 × 0.0004 | USD 0.009 |
500 Word files of about 7,500 characters each (set maxDocuments to 500) | 3 pages each: 500 × 0.003 + 1,500 × 0.0004 = 1.50 + 0.60 | USD 2.10 |
1,000 decks of 25 slides (set maxDocuments to 1000) | 1,000 × 0.003 + 25,000 × 0.0004 = 3.00 + 10.00 | USD 13.00 |
| A legacy .doc, a damaged file or a link that fails | nothing converted | USD 0 |
The examples leave out the start event. A run converts at most 100 files unless you raise
maxDocuments (up to 1,000). Set a maximum charge per run in the run
options: the tool converts as much as that budget allows, says so in the document's warnings, and
stops cleanly.
Calling it from the API
Start the run and wait up to 60 seconds for it; maxTotalChargeUsd caps what it may
charge. Then read the rows from the run's dataset:
curl -s -X POST "https://api.apify.com/v2/acts/quarrywise~pdf-to-markdown-rag/runs?waitForFinish=60&maxTotalChargeUsd=0.10" \
-H "Authorization: Bearer $APIFY_TOKEN" -H "Content-Type: application/json" \
--data '{"documentUrls":["https://quarrywise.com/samples/volunteer-handbook.docx"],"outputFormat":"markdown"}'
# if data.status is still RUNNING, wait again with data.id from the answer:
curl -s "https://api.apify.com/v2/actor-runs/RUN_ID?waitForFinish=60" -H "Authorization: Bearer $APIFY_TOKEN"
# then, with data.defaultDatasetId from the answer:
curl -s "https://api.apify.com/v2/datasets/DATASET_ID/items?clean=1" -H "Authorization: Bearer $APIFY_TOKEN"
Each row's markdown field holds the file's Markdown. To get .md files
instead, set saveMarkdownFiles to true: each row then carries a
markdown_file_url, a link to the file in the run's key-value store. Anyone who has that
link can read the file, so treat it like the document itself. For RAG, set
outputFormat to chunks; our guide on
PDF to Markdown for RAG covers chunk size, overlap
and calls from Python, n8n and Make.
The limits, honestly
- Legacy .doc and .ppt, OpenDocument (.odt, .odp, .ods), EPUB and RTF are not read; each gets a free error row. Save them as .docx, .pptx or .xlsx first.
- Cells merged down several rows are not written as
rowspan: the text stays in the top cell and the cells below are empty. - Charts and images are not described or saved. For Word and PowerPoint only their count is in
the quality report; Excel charts and images are left out without a count. Use
Pandoc with
--extract-mediaif you need the images. - Spreadsheets: up to 5,000 rows per sheet by default (
maxRowsPerSheet, at most 100,000), 512 filled columns per sheet and 2,000,000 cells per file. Formulas whose result was never saved come out blank, with a warning. - Decks: up to 300 slides by default (
maxPages, at most 5,000). - Files up to 50 MB by default (
maxFileSizeMb, at most 100 MB). Links behind a login are not supported: use a public or signed link, or upload the file.
A ready-made input on the Store: Word, Excel and PowerPoint to Markdown for LLMs (the same three sample files). Related ones: Excel to JSON and CSV to Markdown table.
See the converter on the Apify Store
FAQ
What is the best free way to convert docx to Markdown?
Pandoc: pandoc -f docx -t gfm --extract-media=. --wrap=none in.docx -o out.md.
It is free, turns Word heading styles into Markdown headings, saves the images with
--extract-media, and runs on your own computer, so the file never leaves it.
Why are my headings missing in the Markdown?
The text in Word is formatted to look like a heading but does not use a heading style. Apply Word's Heading 1, 2 and 3 styles and convert again. This is true for Pandoc and for our tool.
Can I get only the speaker notes from a deck?
Not as a separate field: the notes are part of each slide's Markdown, after the slide content, each starting with "Speaker notes:". Filter those lines out of the Markdown if you need them alone.
Is the output the same every time?
For our tool, yes: the same file with the same settings gives the same Markdown and the same
document_id, which helps when you update a knowledge base and want no
duplicates.
Do you keep my files?
No. Documents are processed in memory during the run and stored only in the run's own dataset and key-value store, which you control and can delete. Nothing is used for training.
Quarrywise is independent and isn't affiliated with or endorsed by Microsoft, the Pandoc project, GitHub or Apify.
Sources
- Pandoc User's Guide (input formats, --extract-media, --wrap, --track-changes, the docx reader and styles)
- Pandoc 3.8.3 release notes (pptx and xlsx input)
- Pandoc 3.13 source code (docx reader: merged cells, Title style, media paths; pptx reader; Markdown writer's HTML table fallback)
- Installing Pandoc
- MarkItDown on GitHub (formats, install extras, Python versions)
- MarkItDown converters source code (PowerPoint speaker notes, Excel sheets and blank cells)
- GitHub Flavored Markdown spec: tables and HTML blocks
- Microsoft Support: Date systems in Excel
- PDF to Markdown & RAG Chunks on the Apify Store (README, prices, limits)