skills/markitdown/references/workflows.md
All workflows target MarkItDown 0.1.6 and use .markdown, not the legacy .text_content alias.
from hashlib import sha256
from pathlib import Path
from markitdown import MarkItDown
source = Path("protocol.docx").resolve(strict=True)
output = Path("protocol.md")
result = MarkItDown().convert_local(source)
output.write_text(result.markdown, encoding="utf-8")
provenance = {
"source": source.name,
"sha256": sha256(source.read_bytes()).hexdigest(),
"title": result.title,
"output": str(output),
}
print(provenance)
For very large files, compute the hash incrementally rather than loading the source again.
Validate size and type before conversion. Use BytesIO plus explicit hints:
from io import BytesIO
from markitdown import MarkItDown, StreamInfo
def convert_pdf_upload(payload: bytes, filename: str) -> str:
max_bytes = 25 * 1024 * 1024
if len(payload) > max_bytes:
raise ValueError("PDF exceeds the 25 MiB conversion limit")
if not payload.startswith(b"%PDF-"):
raise ValueError("Payload does not have a PDF signature")
result = MarkItDown().convert_stream(
BytesIO(payload),
stream_info=StreamInfo(
extension=".pdf",
mimetype="application/pdf",
filename=filename,
),
)
return result.markdown
Signature checks are only one validation layer; they do not make the parser sandboxed.
python scripts/batch_convert.py inputs/ outputs/ \
--recursive \
--extensions .pdf .docx .pptx .xlsx .csv .html \
--manifest outputs/manifest.json
Properties:
paper.pdf.md--overwrite--pluginsRetry failed files after installing the missing format extra:
uv pip install "markitdown[pdf,docx,pptx,xlsx]==0.1.6"
python scripts/batch_convert.py inputs/ outputs/ --recursive --overwrite
Use a fresh output directory when comparing converter versions.
Input naming convention:
Author_Year_Title.pdf
Convert and build indexes:
python scripts/convert_literature.py papers/ literature/ \
--recursive \
--create-index
Organize by inferred year:
python scripts/convert_literature.py papers/ literature/ \
--recursive \
--organize-by-year \
--create-index
Each output contains provenance front matter:
---
title: "Example title"
author: "Smith"
year: "2025"
source: "incoming/Smith_2025_Example_Title.pdf"
converted_at: "2026-07-23T17:00:00+00:00"
markitdown_version: "0.1.6"
---
The helper also writes catalog.json and INDEX.md when --create-index is requested. Metadata inferred from a filename is a convenience, not authoritative bibliographic metadata.
Recommended sequence:
Example conversion envelope:
from dataclasses import asdict, dataclass
from hashlib import sha256
from importlib.metadata import version
from pathlib import Path
from markitdown import MarkItDown
@dataclass(frozen=True)
class ConvertedDocument:
source_name: str
source_sha256: str
converter_version: str
title: str | None
markdown: str
def convert_for_ingestion(path: Path) -> ConvertedDocument:
path = path.resolve(strict=True)
digest = sha256(path.read_bytes()).hexdigest()
result = MarkItDown().convert_local(path)
if not result.markdown.strip():
raise ValueError(f"Conversion produced no text: {path.name}")
return ConvertedDocument(
source_name=path.name,
source_sha256=digest,
converter_version=version("markitdown"),
title=result.title,
markdown=result.markdown,
)
Do not treat Markdown line numbers as stable page citations. MarkItDown does not expose PDF page coordinates.
Use a two-pass workflow:
This minimizes cost and external data transfer.
Use MarkItDown to understand workbook organization:
from markitdown import MarkItDown
preview = MarkItDown().convert_local("assay-results.xlsx").markdown
print(preview)
Then use a spreadsheet/dataframe library for calculations:
Do not parse a scientific numeric dataset back out of Markdown when the original workbook is available.
from markitdown import MarkItDown
result = MarkItDown().convert_local("lab-meeting.pptx")
Review:
If images carry essential meaning, add an approved vision description or OCR pass and label generated text as model-derived.
markitdown-ocr for approved vision-provider processingDo not automatically send failed local conversions to a cloud service.
The application—not MarkItDown—should:
requests.ResponseThen:
result = MarkItDown().convert_response(validated_response)
See security.md for the complete SSRF policy. A simple scheme check is not sufficient.
Keep a small, redistributable corpus covering:
For each fixture, assert:
Avoid asserting a full byte-for-byte Markdown snapshot unless exact formatting stability is required; semantic assertions are less brittle.
Track per file:
The bundled batch manifest provides a baseline for these records.