PDF 转 Markdown API

只有一个接口。发送 PDF,返回结构化的 Markdown。表格、标题、列表全部保留, 中文扫描件同样适用。无需注册即可试用。

需要 Bash、curl 和 Python 3。脚本验证成功响应后才输出 Markdown。

#!/usr/bin/env bash
set -euo pipefail
key=demo_public_key
identity=demo-example
work=$(mktemp -d)
trap 'rm -rf "$work"' EXIT
result=$work/result
status=$(curl --silent --show-error --max-time 660 -o "$result" -w '%{http_code}' \
  "https://pdftomarkdown.dev/v1/convert" \
  -H "Authorization: Bearer $key" -H "Idempotency-Key: $identity" \
  -H "Content-Type: application/json" \
  --data '{"input":{"pdf_url":"https://pdftomarkdown.dev/samples/invoice.pdf"}}')
if [ "$status" != 200 ]; then echo "HTTP $status; keep the identity for recovery" >&2; exit 1; fi
python3 - "$result" <<'PY'
import json, sys
with open(sys.argv[1]) as response:
    result = json.load(response)
if not (isinstance(result, dict) and result.get("complete") is True
        and isinstance(result.get("markdown"), str)
        and type(result.get("pages")) is int and result["pages"] >= 0
        and isinstance(result.get("request_id"), str) and result["request_id"].strip()):
    sys.exit("Incomplete or invalid conversion response")
sys.stdout.write(result["markdown"])
PY
{
  "complete": true,
  "markdown": "# CONTOSO LTD.\n\n# INVOICE\n\nContoso Headquarters\n\n123 456th St\n\nNew York, NY, 10001\n\nINVOICE: INV-100\n\nDATE: 11/15/2019\n\nDUE DATE: 12/15/2019\n\nCUSTOMER NAME: MICROSOFT CORPORATION\n\nCUSTOMER ID: CID-12345\n\nMicrosoft Corp\n\n123 Other St,\n\nRedmond WA, 98052\n\nBILL TO:\n\nMicrosoft Finance\n\n123 Bill St,\n\nRedmond WA, 98052\n\nSHIP TO:\n\nMicrosoft Delivery\n\n123 Ship St,\n\nRedmond WA, 98052\n\nSERVICE ADDRESS:\n\nMicrosoft Services\n\n123 Service St,\n\nRedmond WA, 98052\n\n| SALESPERSON | P.O. NUMBER | REQUISITIONER | SHIPPED VIA | F.O.B. POINT | TERMS |\n| --- | --- | --- | --- | --- | --- |\n|  | PO-3333 |  |  |  |  |\n\n<table><tr><th>QUANTITY</th><th>DESCRIPTION</th><th>UNIT PRICE</th><th>TOTAL</th></tr><tr><td>1</td><td>Test for 23 fields</td><td>1</td><td>$100.00</td></tr><tr><td></td><td></td><td></td><td></td></tr><tr><td colspan=\"3\">SUBTOTAL</td><td>$100.00</td></tr><tr><td colspan=\"3\">SALES TAX</td><td>$10.00</td></tr><tr><td colspan=\"3\">TOTAL</td><td>$110.00</td></tr><tr><td colspan=\"3\">PREVIOUS BALANCE</td><td>$500.00</td></tr><tr><td colspan=\"3\">TOTAL DUE</td><td>$610.00</td></tr></table>\n\nTHANK YOU FOR YOUR BUSINESS!\n\nREMIT TO:\n\nContoso Billing\n\n123 Remit St\n\nNew York, NY, 10001\n\n> Processed by pdfToMarkdown.dev",
  "pages": 1,
  "request_id": "req_example_invoice"
}

为什么适合中文文档

当前处理模型为 Mistral OCR 4.1,可查看带日期的源 PDF 与完整输出。2026年7月 CJK 基准记录的是旧 PaddleOCR 处理器。 中文字符错误率0.00%(Tesseract 5.5.1:0.97%)仅适用于当次模拟扫描,不能代表当前模型的准确率。

扫描件 / 图片型 PDF

没有文本层的纸质扫描件也能识别中文正文和标题,输出为 Markdown。

复杂表格

简单的矩形表格会转换为转义后的 GFM 管道表格;包含跨行、跨列或嵌套内容的复杂表格会以经过清理的 HTML 保留在 Markdown 中,避免破坏结构。

多栏排版

论文、报告的多栏版式也能按正确的阅读顺序重组文本。

支持的文档类型

发票与收据

研究论文(含数学公式)

合同协议

财务报表

技术手册

扫描文档

在 Python 中使用

一次 HTTP 请求即可。除 requests 外无需安装任何东西。

python
import base64
import os
import sys
from pathlib import Path
import requests

key = "demo_public_key"
identity = "demo-example"
if not key or not identity:
    sys.exit("Set PDFTOMARKDOWN_API_KEY and a saved PDFTOMARKDOWN_IDEMPOTENCY_KEY")
try:
    response = requests.post(
        "https://pdftomarkdown.dev/v1/convert",
        headers={"Authorization": f"Bearer {key}", "Idempotency-Key": identity,
                 "Content-Type": "application/json"},
        json={"input": {"pdf_url": "https://pdftomarkdown.dev/samples/invoice.pdf"}},
        timeout=(10, 660),
    )
    if response.status_code != 200:
        sys.exit(f"HTTP {response.status_code}; keep the identity for recovery")
    result = response.json()
    if not (isinstance(result, dict) and result.get("complete") is True
            and isinstance(result.get("markdown"), str)
            and type(result.get("pages")) is int and result["pages"] >= 0
            and isinstance(result.get("request_id"), str) and result["request_id"].strip()):
        sys.exit("Incomplete or invalid conversion response")
    sys.stdout.write(result["markdown"])
except (requests.RequestException, ValueError, OSError):
    sys.exit("Conversion failed; keep the identity and original input for recovery")

不写代码也能用

官方 CLI 一条命令即可转换本地文件、URL 或标准输入。有 Node 18+ 就行,无需安装。

terminal
$ npx pdftomarkdown 文档.pdf > 文档.md

想让 Codex、Claude Code 等编程智能体读取 PDF?请看让 Codex 读取 PDF。 更多用法见 CLI 指南(英文)。

价格

演示免费转换第1页,新账户一次性获赠20页。之后使用付费额度。月度页数到期失效,单独购买的额度不失效。

Hacker

免费,无需注册

  • 直接使用公开演示密钥
  • 每个 PDF 仅转换第 1 页
  • 每 IP 每分钟 3 次请求
  • 输出带水印
查看文档 →

Developer

一次性20页试用,之后付费

  • 发放个人 API 密钥
  • 新账户一次性获赠 20 页
  • 支持多页 PDF
  • 无水印
获取 API 密钥 →

粘贴到终端即可运行,无需账号。

#!/usr/bin/env bash
set -euo pipefail
key=demo_public_key
identity=demo-example
work=$(mktemp -d)
trap 'rm -rf "$work"' EXIT
result=$work/result
status=$(curl --silent --show-error --max-time 660 -o "$result" -w '%{http_code}' \
  "https://pdftomarkdown.dev/v1/convert" \
  -H "Authorization: Bearer $key" -H "Idempotency-Key: $identity" \
  -H "Content-Type: application/json" \
  --data '{"input":{"pdf_url":"https://pdftomarkdown.dev/samples/invoice.pdf"}}')
if [ "$status" != 200 ]; then echo "HTTP $status; keep the identity for recovery" >&2; exit 1; fi
python3 - "$result" <<'PY'
import json, sys
with open(sys.argv[1]) as response:
    result = json.load(response)
if not (isinstance(result, dict) and result.get("complete") is True
        and isinstance(result.get("markdown"), str)
        and type(result.get("pages")) is int and result["pages"] >= 0
        and isinstance(result.get("request_id"), str) and result["request_id"].strip()):
    sys.exit("Incomplete or invalid conversion response")
sys.stdout.write(result["markdown"])
PY