A premium web application that converts any file to clean Markdown. Powered by Microsoft MarkItDown.
Drop any file into the web UI and get clean, structured Markdown back in seconds. The conversion engine is Microsoft's MarkItDown library — the most popular file-to-Markdown converter on GitHub (119k+ stars).
For multiple files, use the Bulk tab in the web UI, batch.bat, or batch_convert.py. Bulk mode writes one .md file per input file, preserves browser-provided folder paths, records results in batch-results.json, and packages the outputs as a ZIP.
| Category | Formats |
|---|---|
| Documents | PDF, Word (.docx), PowerPoint (.pptx), Excel (.xlsx/.xls), Outlook (.msg), EPUB |
| Web | HTML, HTM |
| Data | CSV, JSON, XML |
| Images | JPEG, PNG, GIF, BMP, TIFF, WebP (EXIF metadata + OCR) |
| Archives | ZIP (recursive content extraction) |
| Audio | WAV, MP3 (requires speech transcription API key) |
| Text | TXT, Markdown, reStructuredText |
Browser (HTML/CSS/JS)
│
├── Drag-and-drop file upload
│ ↓
├── POST /api/convert (multipart form)
│ ↓
FastAPI Server (server.py)
│
├── Receives file → writes to temp file
├── Routes spreadsheets to local streaming converter
├── Routes other files to MarkItDown engine
├── MarkItDown detects non-spreadsheet formats by extension
├── Delegates to format-specific converter:
│ ├── PDF → pdfminer.six / pdfplumber
│ ├── DOCX → mammoth
│ ├── PPTX → python-pptx
│ ├── XLSX/XLS/CSV → streaming openpyxl / xlrd / csv
│ ├── HTML → markdownify (html→md)
│ ├── Images → EXIF extraction + optional OCR
│ ├── Audio → SpeechRecognition (optional)
│ └── Text formats → direct read
├── Returns Markdown text, or bounded preview + download artifact
├── Cleans up temp file
│ ↓
└── JSON response → browser
│
├── Raw Markdown displayed (left pane)
├── markdown-it renders HTML preview (right pane)
├── Copy to clipboard / Download as .md
└── Small full results saved to localStorage history
-
Upload — User drags a file or clicks to browse. The file is sent as a
multipart/form-dataPOST to/api/convert. -
Temp File — FastAPI receives the upload, writes it to a temporary file with the original extension preserved (MarkItDown needs a file path to detect format).
-
Conversion — Spreadsheet files use the local streaming converter in
spreadsheet_convert.py, which writes Markdown incrementally and returns a bounded preview. Other files callMarkItDown.convert(path). Internally, MarkItDown uses magika for content-type detection and routes to the appropriate format handler. -
Response — The server returns a JSON payload with filename, file size, character count, and conversion time. For large spreadsheet output,
markdowncontains the preview anddownload_idpoints to the full generated.mdartifact. The upload temp file is deleted in afinallyblock. -
Preview — The browser receives the Markdown and renders it two ways simultaneously:
- Raw pane —
textContentassignment (safe, no XSS) - Preview pane — markdown-it parses and renders to HTML
- Raw pane —
-
Export — User can copy to clipboard (Clipboard API with
execCommandfallback) or download as.md. Large spreadsheet downloads are fetched from the authenticated artifact endpoint. -
History — Recent conversions are saved to
localStorage(last 20 entries). Large or preview-only results save metadata only because full artifacts expire server-side.
- No database — All state is client-side (localStorage). Server is stateless.
- Temp file cleanup — Guaranteed via
try/finallyblock. Files are never persisted. - Spreadsheet guardrails — Streaming conversion avoids pandas whole-workbook loads and enforces sheet, column, cell, output, ZIP expansion, and timeout limits.
- Artifact cleanup — Large generated Markdown artifacts are stored under the OS temp directory and expire automatically.
- 50MB limit — Enforced both client-side (pre-upload check) and server-side (post-read check).
- Local-only by default — Launchers bind to
127.0.0.1and use a fixed app port. - No stale frontend —
/and/static/*are served withCache-Control: no-cacheso browsers revalidate after every code update instead of executing days-old JavaScript. - Hot reload opt-in — Set
APP_RELOAD=1before runningpython server.py.
- Python 3.10+ (tested on 3.12)
- pip
git clone https://github.com/YOUR_USER/MarkItDown.git
cd MarkItDown
pip install -r requirements.txtstart.batOpen http://127.0.0.1:8000 in your browser. Use stop.bat to close the background server.
- Drag any supported file onto the drop zone (or click to browse)
- View results in split pane — raw Markdown (left) and rendered preview (right)
- Copy to clipboard or Download as
.md - History — access past conversions from the sidebar
- Bulk — switch to the Bulk tab to upload many files or a folder and download a ZIP
The Bulk tab is for converting many files without repeating the single-file upload flow:
- Select files or a folder — or drag a folder straight onto the drop zone (dropped folders are traversed recursively, subfolders included).
- Click Bulk Convert.
- Watch live progress (
Converting files... 12/48plus the current file). - Review per-file converted, skipped, and failed results.
- Download the generated ZIP.
The ZIP contains one Markdown file per successful source file plus batch-results.json. Partial failures do not discard successful conversions.
Robustness behavior:
- Unreadable selections (a file deleted or moved after picking it) are skipped before upload with a toast naming the item, instead of killing the whole batch with a network error.
- Bulk batches run on their own conversion lane, so single-file conversions stay responsive while a long batch runs.
- If the server restarts while the tab is open, the page re-reads its session token and retries automatically — no manual refresh required.
For demo prep or troubleshooting, generate a small redacted failure packet:
python tools/doctor.pystart.bat runs this same doctor automatically after dependency setup and before launching the server. It writes .doctor.json and blocks startup only when diagnostics returns a hard failure. To skip that preflight intentionally:
$env:MD_CREATOR_SKIP_DOCTOR = "1"
.\start.batThe same packet is available from the authenticated API:
GET /api/diagnostics
X-MD-Creator-Token: <local session token>Diagnostics classify likely failure areas without exposing raw local paths:
bad_deploymentartifact_degradedprovider_failedcap_trippedapi_failedconversion_failed
The packet includes runtime state, dependency availability, artifact directory health, upload limits, bulk job counts, spreadsheet guardrails, and recent failures.
Double-click batch.bat for the folder workflow:
- The first run creates an
inputfolder if it does not exist. - Drop files into
input. - Run
batch.batagain. - Markdown files are written to
output, with subfolders preserved. - A machine-readable report is written to
output/batch-results.json.
batch.bat uses academic mode by default. PDF files use MinerU when available and fall back to Standard if MinerU is unavailable or fails. Non-PDF files always use Standard in academic mode.
CLI usage:
python batch_convert.py input output --engine academic
python batch_convert.py input output --engine standard --overwrite
python batch_convert.py input output --engine auto --jsonSafety behavior:
- Existing
.mdoutputs are skipped unless--overwriteis provided. - The output folder cannot be inside the input folder, which prevents recursive output loops.
- Each source file keeps an independent success, skipped, or error record.
- Files over the configured size limit are reported as errors and are not converted.
- Spreadsheet files use the same streaming converter as the web app and write Markdown directly to the output path.
MarkItDown/
├── server.py # FastAPI backend — file upload, MarkItDown conversion, static serving
├── spreadsheet_convert.py # Streaming XLSX/XLS/CSV → Markdown converter with guardrails
├── batch.bat # Double-click folder batch converter
├── batch_convert.py # Testable batch conversion CLI/API
├── requirements.txt # Python dependencies
└── static/
├── index.html # Single-page application — drag/drop, results, history sidebar
├── css/
│ └── style.css # Design system — dark mode, glassmorphism, responsive
└── js/
└── app.js # Client logic — upload, preview, clipboard, download, history
Returns the list of supported input formats.
Response:
{
"formats": [
{ "ext": ".pdf", "label": "PDF", "icon": "📄", "category": "Documents" },
...
]
}Converts an uploaded file to Markdown.
Request: multipart/form-data with a file field.
Response:
{
"success": true,
"filename": "report.pdf",
"extension": ".pdf",
"file_size": 245760,
"markdown": "# Report Title\n\nContent here...",
"markdown_length": 4521,
"conversion_time": 1.23,
"preview_truncated": false
}Large spreadsheet responses include a bounded preview in markdown and a temporary download id:
{
"success": true,
"filename": "large.xlsx",
"extension": ".xlsx",
"file_size": 245760,
"markdown": "## Sheet1\n\n| Column | ...",
"markdown_length": 10485760,
"markdown_is_preview": true,
"preview_truncated": true,
"download_id": "temporary-id",
"download_filename": "large.md",
"converter": "spreadsheet"
}Downloads a generated Markdown artifact. Requires the same X-MD-Creator-Token header as conversion requests.
Creates a temporary bulk conversion job from uploaded files.
Request: multipart/form-data with repeated files fields, optional repeated paths fields for relative folder paths, and an engine field.
Response:
{
"job": {
"id": "temporary-job-id",
"status": "queued",
"engine": "standard",
"file_count": 2,
"total_size": 12345
}
}Returns the current bulk job state and summary once complete.
Downloads the completed bulk ZIP artifact. Requires the same X-MD-Creator-Token header as conversion requests.
Error Response (413):
{ "detail": "File too large. Maximum size is 50MB" }| Layer | Technology | Purpose |
|---|---|---|
| Backend | FastAPI | Async web framework |
| Server | Uvicorn | ASGI server |
| Engine | MarkItDown | File → Markdown conversion |
| Frontend | Vanilla HTML/CSS/JS | Zero-dependency UI |
| Preview | markdown-it | Markdown → HTML rendering |
| Fonts | Inter + JetBrains Mono | Typography |