# MarkItDown User Guide

## 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:**

```bash
pip install 'markitdown[all]'
markitdown document.pdf
```

```text
# 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.

```bash
pip install 'markitdown[all]'
```

Verify the installation:

```bash
markitdown --version
```

```text
markitdown 0.1.6b2
```

### Install Specific Format Support

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

```bash
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

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

### Virtual Environment Setup

=== "Standard Python"
    ```bash
    python -m venv .venv
    source .venv/bin/activate
    pip install 'markitdown[all]'
    ```

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

=== "Conda"
    ```bash
    conda create -n markitdown python=3.12
    conda activate markitdown
    pip install 'markitdown[all]'
    ```

### Docker

Build and run MarkItDown in a container:

```bash
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:

```bash
pip install 'markitdown[pdf]'
```

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

Install the PowerPoint extra:

```bash
pip install 'markitdown[pptx]'
```

**Error: `pip install` fails with version conflicts**

Use a clean virtual environment:

```bash
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:

```bash
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:

```bash
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:

```bash
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:

```bash
markitdown report.pdf -o report.md
```

```bash
markitdown report.pdf > report.md
```

#### Read from stdin

Pipe content or redirect files:

```bash
cat document.pdf | markitdown
```

```bash
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`:
> ```bash
> cat mystery-file | markitdown --extension .pdf
> ```

#### Converting URLs

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

```python
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

```python
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:

```python
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:

```python
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:

```python
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:

```python
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:

```python
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:

```bash
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

```bash
pip install markitdown-mcp
```

#### Run with STDIO (default)

```bash
markitdown-mcp
```

#### Run with HTTP/SSE

```bash
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`:

```bash
docker build -t markitdown-mcp:latest packages/markitdown-mcp/
```

```json
{
  "mcpServers": {
    "markitdown": {
      "command": "docker",
      "args": ["run", "--rm", "-i", "markitdown-mcp:latest"]
    }
  }
}
```

To mount a local directory for file access:

```json
{
  "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:

```bash
MARKITDOWN_ENABLE_PLUGINS=true markitdown-mcp
```

---

## 5. Common Tasks

### Convert a PDF and Save to File

```bash
markitdown report.pdf -o report.md
```

### Batch Convert All PDFs in a Directory

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

### Convert a Web Page

```python
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

```python
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

```python
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

```python
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

```python
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

```bash
markitdown archive.zip
```

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

### Convert Outlook Email to Markdown

```python
from markitdown import MarkItDown

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

### Use MarkItDown with OCR Plugin

```python
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

```python
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

```bash
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:**

```bash
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:

```bash
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:

```python
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:

```bash
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`).

---

_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. Unofficial; not affiliated with or reviewed by the project._
