PDF to Markdown
Servidor MCP alojado que convierte PDFs en Markdown limpio y listo para LLM, con tablas, fórmulas (LaTeX) y OCR. Motores propios (MinerU + Docling), no un envoltorio de LLM.
Documentación
Dos superficies, un mismo motor
Elige la integración que mejor se adapte. Ambas utilizan el mismo motor de conversión y respetan las mismas ranuras, límites y retención.
API REST
Endpoints HTTPS con una clave de API tipo bearer. DTOs estables, errores predecibles, creación idempotente.
MCP alojado
Un endpoint de Model Context Protocol gestionado que expone la conversión como herramientas de agente: una capa delgada sobre la misma API.
Acciones de GPT personalizadas
Importa una especificación OpenAPI reducida en un ChatGPT Custom GPT para que pueda convertir PDFs como una herramienta integrada.
Claves de API bearer sobre HTTPS
La API y MCP utilizan claves de API bearer, distintas de la ruta firmada por dispositivo que usa la extensión de Chrome. Se requiere una cuenta gratuita de Google para generar claves.
Obtener una clave
- Inicia sesión con Google (cuenta gratuita).
- Genera una clave de API en tu cuenta; se muestra una sola vez.
- Envíala como
Authorization: Bearer p2m_…en cada solicitud. - Las claves son secretos: guárdalas en el servidor, rota y revoca en cualquier momento.
Valores predeterminados honestos
Claves, no contraseñas. La extensión permanece anónima y firmada por dispositivo; las claves de API/MCP son una credencial separada vinculada a la cuenta.
Solo HTTPS. Envía siempre las claves a través de TLS; nunca incrustes una clave en código de cliente distribuido a los usuarios.
Creación idempotente. Un Idempotency-Key opcional en la creación te permite reintentar de forma segura sin trabajos duplicados.
Ámbitos. Cada clave de API lleva ámbitos: jobs:create, jobs:read, jobs:download, jobs:delete (los predeterminados), además de settings:read / settings:write. Emite claves con privilegios mínimos; tanto la API REST como las herramientas MCP aplican los ámbitos de la clave.
Crear un trabajo, esperar, obtener Markdown, limpiar la ranura
Un ciclo de vida predecible, dos formas de manejarlo: llama a la API REST desde tu propio código o usa las herramientas equivalentes de MCP alojado. Nunca reclames un resultado antes de status=ready.
API REST MCP alojado
1
Crear el trabajo
Envía por POST una URL de PDF o sube bytes. Obtén un id de trabajo y una ranura. Idempotency-Key se respeta pero es opcional.
POST /api/v2/jobsmcp · pdf_to_markdown_create_job_from_url
2
Comprobar el estado
Consulta el trabajo hasta que esté en ready o error, o registra un webhook firmado en los planes de pago en lugar de consultar.
GET /api/v2/jobs/{id}mcp · pdf_to_markdown_get_job
3
Obtener el Markdown
Descarga el resultado una vez que esté listo. Lee truncated y pages para saber si un documento largo se devolvió parcialmente.
GET /api/v2/jobs/{id}/downloadmcp · pdf_to_markdown_get_markdown
4
Eliminar / limpiar la ranura
Libera una ranura cuando termines. Eliminar trabajos en cola o en proceso es destructivo: confírmalo en los clientes orientados al usuario.
DELETE /api/v2/jobs/{id}mcp · pdf_to_markdown_delete_job
# 1. create a job from a PDF URL
curl -X POST https://pdf2md.dev/api/v2/jobs \
-H "Authorization: Bearer p2m_…" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/report.pdf"}'
# → { "job_id": "job_9f3c…", "status": "queued" }
# 2. poll status
curl https://pdf2md.dev/api/v2/jobs/job_9f3c… \
-H "Authorization: Bearer p2m_…"
# → { "status": "ready", "pages": 24, "truncated": false }
# 3. fetch the Markdown
curl https://pdf2md.dev/api/v2/jobs/job_9f3c…/download \
-H "Authorization: Bearer p2m_…"
# 4. free the slot
curl -X DELETE https://pdf2md.dev/api/v2/jobs/job_9f3c… \
-H "Authorization: Bearer p2m_…"
Errores. Las respuestas usan formas estables y códigos HTTP predecibles (400 entrada incorrecta, 401 autenticación, 404 trabajo desconocido, 409 sin ranura libre / slots_full, 413 demasiado grande, 429 límite de velocidad superado). El esquema completo está en la especificación OpenAPI.
Más endpoints de trabajos
Crear desde archivo (multipart) POST /api/v2/jobs
Listar tus trabajos GET /api/v2/jobs
Creación por lotes (de pago) POST /api/v2/jobs/batch
La creación acepta un url JSON o un file multipart, además de file_name, external_id, tags y callback_url / callback_secret opcionales para un webhook por trabajo. La creación por lotes es de todo o nada y debe caber en tus ranuras libres.
El objeto de trabajo
job_id cadena
status queued · processing · ready · error
pages · output_size entero
truncated booleano
error_code · error_message motivo (cuando hay error)
download_url cadena (cuando está listo)
external_id · tags tus metadatos
slot_usage · tier contexto de cuota
Cuenta y uso. Consulta tu nivel, límites y uso en tiempo de ejecución con GET /api/v2/me, /api/v2/limits y /api/v2/usage; gestiona claves en /api/v2/api-keys y webhooks en /api/v2/webhooks.
Convertir un PDF a Markdown en Python
Las mismas cuatro llamadas desde cualquier lenguaje. Aquí se muestra con requests. Para un tutorial completo paso a paso con manejo de errores, consulta el tutorial de Python.
# pip install requests
import time, requests
API = "https://pdf2md.dev/api/v2"
H = {"Authorization": "Bearer p2m_…"}
# 1. create a job from a PDF URL (or post a file with files={"file": ...})
job = requests.post(f"{API}/jobs", headers=H,
json={"url": "https://example.com/report.pdf"}).json()
jid = job["job_id"]
# 2. poll until ready (or register a webhook instead)
while True:
j = requests.get(f"{API}/jobs/{jid}", headers=H).json()
if j["status"] in ("ready", "error"):
break
time.sleep(3)
# 3. download the Markdown
md = requests.get(f"{API}/jobs/{jid}/download", headers=H).text
print(md)
Más recetas: subida de archivos y Node
Crear desde un archivo local (curl)
# multipart upload of a local PDF
curl -X POST https://pdf2md.dev/api/v2/jobs \
-H "Authorization: Bearer p2m_…" \
-F "[email protected]" \
-F "file_name=document.pdf"
Node 18+ (fetch global)
// create from URL, poll, download
const API = "https://pdf2md.dev/api/v2";
const H = { Authorization: "Bearer p2m_…" };
let job = await (await fetch(\`${API}/jobs\`, {
method: "POST",
headers: { ...H, "Content-Type": "application/json" },
body: JSON.stringify({ url: "https://example.com/report.pdf" })
})).json();
while (job.status === "queued" || job.status === "processing") {
await new Promise(s => setTimeout(s, 2000));
job = await (await fetch(\`${API}/jobs/${job.job_id}\`, { headers: H })).json();
}
if (job.status === "ready") {
const md = await (await fetch(\`${API}/jobs/${job.job_id}/download\`, { headers: H })).text();
console.log(md);
}
La verificación de firma de webhooks está en la sección Webhooks; la configuración del cliente MCP está en la sección MCP. Esquema completo: OpenAPI.
Conversión como herramientas de agente
Conecta un agente compatible a nuestro endpoint MCP gestionado. Las herramientas son una capa delgada sobre la API REST, por lo que cada llamada respeta las mismas ranuras, límites y retención.
1
Apuntar el agente al endpoint
JSON-RPC 2.0 sobre Streamable HTTP con tu clave de API como token bearer. No hay servidor local que ejecutar. Métodos: initialize, tools/list, tools/call, ping.
POST https://pdf2md.dev/api/v2/mcp
2
Llamar a las herramientas
El mismo ciclo de vida más límites, expuesto como siete herramientas. Cada una respeta los ámbitos de la clave; las respuestas de tools/call incluyen slot_usage y tier.
create_job_from_url · create_job_from_upload (jobs:create) list_jobs · get_job (jobs:read) get_markdown (jobs:download) · delete_job (jobs:delete) get_limits
3
Respetar las reglas
Espera a ready antes de usar la salida; confirma antes de eliminar trabajos en cola/en proceso; maneja truncated y 429 Retry-After. (Los nombres de las herramientas llevan el prefijo pdf_to_markdown_.)
// MCP client config (hosted, no local process)
{
"mcpServers": {
"pdf2md": {
"url": "https://pdf2md.dev/api/v2/mcp",
"headers": {
"Authorization": "Bearer p2m_…"
}
}
}
}
Importar la especificación, obtener una herramienta integrada
Publicamos dos especificaciones: la OpenAPI completa para desarrolladores y una especificación de acción reducida con el subconjunto seguro y mínimo para clientes de IA y ChatGPT Custom GPT Actions.
OpenAPI completa
El contrato completo: cada endpoint, parámetro, DTO y error. Genera clientes o explórala en tus herramientas.
Abrir la especificación completa
Especificación reducida para Custom GPT
Un subconjunto mínimo de acciones (crear, estado, obtener) para ChatGPT Custom GPT Actions. Importa la URL, configura tu clave de API como autenticación y tu GPT convertirá PDFs de forma nativa.
Abrir la especificación reducida
La especificación reducida es una conveniencia para clientes de IA, no un límite de seguridad: se aplican la misma autenticación, ámbitos y límites que en la API completa.
Límites por nivel, aplicados por igual a API y MCP
Los límites provienen de tu nivel y se aplican de forma idéntica en todas las superficies. Los valores en vivo están en la página de precios.
Nivel gratuito (con cuenta)
Ranuras activas (profundidad de cola) 3
Tamaño máximo de PDF 10 MB
Presupuesto de tiempo por documento 15 min
Retención de resultados listos 1 hora
Los niveles de pago aumentan ranuras, tamaño de archivo, presupuesto de tiempo, retención y límites de velocidad, y añaden webhooks y creación por lotes. Comparar planes →
Límites de velocidad y contrapresión
Límites de velocidad por nivel. Las solicitudes están limitadas por clave; si las superas, obtienes 429 con una cabecera Retry-After: retrocede y reintenta.
Presión de ranuras. Si todas las ranuras están ocupadas, la creación devuelve 409. Libera una ranura con delete o espera a que un trabajo termine.
Prioridad en pago. Los trabajos de pago se ejecutan con mayor prioridad de cola en un grupo de conversión de pago dedicado, por lo que no esperan detrás del backlog gratuito.
Recibe notificaciones en lugar de consultar
En los niveles de pago, registra un webhook firmado (o pasa un callback_url por trabajo) y te enviamos un POST en cada evento terminal notable: job.ready, job.error, job.truncated y job.deleted. El evento es una notificación, no una entrega: no contiene contenido del documento, así que obtén el Markdown a través de la API después de recibirlo.
1
2
Recibir el evento
Enviamos JSON con las cabeceras X-P2M-Event, X-P2M-Timestamp, X-P2M-Delivery y X-P2M-Signature.
3
Verificar y luego actuar
Recalcula la firma, confirma con 2xx y sé idempotente (las entregas pueden reintentarse con retroceso). Luego descarga el Markdown.
# delivery → your endpoint
X-P2M-Event: job.ready
X-P2M-Timestamp: 1718900000
X-P2M-Signature: sha256=9a8b7c…
{
"event": "job.ready",
"job": {
"job_id": "job_9f3c…",
"status": "ready",
"pages": 24, "truncated": false,
"download_url": "/api/v2/jobs/job_9f3c…/download"
}
}
# verify (Python): signature = sha256= + hex(HMAC(secret, "ts.rawbody"))
import hmac, hashlib
def verify(secret, ts, raw_body, sig):
expected = "sha256=" + hmac.new(
secret.encode(), f"{ts}.".encode() + raw_body,
hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, sig)
Instrucciones portables para agentes
Incorpora estas reglas en el prompt del sistema de un agente para que maneje las herramientas correctamente y nunca invente resultados.
Esperar a ready
Nunca reclames ni resumas un resultado antes de status=ready. Mientras esté en queued o processing, sigue consultando o espera el webhook.
Confirmar eliminaciones
Eliminar un trabajo en queued o processing es destructivo. Pregunta al usuario antes de llamar a pdf_to_markdown_delete_job en un trabajo no finalizado.
Manejar la truncación
Si truncated=true, informa al usuario de que el documento se devolvió parcialmente hasta el presupuesto de tiempo del nivel y ofrece un nivel superior o dividir el archivo.
Respetar 429
En 429, espera Retry-After segundos antes de reintentar. No satures la cola.
Limpiar ranuras
Elimina los trabajos finalizados que ya no necesites para no agotar tus ranuras.
Leer el descubrimiento
Empieza por /llms.txt y la especificación OpenAPI en lugar de adivinar endpoints a partir del texto.