Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

MarkItDown User Guide

Generated May 2026 from microsoft/markitdown at 604bba1

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

1. Quick Start

MarkItDown is a lightweight Python utility by Microsoft that converts various file formats to Markdown, purpose-built for LLMs and text analysis pipelines. It preserves document structure including headings, lists, tables, and links while producing clean, token-efficient Markdown output.

Key features:

  • Converts PDF, Word (.docx), PowerPoint (.pptx), Excel (.xlsx/.xls), HTML, CSV, JSON, XML, images, audio, EPub, Outlook messages (.msg), and ZIP archives
  • Fetches and converts YouTube video transcripts and Wikipedia articles from URLs
  • Supports Azure Document Intelligence for advanced document extraction
  • Extensible plugin system for third-party format converters
  • MCP (Model Context Protocol) server for integration with LLM applications
  • LLM-powered image descriptions and OCR via optional plugins
  • Reads from files, URLs, stdin, or Python streams

Prerequisites: Python 3.10 or higher

Install and run:

pip install 'markitdown[all]'
markitdown document.pdf
# Document Title

Content extracted from your PDF as clean Markdown...

Tip

For a quick one-liner that converts and saves in one step: markitdown report.pdf -o report.md. You can also pipe from stdin: cat file.pdf | markitdown.

What to try next:

  • Save output to a file — see Core Usage below
  • Convert content from a URL — see the Python API section
  • Use MarkItDown as a Python library — see Python API
  • Set up the MCP server for LLM integration — see MCP Server

2. Installation

System Requirements

Requirement Minimum Recommended
Python 3.10 3.12 or 3.13
OS Linux, macOS, Windows Any
RAM 256 MB 512 MB+ for large PDFs

Install via pip

Tip

The recommended installation uses [all] to include every optional dependency. If you only need specific format support, install individual extras to keep your environment lean.

pip install 'markitdown[all]'

Verify the installation:

markitdown --version
markitdown 0.1.6b2

Install Specific Format Support

Instead of installing all dependencies, you can choose only what you need:

pip install 'markitdown[pdf, docx, pptx]'
Extra Formats Enabled Dependencies Installed
[all] All formats below Everything
[pdf] PDF files pdfminer.six, pdfplumber
[docx] Word documents mammoth, lxml
[pptx] PowerPoint presentations python-pptx
[xlsx] Excel spreadsheets (.xlsx) pandas, openpyxl
[xls] Legacy Excel (.xls) pandas, xlrd
[outlook] Outlook messages (.msg) olefile
[audio-transcription] WAV, MP3 audio pydub, SpeechRecognition
[youtube-transcription] YouTube video transcripts youtube-transcript-api
[az-doc-intel] Azure Document Intelligence azure-ai-documentintelligence, azure-identity

Important

The extras changed between 0.0.1 and 0.1.0. If you are upgrading from an earlier version, switch to pip install 'markitdown[all]' to get the same all-inclusive behavior as before.

Note

The core package always includes beautifulsoup4, requests, markdownify, magika, charset-normalizer, and defusedxml. These handle HTML, plain text, CSV, JSON, XML, RSS, and basic web content without any extras.

Install from Source

git clone git@github.com:microsoft/markitdown.git
cd markitdown
pip install -e 'packages/markitdown[all]'

Virtual Environment Setup

Standard Python

python -m venv .venv
source .venv/bin/activate
pip install 'markitdown[all]'

uv

uv venv --python=3.12 .venv
source .venv/bin/activate
uv pip install 'markitdown[all]'

Conda

conda create -n markitdown python=3.12
conda activate markitdown
pip install 'markitdown[all]'

Docker

Build and run MarkItDown in a container:

docker build -t markitdown:latest .
docker run --rm -i markitdown:latest < ~/your-file.pdf > output.md

The Docker image is based on python:3.13-slim-bullseye and includes ffmpeg and exiftool for audio and image metadata extraction.

Install Troubleshooting

Error: No module named 'pdfminer' when converting PDFs

You installed MarkItDown without the PDF extra. Install it:

pip install 'markitdown[pdf]'

Error: No module named 'pptx' when converting PowerPoint files

Install the PowerPoint extra:

pip install 'markitdown[pptx]'

Error: pip install fails with version conflicts

Use a clean virtual environment:

python -m venv .venv-markitdown
source .venv-markitdown/bin/activate
pip install 'markitdown[all]'

Error: markitdown: command not found after installation

Ensure your Python scripts directory is on your PATH. If you installed in a virtual environment, make sure it is activated.

Error: Python version too old

MarkItDown requires Python 3.10+. Check your version:

python --version

If you have multiple Python versions, use python3.12 or the full path to a supported Python installation.

Error: magika installation fails

The magika dependency requires a compatible version (~=0.6.1). If you encounter issues, try:

pip install --upgrade pip
pip install 'markitdown[all]'

3. Configuration

MarkItDown is designed to work with zero configuration. All behavior is controlled through command-line flags or Python API parameters at runtime.

Environment Variables

Variable Purpose Default
EXIFTOOL_PATH Path to the exiftool binary for image/audio metadata Auto-detected from well-known paths
MARKITDOWN_ENABLE_PLUGINS Enable third-party plugins in the MCP server false

Note

If exiftool is installed in a standard location (/usr/bin, /usr/local/bin, /opt/homebrew/bin, C:\Program Files, etc.), MarkItDown finds it automatically. You only need EXIFTOOL_PATH if it is installed in a non-standard location.

For full configuration details including Azure Document Intelligence setup and LLM integration, see the Administrator & Developer Guide.


4. Core Usage

Command-Line Interface

The markitdown command converts a file and writes Markdown to stdout:

markitdown example.pdf

CLI Flags

Flag Description
<filename> File to convert (optional; reads from stdin if omitted)
-o, --output Write output to a file instead of stdout
-v, --version Show version number and exit
-x, --extension Hint about file extension (useful when reading from stdin)
-m, --mime-type Hint about the file's MIME type
-c, --charset Hint about the file's character encoding (e.g., UTF-8)
-d, --use-docintel Use Azure Document Intelligence instead of offline conversion
-e, --endpoint Azure Document Intelligence endpoint URL (required with -d)
-p, --use-plugins Enable third-party plugins for conversion
--list-plugins List installed third-party plugins and exit
--keep-data-uris Keep base64-encoded data URIs in output (truncated by default)

Save to File

Use -o or shell redirection:

markitdown report.pdf -o report.md
markitdown report.pdf > report.md

Read from stdin

Pipe content or redirect files:

cat document.pdf | markitdown
markitdown < document.pdf

Tip

When reading from stdin, MarkItDown uses magika to detect the file type automatically. If detection fails, provide a hint with --extension:

cat mystery-file | markitdown --extension .pdf

Converting URLs

MarkItDown can fetch and convert content directly from URLs via the Python API:

from markitdown import MarkItDown

md = MarkItDown()
result = md.convert("https://example.com/document.pdf")
print(result.markdown)

Supported URI schemes: http:, https:, file:, and data:.

Note

When fetching URLs, MarkItDown sends an Accept header that prefers Markdown: text/markdown, text/html;q=0.9, text/plain;q=0.8, */*;q=0.1. Sites that support Markdown responses (like Cloudflare's markdown-for-agents) will serve Markdown directly.

Python API

Basic Conversion

from markitdown import MarkItDown

md = MarkItDown()
result = md.convert("test.xlsx")
print(result.markdown)

The convert() method accepts:

Input Type Example
File path (string) md.convert("report.pdf")
File path (Path object) md.convert(Path("report.pdf"))
URL (string) md.convert("https://example.com/page.html")
Data URI (string) md.convert("data:text/plain;base64,SGVsbG8=")
Binary stream md.convert(open("file.pdf", "rb"))
requests.Response md.convert(response)

Conversion Result

The convert() method returns a DocumentConverterResult object:

result = md.convert("document.pdf")
print(result.markdown)  # The converted Markdown text
print(result.title)     # Optional document title (may be None)
print(str(result))      # Same as result.markdown

Note

The text_content property is a soft-deprecated alias for markdown. New code should use result.markdown or str(result).

Stream Conversion

Convert binary streams directly:

from markitdown import MarkItDown, StreamInfo

md = MarkItDown()

with open("report.pdf", "rb") as f:
    result = md.convert_stream(f, stream_info=StreamInfo(extension=".pdf"))
    print(result.markdown)

The StreamInfo object provides hints about the stream content:

Field Type Purpose
mimetype str MIME type (e.g., application/pdf)
extension str File extension with dot (e.g., .pdf)
charset str Character encoding (e.g., utf-8)
filename str Original filename
url str Source URL

LLM-Powered Image Descriptions

Provide an OpenAI-compatible client for AI-generated image captions:

from markitdown import MarkItDown
from openai import OpenAI

client = OpenAI()
md = MarkItDown(llm_client=client, llm_model="gpt-4o")
result = md.convert("photo.jpg")
print(result.markdown)

You can customize the prompt used for image description:

md = MarkItDown(
    llm_client=client,
    llm_model="gpt-4o",
    llm_prompt="Describe this image in detail, focusing on any text content."
)

Important

LLM-powered descriptions require a valid OpenAI API key (or compatible API). Without an llm_client, image conversion falls back to EXIF metadata extraction only.

Azure Document Intelligence

For high-fidelity document extraction:

from markitdown import MarkItDown

md = MarkItDown(docintel_endpoint="https://your-resource.cognitiveservices.azure.com/")
result = md.convert("complex-document.pdf")
print(result.markdown)

From the command line:

markitdown document.pdf -d -e "https://your-resource.cognitiveservices.azure.com/"

Note

Azure Document Intelligence requires an Azure subscription and a configured Document Intelligence resource. Authentication uses Azure's DefaultAzureCredential, which checks environment variables, managed identity, and CLI login in order.

Supported Formats

Format Extensions Notes
PDF .pdf Text extraction via pdfminer.six; table extraction via pdfplumber; supports form-style and borderless tables
Word .docx Via mammoth; supports equations, comments, custom style maps
PowerPoint .pptx Extracts text from slides including speaker notes
Excel .xlsx Via pandas + openpyxl; each sheet becomes a Markdown table
Legacy Excel .xls Via pandas + xlrd
CSV .csv Auto-detects delimiter and encoding
HTML .html, .htm Converts via markdownify; special handling for Wikipedia and Bing SERP pages
EPub .epub Extracts text from e-book files
Images .jpg, .jpeg, .png EXIF metadata extraction; optional LLM-powered descriptions
Audio .mp3, .wav, .m4a EXIF metadata; optional speech transcription via SpeechRecognition
Outlook .msg Extracts email content from Outlook message files
Jupyter .ipynb Extracts code cells and outputs from notebooks
RSS/XML .xml RSS feed content extraction
JSON .json Rendered as formatted text
ZIP .zip Iterates over archive contents and converts each file
YouTube URL Extracts title, description, metadata, and transcript
Wikipedia URL Special formatting for Wikipedia article pages

Note

MarkItDown uses Google's magika library for content-type detection. Even without file extension hints, it can often identify the correct format from file content alone.

MCP Server

The markitdown-mcp package provides a Model Context Protocol server that lets LLM applications (like Claude Desktop) convert files to Markdown.

Install

pip install markitdown-mcp

Run with STDIO (default)

markitdown-mcp

Run with HTTP/SSE

markitdown-mcp --http --host 127.0.0.1 --port 3001
Flag Description Default
--http Use Streamable HTTP and SSE transport STDIO
--sse Deprecated alias for --http -
--host Host to bind to (only with --http) 127.0.0.1
--port Port to listen on (only with --http) 3001

The MCP server exposes one tool: convert_to_markdown(uri) which accepts http:, https:, file:, and data: URIs.

Warning

The MCP server runs with the privileges of the user who started it. It has no built-in authentication. When using --http, it binds to localhost by default. Do not bind to non-localhost interfaces unless you understand the security implications — any process on the network could use the server to read files accessible to that user.

Claude Desktop Integration

Build the Docker image and configure claude_desktop_config.json:

docker build -t markitdown-mcp:latest packages/markitdown-mcp/
{
  "mcpServers": {
    "markitdown": {
      "command": "docker",
      "args": ["run", "--rm", "-i", "markitdown-mcp:latest"]
    }
  }
}

To mount a local directory for file access:

{
  "mcpServers": {
    "markitdown": {
      "command": "docker",
      "args": [
        "run", "--rm", "-i",
        "-v", "/home/user/data:/workdir",
        "markitdown-mcp:latest"
      ]
    }
  }
}

Enable Plugins in MCP Server

Set the MARKITDOWN_ENABLE_PLUGINS environment variable:

MARKITDOWN_ENABLE_PLUGINS=true markitdown-mcp

5. Common Tasks

Convert a PDF and Save to File

markitdown report.pdf -o report.md

Batch Convert All PDFs in a Directory

for f in *.pdf; do
    markitdown "$f" -o "${f%.pdf}.md"
done

Convert a Web Page

from markitdown import MarkItDown

md = MarkItDown()
result = md.convert("https://example.com/blog-post.html")
with open("blog-post.md", "w") as f:
    f.write(result.markdown)

Extract YouTube Transcript

from markitdown import MarkItDown

md = MarkItDown()
result = md.convert("https://www.youtube.com/watch?v=dQw4w9WgXcQ")
print(result.markdown)

Note

YouTube transcript extraction requires the [youtube-transcription] extra: pip install 'markitdown[youtube-transcription]'

Convert Excel Spreadsheet to Markdown Tables

from markitdown import MarkItDown

md = MarkItDown()
result = md.convert("data.xlsx")
print(result.markdown)

Each sheet in the workbook is converted to a separate Markdown table.

Get Image Descriptions with AI

from markitdown import MarkItDown
from openai import OpenAI

client = OpenAI()
md = MarkItDown(llm_client=client, llm_model="gpt-4o")
result = md.convert("screenshot.png")
print(result.markdown)

Convert with Custom DOCX Style Mapping

from markitdown import MarkItDown

md = MarkItDown(style_map="p[style-name='Heading 1'] => h1:fresh")
result = md.convert("custom-styled.docx")
print(result.markdown)

Process All Files in a ZIP Archive

markitdown archive.zip

MarkItDown automatically iterates over ZIP contents and converts each supported file.

Convert Outlook Email to Markdown

from markitdown import MarkItDown

md = MarkItDown()
result = md.convert("message.msg")
print(result.markdown)

Use MarkItDown with OCR Plugin

from markitdown import MarkItDown
from openai import OpenAI

md = MarkItDown(
    enable_plugins=True,
    llm_client=OpenAI(),
    llm_model="gpt-4o",
)
result = md.convert("scanned-document.pdf")
print(result.markdown)

Important

The OCR plugin (markitdown-ocr) must be installed separately: pip install markitdown-ocr. It requires an OpenAI-compatible LLM client. Without llm_client, the plugin loads but OCR is silently skipped, falling back to the standard converter.

Convert from a Data URI

import base64
from markitdown import MarkItDown

md = MarkItDown()
data = base64.b64encode(b"Hello, World!").decode()
result = md.convert(f"data:text/plain;base64,{data}")
print(result.markdown)

Use MarkItDown in a Pipeline

curl -s https://example.com/document.pdf | markitdown --extension .pdf > output.md

6. Troubleshooting

Diagnostic Decision Tree

  1. Is MarkItDown installed? Run markitdown --version
  2. Is the file format supported? Check the Supported Formats table in Core Usage
  3. Is the required optional dependency installed? Check the extras table in Installation
  4. Is the file corrupted? Try opening it in its native application

Error: Could not convert stream to Markdown. No converter attempted a conversion

Cause: The file format is not supported or was not recognized.

Solution:

  1. Check that the file extension matches the actual format
  2. Provide explicit hints: markitdown file --extension .pdf --mime-type application/pdf
  3. Verify the format is in the supported formats list

Prevention: Always use correct file extensions. When piping from stdin, provide --extension or --mime-type hints.

Error: File conversion failed after N attempts

Cause: A converter recognized the file but failed during conversion. This wraps the underlying exceptions from individual converter attempts.

Solution:

  1. Check the error message for specific converter failures
  2. Ensure all required dependencies are installed for that format
  3. Try with a different file of the same type to rule out file corruption

Error: MissingDependencyException for a specific format

Cause: The optional dependency for that file format is not installed.

Solution:

pip install 'markitdown[pdf]'  # Replace 'pdf' with the needed extra

The error message tells you exactly which extra to install.

Error: A one-byte document is generated with no error output

Cause: Known issue (GitHub #1784). Some files may produce minimal or empty output without raising an error.

Solution:

  1. Verify the file is not empty or corrupted
  2. Try converting with Azure Document Intelligence (-d flag) for better extraction
  3. Check if the file contains extractable text (scanned images in PDFs require OCR)

Warning

If you consistently get empty output for a specific file type, it may indicate a converter bug. File a GitHub issue with a sanitized sample file.

PDF Conversion Produces Poor Results

Cause: PDFs with complex layouts, scanned images, or unusual fonts may not extract well with the default converter.

Solution:

  1. Try Azure Document Intelligence for higher fidelity: markitdown file.pdf -d -e "<endpoint>"
  2. Install and use the OCR plugin for scanned documents
  3. For form-style PDFs, the built-in converter uses pdfplumber for table detection automatically

Output Contains Truncated Data URIs

Cause: By default, MarkItDown truncates base64-encoded data URIs (like embedded images) to keep output clean.

Solution: Use --keep-data-uris to preserve them:

markitdown document.html --keep-data-uris

YouTube Transcript Not Extracted

Cause: Either the youtube-transcript-api dependency is not installed, or the video has no available transcript.

Solution:

  1. Install the dependency: pip install 'markitdown[youtube-transcription]'
  2. Verify the video has captions/subtitles available
  3. Note that private or age-restricted videos may not expose transcripts

Warning

YouTube short URLs (youtu.be/...) and Shorts (youtube.com/shorts/...) are not currently recognized by the YouTube converter (GitHub #1775). Use full youtube.com/watch?v= URLs.

Memory Issues with Large PDFs

Cause: Previously reported as a regression in version 0.1.5 (GitHub #1611, now closed/fixed). Large PDFs can consume significant memory during extraction.

Solution: The current version processes PDF pages individually and calls page.close() after each page to free memory. If you still experience issues with very large PDFs, consider splitting the PDF into smaller chunks before conversion.


7. FAQ

Q: What makes MarkItDown different from other document converters?

MarkItDown focuses on preserving document structure as Markdown specifically for consumption by LLMs and text analysis tools. Unlike high-fidelity converters designed for human readability, it prioritizes clean, token-efficient Markdown that AI systems process well.

Q: Can I convert files without installing all dependencies?

Yes. Install only the extras you need: pip install 'markitdown[pdf, docx]'. The core package handles HTML, plain text, CSV, JSON, and XML without any optional dependencies.

Q: How does file type detection work?

MarkItDown uses a multi-layered detection approach: file extension, MIME type hints, and Google's magika library for content-based detection. It generates multiple "guesses" and tries converters in priority order until one succeeds.

Q: Can I write my own converter plugin?

Yes. MarkItDown supports third-party plugins via Python's entry points system. See the packages/markitdown-sample-plugin directory for a complete example. Plugins implement the DocumentConverter interface with accepts() and convert() methods, and register via the markitdown.plugin entry point group.

Q: Does MarkItDown support Word equations?

Yes. The DOCX converter includes a math converter that handles Office MathML (OMML) and converts equations to LaTeX notation in the Markdown output.

Q: How are ZIP files handled?

MarkItDown recursively iterates over all files in a ZIP archive and attempts to convert each one. The output is the concatenation of all successfully converted files.

Q: Is there a breaking change between 0.0.1 and 0.1.0?

Yes, three important changes: (1) Dependencies are now organized into optional feature groups — use pip install 'markitdown[all]' for backward-compatible behavior. (2) convert_stream() now requires a binary file-like object (not io.StringIO). (3) The DocumentConverter class interface changed to read from streams rather than file paths, which affects plugin developers.

Q: Can I use a custom requests session for URL fetching?

Yes. Pass it to the constructor:

import requests
session = requests.Session()
session.headers.update({"Authorization": "Bearer token"})
md = MarkItDown(requests_session=session)

Q: What happens if multiple converters can handle the same file?

Converters are tried in priority order. Specific format converters (priority 0.0) are tried before generic ones like PlainText and HTML (priority 10.0). The first converter that accepts the file and successfully converts it wins.

Q: How can I debug MCP server issues?

Use the MCP Inspector tool:

npx @modelcontextprotocol/inspector

Connect to the server via STDIO, Streamable HTTP (http://127.0.0.1:3001/mcp), or SSE (http://127.0.0.1:3001/sse).