Convertir PDF a Markdown en Python
Un tutorial paso a paso usando la API REST: consigue una clave, crea un trabajo, consulta y descarga Markdown limpio, con un ejemplo completo y copiable y manejo de errores como es debido.
Una clave, tres llamadas
Para convertir un PDF a Markdown en Python llamas a una pequeña API REST con una clave bearer: POST /api/v2/jobs para crear un trabajo, GET /api/v2/jobs/{id} para consultar hasta que esté listo, y luego GET /api/v2/jobs/{id}/download para el Markdown. No hay nada que alojar ni una librería pesada que instalar, solo requests. El mismo ciclo de vida se expone como un MCP alojado para agentes, y la salida es el mismo Markdown que producen la extensión y la app web, así puedes prototipar en el navegador y luego automatizar con confianza.
Configúralo en cuatro pasos
Consigue una clave de API
Inicia sesión con una cuenta de Google gratuita y crea una clave de API en tu cuenta; se muestra una sola vez. Envíala como Authorization: Bearer p2m_your_key.
Crea un trabajo
Haz POST de la URL del PDF (o los bytes subidos) a /api/v2/jobs. Obtienes un id de trabajo y un estado.
Consulta hasta ready
Haz GET /api/v2/jobs/{id} hasta que status sea ready o error.
Descarga el Markdown
Haz GET /api/v2/jobs/{id}/download para el texto Markdown. Respeta truncated y pages.
El script de Python completo
Librería estándar más requests. Crea desde una URL de PDF, consulta y luego guarda el Markdown.
# pip install requests import requests, time API = "https://pdf2md.dev/api/v2" H = {"Authorization": "Bearer p2m_your_key"} # 1) crear un trabajo desde una URL de PDF r = requests.post(f"{API}/jobs", headers={**H, "Idempotency-Key": "report-2026-01"}, json={"url": "https://example.com/report.pdf"}) r.raise_for_status() jid = r.json()["job_id"] # 2) consultar hasta ready o error while True: job = requests.get(f"{API}/jobs/{jid}", headers=H).json() if job["status"] in ("ready", "error"): break time.sleep(3) if job["status"] == "error": raise SystemExit(f"conversion failed: {job.get('error_code')} {job.get('error_message')}") # 3) descargar el Markdown md = requests.get(f"{API}/jobs/{jid}/download", headers=H).text if job.get("truncated"): print("nota: resultado parcial (alcanzó el límite de tiempo)") open("report.md", "w").write(md)
¿Subir un archivo local en vez de una URL? Haz POST de los bytes como multipart/form-data con un campo file al mismo endpoint. Las formas completas de la petición y la respuesta están en la especificación OpenAPI.
Para convertir muchos PDF, crea un trabajo por archivo y consúltalos en paralelo hasta tu límite de espacios (3 en el plan gratuito, más en los de pago). Mantén un Idempotency-Key distinto por archivo para que los reintentos nunca dupliquen trabajo, y espera ante un 429 usando la cabecera Retry-After.
Errores, reintentos y webhooks
Lee el código de error
Un trabajo fallido devuelve status: error con un error_code legible por máquina (processing_timeout o conversion_failed) y un error_message seguro: ramifica según el código, no el texto.
Maneja truncated
Un documento largo puede volver ready con truncated=true. Comprueba la marca y divide el archivo o usa un presupuesto de pago mayor.
Idempotency-Key
Envía una cabecera Idempotency-Key para que un create reintentado devuelva el mismo trabajo en vez de duplicar trabajo.
Webhooks en vez de consulta
En planes de pago, registra un webhook o pasa callback_url y recibe un POST en ready/error en vez de consultar.
Respeta el 429
Ante un 429, espera los segundos de Retry-After antes de reintentar; no satures la cola.
Guarda las claves en el servidor
La clave es un secreto: guárdala en el servidor, envíala por TLS, y rótala o revócala cuando quieras.
¿No usas Python? Las mismas tres llamadas
Es una API HTTPS simple, así que cualquier lenguaje funciona. El mismo crear / consultar / descargar en Node:
// Node 18+ (fetch global) const API = "https://pdf2md.dev/api/v2"; const H = { Authorization: "Bearer p2m_your_key" }; let r = await fetch(`${API}/jobs`, { method: "POST", headers: { ...H, "Content-Type": "application/json" }, body: JSON.stringify({ url: "https://example.com/report.pdf" }) }); let { job_id } = await r.json(); let job; do { await new Promise(s => setTimeout(s, 3000)); job = await (await fetch(`${API}/jobs/${job_id}`, { headers: H })).json(); } while (!["ready", "error"].includes(job.status)); const md = await (await fetch(`${API}/jobs/${job_id}/download`, { headers: H })).text();
¿Prefieres herramientas de agente o RAG?
El mismo ciclo de vida es un MCP alojado para ChatGPT, Claude y frameworks de agentes. Para ingesta y chunking, consulta la guía de RAG.
Preguntas habituales
¿Cómo convierto un PDF a Markdown en Python?
Llama a la API REST con una clave bearer: haz POST del PDF a /api/v2/jobs, consulta GET /api/v2/jobs/{id} hasta ready, y luego haz GET de /download para el Markdown. El ejemplo completo con requests está arriba.
¿Necesito una clave de API?
Sí para la API. Una cuenta de Google gratuita te permite crear una clave y usar el MCP alojado. La extensión del navegador y la app web siguen siendo anónimas y no necesitan clave.
¿Cómo manejo los errores y tiempos de espera?
Un trabajo fallido devuelve status: error con un error_code legible por máquina (processing_timeout o conversion_failed) y un error_message seguro. Un documento largo puede volver ready con truncated=true; comprueba esa marca.
¿Puedo evitar la consulta?
En planes de pago, registra un webhook o pasa callback_url al crear el trabajo, y el servicio te hace POST en ready o error para que no consultes.
¿Funciona desde Node u otros lenguajes?
Sí. Es una API HTTPS simple, así que cualquier lenguaje funciona. Arriba hay un ejemplo corto en Node, y el contrato completo está en la especificación OpenAPI.
¿Cómo convierto un archivo local en vez de una URL?
Haz POST de los bytes del archivo como multipart/form-data con un campo file a /api/v2/jobs, en vez de un cuerpo JSON con url. Los pasos de consultar y descargar son idénticos.
¿Puedo convertir muchos PDF a la vez?
Crea un trabajo por archivo y consúltalos en paralelo hasta tu límite de espacios (3 en el plan gratuito, más en los de pago). Los planes de pago añaden un endpoint de creación por lotes y webhooks para que no consultes nada.
¿La salida es la misma que la de la extensión y la app web?
Sí. Cada superficie usa el mismo motor de conversión, así que la API devuelve el mismo Markdown que obtendrías en el navegador.
¿Es gratis?
El plan gratuito ofrece 3 espacios, archivos de 10 MB, un presupuesto de tiempo de 15 minutos y retención de 1 hora. Los planes de pago amplían cada límite y añaden webhooks y creación por lotes.