API REST
Endpoints HTTPS con una clave de API bearer. DTOs estables, errores predecibles, creación idempotente.
Ver el ciclo de vidaUn ciclo de vida del trabajo predecible sobre una API REST y un MCP alojado equivalente: crea un trabajo, espera a ready, obtén el Markdown, libera el espacio. La API y el MCP nunca saltan los límites del producto.
Elige la integración que encaje. Ambas llaman al mismo motor de conversión y obedecen los mismos espacios, límites y retención.
Endpoints HTTPS con una clave de API bearer. DTOs estables, errores predecibles, creación idempotente.
Ver el ciclo de vidaUn endpoint gestionado de Model Context Protocol que expone la conversión como herramientas de agente: una capa fina sobre la misma API.
Conectar MCPImporta un spec OpenAPI reducido en un Custom GPT de ChatGPT para que pueda convertir PDF como herramienta integrada.
Configurar la actionLa API y el MCP usan claves de API bearer, distintas de la vía firmada por dispositivo que usa la extensión de Chrome. Se requiere una cuenta gratuita de Google para generar claves.
Authorization: Bearer p2m_… en cada solicitud.Idempotency-Key opcional al crear te permite reintentar de forma segura sin trabajos duplicados.Scopes. Cada clave de API lleva scopes: jobs:create, jobs:read, jobs:download, jobs:delete (los predeterminados), más settings:read / settings:write. Acuña claves de mínimo privilegio; tanto la API REST como las herramientas MCP aplican los scopes de la clave.
Un ciclo de vida predecible, dos formas de manejarlo: llama a la API REST desde tu propio código, o usa las herramientas equivalentes del MCP alojado. Nunca reclames un resultado antes de status=ready.
Haz POST de una URL de PDF o sube bytes. Recibes un id de trabajo y un espacio. Idempotency-Key se respeta pero es opcional.
Consulta el trabajo hasta ready o error, o registra un webhook firmado en los planes de pago en vez de consultar.
Descarga el resultado una vez listo. Lee truncated y pages para saber si un documento largo se devolvió en parte.
Libera un espacio cuando termines. Eliminar trabajos en cola o en proceso es destructivo: confírmalo en los clientes de cara al usuario.
# 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 auth, 404 trabajo desconocido, 409 sin espacio libre / slots_full, 413 demasiado grande, 429 límite de tasa). El esquema completo está en el spec OpenAPI.
Create acepta un url JSON o un file multipart, más file_name, external_id, tags opcionales y callback_url / callback_secret para un webhook por trabajo. La creación por lotes es todo-o-nada y debe caber en tus espacios libres.
Cuenta y uso. Consulta tu plan, 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.
Las mismas cuatro llamadas desde cualquier lenguaje. Aquí con requests. Para un recorrido 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)
# 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"
// 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.
Conecta un agente compatible a nuestro endpoint MCP gestionado. Las herramientas son una capa fina sobre la API REST, así que cada llamada obedece los mismos espacios, límites y retención.
JSON-RPC 2.0 sobre Streamable HTTP con tu clave de API como token bearer. Sin servidor local que ejecutar. Métodos: initialize, tools/list, tools/call, ping.
El mismo ciclo de vida más los límites, expuesto como siete herramientas. Cada una respeta los scopes de la clave; las respuestas de tools/call incluyen slot_usage y tier.
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 herramienta 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_…"
}
}
}
}
Publicamos dos specs: el OpenAPI completo para desarrolladores, y un spec de action reducido con el subconjunto seguro y mínimo para clientes de IA y Custom GPT Actions de ChatGPT.
El contrato completo: cada endpoint, parámetro, DTO y error. Genera clientes o explóralo en tus herramientas.
Un subconjunto mínimo de action (crear, estado, obtener) para Custom GPT Actions de ChatGPT. Importa la URL, pon tu clave de API como auth, y tu GPT convierte PDF de forma nativa.
El spec reducido es una comodidad para clientes de IA, no una frontera de seguridad: aplican la misma auth, scopes y límites que en la API completa.
Los límites vienen de tu plan y se aplican de forma idéntica en cada superficie. Los valores en vivo están en la página de precios.
Los planes de pago amplían espacios, tamaño de archivo, presupuesto de tiempo, retención y límites de tasa, y añaden webhooks y creación por lotes. Comparar planes →
429 con una cabecera Retry-After: espera y reintenta.409. Libera un espacio con delete, o espera a que un trabajo termine.En los planes de pago, registra un webhook firmado (o pasa un callback_url por trabajo) y te hacemos 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 lleva contenido del documento, así que obtén el Markdown por la API después de recibirlo.
Haz POST de una URL HTTPS (con protección SSRF) y un filtro events opcional. El secreto de firma whsec_… se devuelve una sola vez. O pon callback_url + callback_secret en un único trabajo.
Te hacemos POST de JSON con las cabeceras X-P2M-Event, X-P2M-Timestamp, X-P2M-Delivery y X-P2M-Signature.
Recalcula la firma, confirma con 2xx, y sé idempotente (las entregas pueden reintentar con backoff). 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)
Pon estas reglas en el system prompt de un agente para que maneje las herramientas correctamente y nunca invente resultados.
Nunca reclames ni resumas un resultado antes de status=ready. Mientras esté queued o processing, sigue consultando o espera el webhook.
Eliminar un trabajo queued o processing es destructivo. Pregunta al usuario antes de llamar a pdf_to_markdown_delete_job en un trabajo sin terminar.
Si truncated=true, dile al usuario que el documento se devolvió en parte hasta el presupuesto de tiempo del plan, y ofrece un plan superior o dividir el archivo.
Ante un 429, espera los segundos de Retry-After antes de reintentar. No martillees la cola.
Elimina los trabajos terminados que ya no necesites para no agotar tus espacios.
Empieza por /llms.txt y el spec OpenAPI en vez de adivinar endpoints a partir de la prosa.
Todo lo que un agente necesita para integrarse sin leer código fuente: un archivo compacto de capacidades, un archivo de contexto detallado y el spec OpenAPI.