Retrato de Santiago Fernández

CubeLabs

IA · Fintech · Agentes · Automatización
⚡ Práctico

MarkItDown: Convierte Cualquier Documento en Markdown para tus LLMs

PDF, Word, Excel, PowerPoint, imágenes, audio, webs y YouTube → Markdown estructurado y token-efficient

⏱ 40 min 📊 Principiante 📝 10 lecciones

MarkItDown es la utilidad de Microsoft —nacida en el equipo de AutoGen— que convierte casi cualquier archivo en Markdown listo para un LLM. No busca una conversión bonita para humanos: busca preservar la estructura (títulos, listas, tablas, enlaces) en un formato que los modelos hablan de forma nativa y que además es barato en tokens. Con ~185.000 estrellas en GitHub, licencia MIT y un servidor MCP oficial, se ha convertido en la pieza estándar del primer paso de cualquier pipeline RAG. En este curso lo llevas desde pip install hasta un pipeline completo de documentos → Markdown, pasando por la CLI, la API de Python, el servidor MCP para agentes, los plugins y la conversión cloud con Azure.

MarkItDown — Python tool for converting files and office documents to Markdown
MarkItDown · Python tool for converting files and office documents to Markdown — Microsoft (MIT)

Contenido

  1. Qué es MarkItDown y por qué Markdown es el idioma de los LLMs
  2. Instalación y dependencias opcionales
  3. La CLI en acción: flags, pipes y conversión en lote
  4. La API de Python: convierte sin escribir en disco
  5. PDF, Word, PowerPoint y Excel: qué estructura se preserva
  6. Imágenes y audio: EXIF, OCR y descripciones con LLM Vision
  7. Fuentes remotas: HTML, YouTube, EPUB, ZIP y data URIs
  8. markitdown-mcp: documentos para tus agentes
  9. Plugins: markitdown-ocr y cómo escribir el tuyo
  10. Azure y un pipeline RAG de principio a fin

1. Qué es MarkItDown y por qué Markdown es el idioma de los LLMs

MarkItDown es una librería de Python que convierte archivos y documentos de oficina a Markdown. Se compara a sí misma con textract, pero con una diferencia de enfoque: mientras textract extrae texto plano, MarkItDown intenta preservar la estructura del documento como Markdown —encabezados, listas, tablas, enlaces— porque ese texto no es para leerlo: es para que lo consuma una herramienta de análisis de texto.

El proyecto es explícito sobre su límite: la salida es razonablemente legible por humanos, pero no es la mejor opción para conversión de alta fidelidad para consumo humano. Eso es una decisión de diseño, no un defecto: optimiza para lo que un LLM necesita.

¿Por qué Markdown y no texto plano o HTML?

El razonamiento del proyecto es sencillo y vale la pena interiorizarlo:

Qué puede convertir

El soporte incluye formatos de oficina, multimedia y web:

EntradaQué extraeDependencia
PDFTexto y estructura (o vía Azure si es escaneado)[pdf]
PowerPointTítulo, texto, notas y descripción de imágenes con LLM[pptx]
WordEncabezados, listas, tablas y enlaces[docx]
ExcelHojas y tablas ([xls] para el formato antiguo)[xlsx]
ImágenesMetadatos EXIF y OCR; descripciones con LLM Visioncore + llm_client
AudioMetadatos EXIF y transcripción de voz[audio-transcription]
HTMLEncabezados, párrafos con énfasis, tablas y listascore
CSV, JSON, XMLFormatos basados en textocore
ZIPItera sobre el contenido del archivocore
YouTubeTranscripción del vídeo a partir de la URL[youtube-transcription]
EPubLibros electrónicoscore

Dos hechos de contexto que ayudan a situar el proyecto: nació dentro del ecosistema de AutoGen (lleva el badge Built by AutoGen Team) y sus topics oficiales en GitHub son autogen, langchain, openai, markdown, microsoft-office y pdf — es decir, está pensado para vivir dentro de aplicaciones y agentes, no para ser una app de escritorio.

💡 Tip

La documentación oficial es explícita: el repositorio existe para publicar librerías de Python que se incorporan a otros sistemas, no aplicaciones de usuario final. Si buscas un servidor REST, un frontend web o una app de escritorio para MarkItDown, no lo encontrarás aquí — la recomendación oficial es mantenerlo como un paquete o proyecto aparte que dependa de markitdown de PyPI.

2. Instalación y dependencias opcionales

MarkItDown requiere Python 3.10 o superior y se recomienda una máquina virtual para evitar conflictos de dependencias. El paquete se distribuye en PyPI como markitdown y su instalación completa es:

pip install 'markitdown[all]'

Alternativamente, desde el código fuente (útil si quieres contribuir o fijar un commit):

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

Preparar el entorno

1

Crea el entorno virtual

Con Python estándar: python -m venv .venv y luego source .venv/bin/activate. Con uv: uv venv --python=3.12 .venv (y recuerda usar uv pip install, no pip install, dentro de ese entorno). Con conda: conda create -n markitdown python=3.12.

2

Instala solo lo que vas a usar

El paquete base es ligero; los conversores pesados viven en extras. Por ejemplo, para PDF, Word y PowerPoint: pip install 'markitdown[pdf, docx, pptx]'.

3

Comprueba la versión

markitdown --version imprime el número de versión y sale. En la verificación de este curso la versión instalada y probada fue 0.1.7.

4

Primera conversión

markitdown ejemplo.pdf > ejemplo.md. Si funciona, ya tienes el flujo completo: un archivo entra, Markdown sale.

Catálogo de extras

ExtraActiva
[all]Todas las dependencias opcionales
[pptx]Archivos PowerPoint
[docx]Archivos Word
[xlsx]Archivos Excel
[xls]Excel en formato antiguo
[pdf]Archivos PDF
[outlook]Mensajes de Outlook
[az-doc-intel]Azure Document Intelligence
[az-content-understanding]Azure Content Understanding
[audio-transcription]Transcripción de audio wav y mp3
[youtube-transcription]Transcripción de vídeos de YouTube
⚠️ Error común

Instalar markitdown a secas y lanzarlo contra un .pdf o un .xlsx. El paquete base no incluye los conversores de oficina: sin los extras, esos formatos no se pueden procesar. Si un formato te falla, lo primero que hay que revisar es si su extra está instalado.

3. La CLI en acción: flags, pipes y conversión en lote

La interfaz de línea de comandos es la forma más directa de usar la herramienta. La forma canónica es escribir a stdout y redirigir:

markitdown ruta-al-archivo.pdf > documento.md

O usar -o para indicar el archivo de salida:

markitdown ruta-al-archivo.pdf -o documento.md

Y también acepta contenido por pipe:

cat ruta-al-archivo.pdf | markitdown

Salida real de una conversión

Esto es lo que devuelve MarkItDown al convertir un CSV, ejecutado de verdad durante la preparación de este curso:

$ markitdown ventas.csv
| producto | precio | stock |
| --- | --- | --- |
| Teclado mecanico | 89.90 | 12 |
| Monitor 27 pulgadas | 219.00 | 4 |
| SSD NVMe 1TB | 74.50 | 31 |

Fíjate en lo que ha ocurrido: el CSV se ha convertido en una tabla Markdown, no en texto separado por comas. Es exactamente el tipo de transformación que un LLM agradece.

Cuando el contenido llega por stdin sin nombre de archivo

Si no hay extensión que inspeccionar, se la das tú con pistas. Verificado en producción:

$ cat ventas.csv | markitdown -x csv
| producto | precio | stock |
| --- | --- | --- |
| Teclado mecanico | 89.90 | 12 |
| Monitor 27 pulgadas | 219.00 | 4 |
| SSD NVMe 1TB | 74.50 | 31 |

Referencia de flags

FlagPara qué sirve
-o, --outputArchivo de salida. Si no se indica, se escribe en stdout.
-x, --extensionPista de extensión del archivo (por ejemplo al leer desde stdin).
-m, --mime-typePista del tipo MIME del archivo.
-c, --charsetPista del juego de caracteres (por ejemplo UTF-8).
-p, --use-pluginsHabilita plugins de terceros durante la conversión.
--list-pluginsLista los plugins instalados y sale.
--keep-data-urisConserva los data URIs (imágenes en base64) en la salida.
-d, --use-docintelUsa Azure Document Intelligence en lugar de la conversión offline.
-e, --endpointEndpoint de Document Intelligence (por defecto MARKITDOWN_DOCINTEL_ENDPOINT).
--use-cuUsa Azure Content Understanding. Requiere --cu-endpoint.
--cu-analyzerID del analizador de Content Understanding (si no, autoselecciona por tipo).
--cu-file-typesTipos a enrutar a Content Understanding, separados por comas (por ejemplo pdf,jpeg,mp4).
-v, --versionMuestra la versión y sale.

-d/--use-docintel y --use-cu son mutuamente excluyentes: el parser los declara en un grupo exclusivo, así que no puedes pedir las dos nubes a la vez.

Convertir una carpeta entera

Para pipelines reales, el patrón habitual es un bucle que respete la estructura de directorios:

mkdir -p salida
for f in documentos/*.pdf; do
  base=$(basename "$f" .pdf)
  markitdown "$f" -o "salida/$base.md"
done
⚠️ Error común

Los data URIs —imágenes incrustadas en base64— se truncan por defecto en la salida. Si necesitas las imágenes embebidas (por ejemplo para reconstruir un documento), añade --keep-data-uris; de lo contrario un pipeline que cuente caracteres se comportará de forma distinta a lo que esperas.

💡 Tip

Antes de construir un pipeline complicado, comprueba los plugins disponibles con markitdown --list-plugins. La salida real del comando, con el paquete recién instalado, es: Installed MarkItDown 3rd-party Plugins seguido de No 3rd-party plugins installed y una sugerencia de buscar el hashtag #markitdown-plugin en GitHub.

4. La API de Python: convierte sin escribir en disco

El uso básico es de tres líneas. Se instancia MarkItDown y se llama a convert():

from markitdown import MarkItDown

md = MarkItDown(enable_plugins=False)  # True para habilitar plugins
result = md.convert("test.xlsx")
print(result.markdown)

El objeto de resultado

La conversión devuelve un DocumentConverterResult con dos campos relevantes:

El objeto también implementa __str__ devolviendo el Markdown, y existe text_content como alias en desuso: si lo ves en código antiguo, el código nuevo debe migrar a markdown.

Elige el método más estrecho posible

Este es el punto de seguridad más importante de todo el curso. MarkItDown hace E/S con los privilegios del proceso actual: igual que open() o requests.get(), accede a los recursos a los que el proceso tiene acceso. Por eso la API ofrece varios niveles de control:

MétodoQué aceptaCuándo usarlo
convert()Archivos locales, URIs remotas y streams de bytesIntencionadamente permisivo; cómodo en scripts de confianza
convert_local()Solo archivos localesCuando solo necesitas leer del disco
convert_response()El objeto de respuesta de requestsCuando quieres controlar tú la descarga de la URI
convert_stream()Un stream abiertoControl máximo sobre qué se lee y de dónde

Un ejemplo real de conversión desde un stream de bytes en memoria, verificado durante la preparación de este curso:

$ python3 - <<'EOF'
import io
from markitdown import MarkItDown

md = MarkItDown()
with open('ventas.csv', 'rb') as f:
    stream = io.BytesIO(f.read())

result = md.convert_stream(stream, stream_info=None)
print('tipo:', type(result).__name__)
print('title:', result.title)
print(result.markdown[:120])
EOF
tipo: DocumentConverterResult
title: None
| producto | precio | stock |
| --- | --- | --- |
| Teclado mecanico | 89.90 | 12 |
| Monitor 27 pulgadas | 219.00 | 4 |

Dos observaciones útiles de la salida: el tipo es DocumentConverterResult y el title es None porque un CSV no tiene título. Tu código debe estar preparado para que title venga vacío.

⚠️ Error común

No pases entrada no confiable a convert(). Si el input puede estar controlado por un usuario o un sistema externo —el caso típico de una aplicación alojada o del lado servidor— hay que validarlo y restringirlo antes: limitar rutas de archivo, restringir esquemas de URI y destinos de red, y bloquear el acceso a direcciones privadas, de loopback, link-local o de servicios de metadatos. La documentación de seguridad del proyecto lo dice sin rodeos: sanitiza las entradas en entornos no confiables y llama a la función convert_* más estrecha que necesites.

5. PDF, Word, PowerPoint y Excel: qué estructura se preserva

Los formatos de oficina son el caso de uso principal. Cada uno tiene su dependencia y sus particularidades:

FormatoExtraQué esperar
PDF[pdf]Extracción de texto y estructura offline. Los PDF escaneados no tienen capa de texto: necesitan OCR o Azure Document Intelligence.
Word (.docx)[docx]Encabezados, listas, tablas y enlaces preservados como Markdown.
PowerPoint (.pptx)[pptx]Contenido de las diapositivas; admite descripción de imágenes por LLM.
Excel (.xlsx)[xlsx]Una sección por hoja con sus tablas convertidas a tablas Markdown.
Outlook[outlook]Mensajes de correo.

Ejemplo real: de Excel a Markdown

Creamos un libro con una hoja llamada Q3 y dos filas de datos. Esto es exactamente lo que devuelve MarkItDown:

$ markitdown informe.xlsx
## Q3
| Region | Ingresos | Margen |
| --- | --- | --- |
| EMEA | 412000 | 0.31 |
| LATAM | 188500 | 0.27 |

El detalle importante: el nombre de la hoja se convierte en un encabezado de nivel 2 (## Q3). Eso significa que si concatenas varias hojas, un LLM puede atribuir cada tabla a su contexto sin ambigüedad. Los valores numéricos se mantienen tal cual, sin formateo añadido: el trabajo de interpretarlos es del modelo, no de la herramienta.

Ejemplo real: de HTML a Markdown

$ markitdown pagina.html
# Experimento 14

Medimos **latencia** con 3 modelos.

| Modelo | ms |
| --- | --- |
| A | 120 |
| B | 98 |

* Repetir 5 veces
* Anotar p95

Se ve la traducción directa: <h1> → #, <strong> → **, <table> → tabla Markdown, <ul><li> → lista con asteriscos.

💡 Tip

Como el objetivo es alimentar modelos, el nombre de la hoja o del fichero original es información valiosa que conviene conservar: al convertir en lote, guarda el Markdown en un archivo con el mismo nombre base y, si vas a concatenar, añade siempre el origen como encabezado. El contexto que no está en el texto, el LLM no lo puede adivinar.

⚠️ Error común

Confundir «no se ha extraído nada» con «el documento está vacío». En PDFs escaneados, en PDFs protegidos o en documentos con el texto como imagen, la conversión offline devuelve muy poco. Antes de culpar a la herramienta, comprueba si el PDF tiene capa de texto.

6. Imágenes y audio: EXIF, OCR y descripciones con LLM Vision

MarkItDown no se limita a documentos. En imágenes extrae los metadatos EXIF (y OCR cuando está disponible) y, opcionalmente, genera descripciones con un LLM. En audio hace lo simétrico: metadatos EXIF y transcripción de voz.

Descripciones de imagen con un LLM

Para usar un modelo en la descripción de imágenes —actualmente para pptx y archivos de imagen— se le pasan llm_client y llm_model:

from markitdown import MarkItDown
from openai import OpenAI

client = OpenAI(max_retries=5)
md = MarkItDown(
    llm_client=client,
    llm_model="gpt-4o",
    llm_prompt="opcional: un prompt personalizado",
)
result = md.convert("ejemplo.jpg")
print(result.markdown)

Tres detalles que marcan la diferencia en producción:

Ese último comportamiento es la razón de que un pipeline pueda seguir funcionando cuando la API de vision falla: hay una cadena de respaldo antes de rendirse.

Audio

NecesidadExtraNotas
Transcribir wav y mp3[audio-transcription]Voz a texto dentro del propio Markdown
Metadatos de audiocoreInformación EXIF del archivo
Audio de mayor calidad en la nube[az-content-understanding]Ver lección 10
Vídeo[az-content-understanding]Los conversores integrados no soportan vídeo
💡 Tip

Si tu corpus tiene imágenes dentro de diapositivas o documentos (diagramas, capturas, gráficos), el paso de llm_client es el que convierte una imagen muda en texto buscable. Es la diferencia entre un RAG que ignora los gráficos y uno que los puede citar.

⚠️ Error común

Olvidar que el vídeo no está cubierto por los conversores integrados. Si tu pipeline incluye archivos .mp4, la única vía de MarkItDown es Content Understanding de Azure: audio y vídeo con analizadores, y ninguna de las dos opciones locales los procesa.

7. Fuentes remotas: HTML, YouTube, EPUB, ZIP y data URIs

Además de archivos locales, MarkItDown trabaja con fuentes remotas y contenedores. Esto es lo que abre la puerta a pipelines que ingieren documentación web o vídeos sin escribir un scraper propio.

FuenteCómoNotas
HTMLURL o archivo localEncabezados, énfasis, tablas y listas a Markdown
YouTubeURL del vídeoRequiere [youtube-transcription]; obtiene la transcripción
ZIPArchivo comprimidoItera sobre el contenido y convierte los elementos que reconoce
EPubLibro electrónicoLibros completos a Markdown
CSV / JSON / XMLFormatos basados en textoSin dependencias extra
data URIsURI data:Truncados por defecto; --keep-data-uris para conservarlos

El ZIP es un sumidero de documentos

El soporte de ZIP merece un párrafo aparte: en vez de devolver un binario ilegible, recorre el contenido del archivo y convierte lo que encuentra dentro. Para un corpus que llega comprimido —un lote de facturas, un export de correo, una carpeta de documentación— eso significa que un solo convert() puede producir Markdown de N documentos.

Ejemplo: convertir una página web

from markitdown import MarkItDown

md = MarkItDown()
result = md.convert("https://ejemplo.com/documentacion")
print(result.markdown)

Y por la CLI, igual de simple: markitdown https://ejemplo.com/documentacion -o doc.md. Para vídeos: markitdown "https://www.youtube.com/watch?v=..." -o transcripcion.md, siempre con el extra [youtube-transcription] instalado.

⚠️ Error común

Aceptar URLs arbitrarias sin filtro cuando el proceso es un servicio. Un endpoint que pasa la URI del usuario directamente a convert() es, en la práctica, un proxy de red con los permisos del servidor: se puede usar para tocar direcciones internas o de metadatos cloud. Restringe esquemas y destinos, o descarga tú el contenido con requests.get() y pásalo a convert_response(): así el control de la red es tuyo.

💡 Tip

Convierte primero a Markdown y guarda el Markdown en disco antes de meterlo en un índice vectorial. El paso de conversión es la parte frágil y lenta de un pipeline RAG; hacerlo una sola vez y cachearlo te permite reindexar, cambiar de modelo de embeddings o re-chunkear sin volver a tocar los originales.

8. markitdown-mcp: documentos para tus agentes

El paquete markitdown-mcp expone la capacidad de conversión como un servidor MCP (Model Context Protocol) que un agente puede llamar. Es la forma de que tu asistente lea documentos en lugar de que tú le pegues el texto.

pip install markitdown-mcp

Expone una sola herramienta: convert_to_markdown(uri), donde la URI puede ser http:, https:, file: o data:.

Transportes

ModoComandoCuándo
STDIO (por defecto)markitdown-mcpCliente local que lanza el proceso
Streamable HTTP + SSEmarkitdown-mcp --http --host 127.0.0.1 --port 3001Alternativa cómoda al STDIO; queda escuchando en localhost

La ayuda real del comando instalado, ejecutada durante la preparación de este curso, es:

$ markitdown-mcp --help
usage: markitdown-mcp [-h] [--http] [--sse] [--host HOST] [--port PORT]

Run a MarkItDown MCP server

options:
  -h, --help   show this help message and exit
  --http       Run the server with Streamable HTTP and SSE transport rather
               than STDIO (default: False)
  --sse        (Deprecated) An alias for --http (default: False)
  --host HOST  Host to bind to (default: 127.0.0.1)
  --port PORT  Port to listen on (default: 3001)

El valor por defecto de --host es 127.0.0.1 y el de --port es 3001. Y atención: --sse está obsoleto, es un alias de --http.

Conectarlo a Claude Desktop

La recomendación oficial es usar la imagen Docker cuando el servidor se lanza desde Claude Desktop. En claude_desktop_config.json:

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

Y si además quieres dar acceso a una carpeta local, se monta el directorio:

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

Con el volumen montado, un archivo ejemplo.txt en /home/usuario/datos es accesible dentro del contenedor como /workdir/ejemplo.txt.

Ejecución en Docker

docker build -t markitdown-mcp:latest .
docker run -it --rm markitdown-mcp:latest
docker run -it --rm -v /home/usuario/datos:/workdir markitdown-mcp:latest

Depurarlo con MCP Inspector

npx @modelcontextprotocol/inspector

En el inspector (por defecto en http://localhost:5173/): elige STDIO y escribe markitdown-mcp como comando, o bien Streamable HTTP con http://127.0.0.1:3001/mcp, o SSE con http://127.0.0.1:3001/sse. Después: pestaña Tools → List Tools → convert_to_markdown → ejecútala sobre cualquier URI válida.

⚠️ Error común

Bindear el servidor a una interfaz no local. El servidor no soporta autenticación y corre con los privilegios del usuario que lo lanza: convert_to_markdown puede leer cualquier archivo al que ese usuario tenga acceso, o cualquier dato de la red. En modo SSE o HTTP se ata a localhost precisamente por eso. La documentación es tajante: no lo expongas a otras interfaces salvo que entiendas las implicaciones de seguridad; si necesitas más aislamiento, ejecútalo en una máquina virtual o contenedor con permisos limitados. El caso de uso previsto es uso local con agentes locales de confianza.

💡 Tip

El servidor MCP convierte a MarkItDown en una capacidad reutilizable por cualquier cliente compatible: Claude Desktop, un agente propio, un IDE. En vez de escribir código de conversión en cada proyecto, levantas un servidor y lo consumes como herramienta. Es la misma filosofía que el resto del ecosistema MCP: una herramienta, muchos consumidores.

9. Plugins: markitdown-ocr y cómo escribir el tuyo

MarkItDown admite plugins de terceros, y esto resuelve un dilema de diseño del proyecto: añadir un formato nuevo suele implicar una dependencia nueva, y el equipo es conservador con eso. La respuesta son los plugins: el soporte de un formato nuevo se puede publicar e instalar de forma independiente del repositorio principal.

Los plugins están deshabilitados por defecto. El ciclo de trabajo es:

# Ver qué hay instalado
markitdown --list-plugins

# Convertir con plugins habilitados
markitdown --use-plugins ruta-al-archivo.pdf

markitdown-ocr: OCR con LLM Vision, sin dependencias nuevas

El plugin de referencia añade soporte OCR a los conversores de PDF, DOCX, PPTX y XLSX, extrayendo el texto de las imágenes incrustadas mediante LLM Vision. El detalle elegante: reutiliza el mismo patrón llm_client / llm_model que MarkItDown ya usa para las descripciones de imágenes, así que no introduce librerías de ML ni dependencias binarias.

pip install markitdown-ocr
pip install openai  # o cualquier cliente compatible con OpenAI

Uso, pasando el mismo cliente y modelo que usarías para describir imágenes:

from markitdown import MarkItDown
from openai import OpenAI

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

Y un comportamiento que hay que conocer bien: si no se proporciona llm_client, el plugin se carga igual, pero el OCR se omite silenciosamente y se usa el conversor integrado estándar. Es decir, un pipeline mal configurado no falla: simplemente pierde texto sin avisar.

Escribir tu propio plugin

El punto de partida oficial es packages/markitdown-sample-plugin, dentro del propio repositorio. Los pasos:

1

Estudia el plugin de ejemplo

Clona el repositorio y abre packages/markitdown-sample-plugin: muestra la estructura mínima de un plugin funcional.

2

Publica tu paquete

El plugin es un paquete de Python independiente que se registra mediante el grupo de entry points markitdown.plugin — así lo descubre el CLI al listar plugins.

3

Etiqueta el repositorio

Añade el hashtag #markitdown-plugin a tu repositorio en GitHub para que otras personas puedan encontrarlo, y documenta el uso.

💡 Tip

Antes de abrir un pull request al repositorio principal para añadir un formato, piensa si tu caso encaja mejor como plugin. La política del proyecto es clara: las mejoras de fidelidad de los conversores existentes y los arreglos de bugs son bienvenidos, pero los formatos nuevos se añaden con cuentagotas, y en la mayoría de los casos la vía recomendada es publicarlo como plugin de terceros.

⚠️ Error común

Escribir aplicaciones, servidores web o frontends y ofrecerlos como contribución al repositorio: están explícitamente fuera de alcance. El proyecto no acepta servidores web, APIs REST o HTTP, servicios de conversión alojados, frontends de navegador ni aplicaciones de escritorio o móviles. Si construyes algo así, mantenlo como paquete o proyecto separado que dependa de markitdown de PyPI.

10. Azure y un pipeline RAG de principio a fin

La conversión offline cubre la mayoría de los casos, pero hay tres escenarios donde la nube gana: documentos escaneados o con tablas complejas, extracción de campos estructurados y tratamiento de audio y vídeo. MarkItDown ofrece dos integraciones de Azure.

Azure Document Intelligence

markitdown ruta.pdf -o documento.md -d -e "<document_intelligence_endpoint>"

El endpoint se puede fijar una sola vez en el entorno, de modo que después baste -d:

export MARKITDOWN_DOCINTEL_ENDPOINT="<document_intelligence_endpoint>"
markitdown ruta.pdf -o documento.md -d

Y en Python: md = MarkItDown(docintel_endpoint="<endpoint>"), seguido de md.convert("test.pdf").

Azure Content Understanding

Content Understanding va un paso más allá: extracción con campos estructurados en front matter YAML, soporte multimodal (documentos, imágenes, audio y vídeo) y analizadores configurables.

pip install 'markitdown[az-content-understanding]'

# CLI
markitdown ruta.pdf --use-cu --cu-endpoint "<content_understanding_endpoint>"

# o fijando el endpoint en el entorno
export MARKITDOWN_CU_ENDPOINT="<content_understanding_endpoint>"
markitdown ruta.pdf --use-cu

En Python, con configuración cero y selección automática de analizador por tipo de archivo:

from markitdown import MarkItDown

md = MarkItDown(cu_endpoint="<content_understanding_endpoint>")
result = md.convert("informe.pdf")    # documentos -> prebuilt-documentSearch
result = md.convert("reunion.mp4")    # vídeo      -> prebuilt-videoSearch
result = md.convert("llamada.wav")    # audio      -> prebuilt-audioSearch
print(result.markdown)

Con un analizador propio para extraer campos de dominio, la salida incluye el front matter:

md = MarkItDown(
    cu_endpoint="<content_understanding_endpoint>",
    cu_analyzer_id="mi-analizador-de-facturas",
)
result = md.convert("factura.pdf")
# ---
# contentType: document
# fields:
#   VendorName: CONTOSO LTD.
#   InvoiceDate: '2019-11-15'
# ---
# <!-- page 1 -->

Cuando se fija cu_analyzer_id, el conversor acota automáticamente el analizador a los tipos de archivo compatibles según su modalidad. Los tipos incompatibles —por ejemplo audio con un analizador de documentos— se enrutan a los analizadores predefinidos por defecto.

Comparativa de las tres vías

CapacidadConversores integradosAzure Document IntelligenceAzure Content Understanding
Conversión de documentosOffline, específica por formatoExtracción de maquetación en la nubeExtracción multimodal en la nube
Campos estructuradosNo disponibleNo expuestos por esta integraciónFront matter YAML desde los campos del analizador
Analizadores propiosNo disponibleNo configurable en esta integraciónSoportado con cu_analyzer_id
Audio y vídeoAudio básico, sin vídeoNo soportadoAnalizadores de audio y vídeo
CosteSolo cómputo localLlamadas facturables a AzureLlamadas facturables a Azure

Para controlar el gasto, cu_file_types restringe qué formatos se enrutan a Content Understanding:

from markitdown.converters import ContentUnderstandingFileType

md = MarkItDown(
    cu_endpoint="<content_understanding_endpoint>",
    cu_file_types=[ContentUnderstandingFileType.PDF],  # solo los PDF usan CU
)
⚠️ Error común

Cada llamada a convert() sobre un formato enrutado a Content Understanding es una llamada facturable a la API de Azure. En un script que recorre un corpus grande, el coste se dispara sin que aparezca ningún error. Decide de antemano qué tipos de archivo merecen la nube y fíjalos con cu_file_types.

Pipeline completo: de una carpeta de documentos a contexto para el LLM

1

Convierte todo a Markdown y cachea

Recorre el corpus y guarda el Markdown en disco. Es el paso caro: hazlo una vez.

2

Trocea y etiqueta el origen

Parte por encabezados Markdown (los # y ## son fronteras naturales) y añade metadatos: ruta original, título y fecha.

3

Indexa y consulta

Genera embeddings y guarda los fragmentos en tu almacén vectorial. Cuando el agente recupere un fragmento, citará el documento original gracias a los metadatos del paso 2.

from pathlib import Path
from markitdown import MarkItDown

md = MarkItDown()
entrada, salida = Path("documentos"), Path("markdown")
salida.mkdir(exist_ok=True)

for f in entrada.rglob("*"):
    if f.is_dir():
        continue
    try:
        result = md.convert_local(str(f))
        destino = salida / (f.stem + ".md")
        # conservamos el origen como encabezado: contexto que el LLM no puede adivinar
        destino.write_text(f"# {f.name}\n\n{result.markdown}", encoding="utf-8")
        print("ok  ", f.name)
    except Exception as e:
        print("fallo", f.name, type(e).__name__)

Y la variante en contenedor, útil para pipelines reproducibles y sin ensuciar el entorno del host:

docker build -t markitdown:latest .
docker run --rm -i markitdown:latest < ~/tu-archivo.pdf > salida.md
💡 Tip

Registra todos los fallos de conversión en vez de silenciarlos. Un pipeline RAG que descarta documentos sin avisar produce respuestas incompletas que parecen un problema del modelo, cuando en realidad faltaba información en el índice. Un contador de «convertidos / fallidos» por carpeta es la mejor inversión de diez líneas de código.

Preguntas frecuentes

¿MarkItDown sustituye a un conversor de alta fidelidad?

No, y el proyecto lo dice explícitamente: su salida es razonablemente legible, pero no es la mejor opción para conversión de alta fidelidad para consumo humano. Está diseñado para que el texto lo consuman herramientas de análisis de texto y LLMs, preservando estructura (títulos, listas, tablas, enlaces) y siendo eficiente en tokens. Si necesitas reproducir la maquetación exacta de un documento, esta no es la herramienta.

¿Necesito Azure o alguna API de pago para usarlo?

No. Toda la conversión offline —PDF, Office, HTML, CSV/JSON/XML, ZIP, EPub— funciona en local. Azure Document Intelligence y Content Understanding son opcionales y solo hacen falta para casos concretos: PDFs escaneados, campos estructurados, audio y vídeo. Las únicas piezas que requieren un modelo externo son las descripciones de imagen y el plugin de OCR, y ahí puedes usar cualquier cliente compatible con OpenAI.

¿Qué pasa con los PDFs escaneados?

La conversión offline necesita una capa de texto, así que un PDF escaneado devuelve muy poco o nada. Las salidas son OCR con un modelo de visión (por ejemplo el plugin markitdown-ocr con llm_client) o el análisis de maquetación en la nube de Azure, que es la vía diseñada para documentos escaneados, tablas complejas y documentos de varias páginas.

¿Puedo dárselo a mi agente como herramienta?

Sí, es justo lo que hace markitdown-mcp: un servidor MCP con una sola herramienta, convert_to_markdown(uri), que acepta URIs http:, https:, file: y data:. Se lanza por STDIO (por defecto) o por Streamable HTTP en 127.0.0.1:3001. Recuerda que no tiene autenticación y puede leer cualquier archivo accesible por su usuario: úsalo en local con agentes de confianza y nunca lo expongas a otras interfaces sin entender las implicaciones.