skills/markitdown/references/cloud_and_ocr.md
This guide distinguishes four different features that are often conflated:
markitdown-ocr vision pluginAll examples target MarkItDown 0.1.6.
| Requirement | Best fit |
|---|---|
| Describe a standalone JPEG/PNG or images on PPTX slides | Built-in llm_client path |
| Read text from PDF/DOCX/PPTX/XLSX embedded images | markitdown-ocr |
| OCR scanned PDFs with Azure layout extraction | Document Intelligence |
| Custom fields, YAML front matter, video, or richer multimodal analysis | Content Understanding |
| Data must remain local | Use a separate local OCR/layout parser |
None of the first four options is a local Tesseract workflow.
Before using an external service:
The built-in JPEG/PNG and PPTX paths can call an OpenAI-compatible client. MarkItDown encodes image bytes as a data URI and calls:
client.chat.completions.create(model=..., messages=...)
Install a reviewed client version:
uv pip install "markitdown[pptx]==0.1.6" "openai==2.41.1"
from markitdown import MarkItDown
from openai import OpenAI
# The SDK obtains only its named provider credential through its normal
# configuration. The image and prompt are sent to that provider.
client = OpenAI()
converter = MarkItDown(
llm_client=client,
llm_model="gpt-4o",
llm_prompt=(
"Describe the scientific figure. Transcribe visible labels, identify "
"axes and units, and report trends without inventing missing values."
),
)
result = converter.convert_local("figure.png")
print(result.markdown)
Use a provider/model approved by the user; model identifiers and availability are provider-specific.
markitdown-ocr PluginVersion 0.1.6 introduced the official monorepo plugin. The published plugin version is 0.1.0.
Install exact versions:
uv pip install \
"markitdown==0.1.6" \
"markitdown-ocr==0.1.0" \
"openai==2.41.1"
Review discovery before activation:
markitdown --list-plugins
Configure through Python:
from markitdown import MarkItDown
from openai import OpenAI
converter = MarkItDown(
enable_plugins=True,
llm_client=OpenAI(),
llm_model="gpt-4o",
llm_prompt=(
"Extract all visible text exactly. Preserve table rows, columns, "
"symbols, signs, decimal points, and units. Do not summarize."
),
)
result = converter.convert_local("scanned-paper.pdf")
print(result.markdown)
OCR blocks are inserted using markers similar to:
*[Image OCR]
<extracted text>
[End OCR]*
-1.0, ahead of built-ins.llm_client is supplied, the plugin loads but silently falls back to standard conversion.The plugin README shows --llm-client and --llm-model, but MarkItDown 0.1.6's core CLI parser does not define those options. Use the Python API above rather than copying that CLI example.
Install:
uv pip install "markitdown[az-doc-intel]==0.1.6"
The converter sends the complete file to Azure's prebuilt-layout analyzer and requests Markdown output. For PDF/images it enables formula extraction, high-resolution OCR, and font-style analysis.
If no explicit credential is supplied, MarkItDown:
AZURE_API_KEY value with AzureKeyCredential when present.DefaultAzureCredential.Prefer workload identity, managed identity, or another DefaultAzureCredential source over long-lived keys.
markitdown report.pdf \
--use-docintel \
--endpoint "https://RESOURCE.cognitiveservices.azure.com/" \
-o report.md
The CLI requires a filename; stdin is not accepted with this mode.
from azure.identity import DefaultAzureCredential
from markitdown import MarkItDown
converter = MarkItDown(
docintel_endpoint="https://RESOURCE.cognitiveservices.azure.com/",
docintel_credential=DefaultAzureCredential(),
)
result = converter.convert_local("report.pdf")
print(result.markdown)
Restrict routing:
from markitdown import MarkItDown
from markitdown.converters import DocumentIntelligenceFileType
converter = MarkItDown(
docintel_endpoint="https://RESOURCE.cognitiveservices.azure.com/",
docintel_file_types=[
DocumentIntelligenceFileType.PDF,
DocumentIntelligenceFileType.PNG,
],
)
Supported enum values include DOCX, PPTX, XLSX, HTML, PDF, JPEG, PNG, BMP, and TIFF. The default list excludes HTML.
The 0.1.6 default Document Intelligence API version is 2024-07-31-preview; override it with docintel_api_version only after checking Azure compatibility.
Install:
uv pip install "markitdown[az-content-understanding]==0.1.6"
Content Understanding provides:
Every routed convert()/convert_local() call is an Azure API call and may be billable.
markitdown interview.mp4 \
--use-cu \
--cu-endpoint "https://RESOURCE.cognitiveservices.azure.com/" \
--cu-file-types mp4 \
-o interview.md
With a custom analyzer:
markitdown invoice.pdf \
--use-cu \
--cu-endpoint "https://RESOURCE.cognitiveservices.azure.com/" \
--cu-analyzer "my-invoice-analyzer" \
--cu-file-types pdf \
-o invoice.md
from azure.identity import DefaultAzureCredential
from markitdown import MarkItDown
from markitdown.converters import ContentUnderstandingFileType
converter = MarkItDown(
cu_endpoint="https://RESOURCE.cognitiveservices.azure.com/",
cu_credential=DefaultAzureCredential(),
cu_file_types=[
ContentUnderstandingFileType.PDF,
ContentUnderstandingFileType.PNG,
],
)
result = converter.convert_local("report.pdf")
print(result.markdown)
Custom analyzer:
converter = MarkItDown(
cu_endpoint="https://RESOURCE.cognitiveservices.azure.com/",
cu_credential=DefaultAzureCredential(),
cu_analyzer_id="my-contract-analyzer",
cu_file_types=[ContentUnderstandingFileType.PDF],
)
When the custom analyzer's modality is incompatible with an input, the converter falls back to the matching prebuilt analyzer.
| Modality | Analyzer |
|---|---|
| Document | prebuilt-documentSearch |
| Image | prebuilt-documentSearch |
| Audio | prebuilt-audioSearch |
| Video | prebuilt-videoSearch |
| Capability | Built-in | Document Intelligence | Content Understanding |
|---|---|---|---|
| Local text extraction | Yes | No | No |
| Scanned PDF OCR | No | Yes | Yes |
| Office conversion | Yes | Yes | Yes |
| Structured custom fields | No | Not exposed by this integration | Yes |
| Video | No | No | Yes |
| Custom analyzer | No | Not exposed by this integration | Yes |
| YAML field front matter | No | No | Yes |
| External cost | No for local-only paths | Yes | Yes |