Doc2X API v2 PDF 接口文档
基础信息
Base URL:https://v2.doc2x.noedgeai.com。本页介绍开放平台 PDF 解析:创建预上传任务、上传原文件、等待解析、请求导出、下载并保存结果。网页工作台、CLI 和 API 的账户与格式范围不同。
旧 POST /api/v2/parse/pdf 不建议新系统接入;请使用下方预上传流程。v3-2026 是解析模型选择,不是把 URL 改成 /api/v3。
输入与额度
- 预上传接口公开文件口径为不超过 1 GiB(1,073,741,824 bytes) 的 PDF;旧直接上传入口的 300 MiB 限制不可套用于这个入口。账号、部署配置或文件条件仍可能进一步限制接收。
- 单任务页数、同时处理任务数、处理中的页数和剩余额度分别判断,以开放平台账户条件及状态响应为准;不要把历史文档的 2000 页写成所有账户保证。图片 API 与 CLI 有各自上限。
- 提供可打开、未损坏的 PDF。文件后缀改成
.pdf不会把别的格式转换成 PDF。遇到密码或兼容问题,先本地检查,必要时拆分或另存副本。 - API 结果与关联图片临时保留约 24 小时,应尽快下载。保存
result.json或 Markdown 文字本身,不等于已保存其引用的图片。
Authorization 鉴权
在开放平台登录并创建/管理 API Key,确认当前解析额度。它不是桌面客户端或 Zotero 的登录 Token。以下 YOUR_DOC2X_API_KEY、TASK_UID、UPLOAD_URL、DOWNLOAD_URL 都是占位符;只在自己的运行环境替换,不提交到版本库。
Authorization: Bearer YOUR_DOC2X_API_KEY请求开放平台接口时发送 Bearer 头;PUT 预签名上传 URL 和 GET 下载 URL 时不要附加该 API Key。签名 URL 自身有时效与访问能力,不放入公开日志。
POST /api/v2/parse/preupload 文件预上传
可选 model 为 v2 或 v3-2026;公开接口约定省略时使用默认模型。本教程显式传入 v3-2026,避免依赖默认值。其他值不要自行编造。
curl --fail-with-body --request POST 'https://v2.doc2x.noedgeai.com/api/v2/parse/preupload' \
--header "Authorization: Bearer $DOC2X_API_KEY" \
--header 'Content-Type: application/json' \
--data '{"model":"v3-2026"}'简化响应示意(ID 和地址为占位,不可直接调用):
{"code":"success","data":{"uid":"TASK_UID","url":"https://upload.example.invalid/signed.pdf"}}先保存 uid,再向返回的 data.url PUT 原始文件二进制,不使用 multipart/form-data。取得链接后立即上传,以签名 URL 实际有效期为准;过期不能靠修改 URL 参数续期。
curl --fail-with-body --request PUT "$UPLOAD_URL" --data-binary '@paper.pdf'PUT 成功后进入状态查询,不要重复上传同一签名链接。上传传输失败时保留 uid 并先判断状态;网络中断不证明服务端未接收,盲目新建任务可能重复消耗额度。
GET /api/v2/parse/status 查看异步状态
curl --fail-with-body --get 'https://v2.doc2x.noedgeai.com/api/v2/parse/status' \
--header "Authorization: Bearer $DOC2X_API_KEY" \
--data-urlencode "uid=$TASK_UID"每隔 1–3 秒查询一次并设置总等待期限。刚上传后可能有短暂队列/状态传播延迟。HTTP 成功不代表任务成功,还需检查 JSON 的 code 和 data.status。
| 字段/状态 | 怎么处理 |
|---|---|
code != "success" | 接口错误;记录错误码和任务 ID,按下方排查。 |
data.status = "processing" | 等待后继续 GET,不重发创建请求。 |
data.status = "failed" | 停止等待,检查 data.detail。 |
data.status = "success" | 读取 data.result.pages,保存结果并进入导出。 |
| 超时或未识别的状态 | 保留 uid,稍后查原任务;不要把本地超时解释为远端取消。 |
简化完成响应:
{"code":"success","data":{"status":"success","progress":100,"result":{"pages":[{"page_idx":0,"page_width":1000,"page_height":1400,"md":"# Example\nText","url":"","score":89}]}}}任务失败可发生在成功的 HTTP/业务外层内:
{"code":"success","data":{"status":"failed","detail":"Example failure detail"}}page_idx 从 0 开始;page_width/page_height 是结果页面坐标尺寸;md 为本页 Markdown,url 可能为空,score 是质量分而非逐字准确率。v3-2026 的结构化字段见 v3 JSON。
POST /api/v2/convert/parse 请求导出文件
解析成功后创建一次导出任务,再轮询导出结果接口。不要把创建接口当成轮询接口。
| 参数 | 类型 | 说明 |
|---|---|---|
uid | string | 已成功解析的任务 ID。 |
to | string | md、tex 或 docx;网页中的其他选项不自动等于公开 API 参数。 |
formula_mode | string | 必填,通常为 normal;Markdown 使用美元公式定界时选 dollar。 |
filename | string,可选 | MD/TEX 文件基本名,不带扩展名,例如 paper。 |
merge_cross_page_forms | boolean,可选 | 尝试合并跨页表格;应核对合并结果。 |
formula_level | integer,可选 | 0 保留公式;1 将行内公式降为文本;2 将所有公式降为文本。只对源解析模型 v3-2026 生效。 |
{"uid":"TASK_UID","to":"md","formula_mode":"normal","filename":"paper","merge_cross_page_forms":false,"formula_level":0}将上面的任务 ID 换成实际值并保存为 export-request.json:
curl --fail-with-body --request POST 'https://v2.doc2x.noedgeai.com/api/v2/convert/parse' \
--header "Authorization: Bearer $DOC2X_API_KEY" \
--header 'Content-Type: application/json' \
--data-binary '@export-request.json'响应可为 {"code":"success","data":{"status":"processing","url":""}}。同一 uid 连续比较不同格式或公式等级时,等当前导出完成并下载后再请求下一份;不要并行覆盖结果。
GET /api/v2/convert/parse/result 导出获取结果
curl --fail-with-body --get 'https://v2.doc2x.noedgeai.com/api/v2/convert/parse/result' \
--header "Authorization: Bearer $DOC2X_API_KEY" \
--data-urlencode "uid=$TASK_UID"沿用有界轮询规则。只有 data.status="success" 且 data.url 非空才下载;失败检查 detail。解析成功与导出成功是两个阶段。
{"code":"success","data":{"status":"success","url":"https://download.example.invalid/export.zip"}}下载与本地保存
curl --fail --location --output export.zip "$DOWNLOAD_URL"
unzip -l export.zipMD/TEX 导出可能是包含文本与图片资源的 ZIP;先检查实际文件格式,再解压到新目录。DOCX 是办公文档,即使内部也使用 ZIP 容器,也应保留 .docx 扩展名用办公软件打开。不要只改后缀推断格式。保留 Markdown/TeX 与图片的相对路径,在目标编辑器检查公式、图片、表格和页序。
标准 JSON 解析会将 \u0026 还原为 &;只有解析后的字符串仍含字面量 \u0026 时才替换一次,不要随意改写签名 URL 的其他字符。
完整 Python 示例
需要 Python 3.10+ 和 requests(python -m pip install requests)。在本机设置 DOC2X_API_KEY,准备 paper.pdf,保存为 parse_pdf.py 后运行 python parse_pdf.py。示例会真实提交解析/导出任务并消耗对应额度;本教程代码仅作离线校验,没有代替你运行服务。
import json
import os
import time
from pathlib import Path
import requests
BASE = "https://v2.doc2x.noedgeai.com"
def api(method, path, *, key, **kwargs):
response = requests.request(method, BASE + path,
headers={"Authorization": "Bearer " + key}, timeout=(10, 90), **kwargs)
response.raise_for_status()
body = response.json()
if body.get("code") != "success":
raise RuntimeError(f"API error: {body.get('code')}: {body.get('msg', '')}")
return body["data"]
def wait_for(path, uid, *, key, max_wait=1200):
deadline = time.monotonic() + max_wait
while time.monotonic() < deadline:
data = api("GET", path, key=key, params={"uid": uid})
state = data.get("status")
if state == "success":
return data
if state == "failed":
raise RuntimeError(f"Task failed: {data.get('detail', '')}")
# Upload/queue propagation can briefly precede processing.
time.sleep(3)
raise TimeoutError(f"Wait expired; retain uid={uid} and query status, do not resubmit")
def main():
key = os.environ["DOC2X_API_KEY"]
source = Path("paper.pdf")
if not source.is_file() or source.stat().st_size == 0:
raise ValueError("Provide a nonempty paper.pdf")
out = Path("api-output")
out.mkdir(exist_ok=False) # Choose a new directory for each intentional task.
task = api("POST", "/api/v2/parse/preupload", key=key,
json={"model": "v3-2026"})
uid = task["uid"]
(out / "task.json").write_text(json.dumps({"uid": uid}), encoding="utf-8")
# Signed object-storage URLs do not need your Doc2X Authorization header.
with source.open("rb") as stream:
response = requests.put(task["url"], data=stream, timeout=(10, 180))
response.raise_for_status()
parsed = wait_for("/api/v2/parse/status", uid, key=key)
result = parsed["result"]
(out / "result.json").write_text(
json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8")
pages = sorted(result.get("pages", []), key=lambda p: p.get("page_idx", 0))
(out / "preview.md").write_text(
"\n\n".join(p.get("md", "") for p in pages), encoding="utf-8")
api("POST", "/api/v2/convert/parse", key=key, json={
"uid": uid, "to": "md", "formula_mode": "normal",
"filename": "paper", "merge_cross_page_forms": False, "formula_level": 0})
exported = wait_for("/api/v2/convert/parse/result", uid, key=key)
download_url = exported["url"].replace("\\u0026", "&")
with requests.get(download_url, stream=True, timeout=(10, 180)) as response:
response.raise_for_status()
with (out / "export.zip").open("wb") as target:
for chunk in response.iter_content(1024 * 1024):
target.write(chunk)
print(f"Saved result.json, preview.md and export.zip in {out}; uid={uid}")
if __name__ == "__main__":
main()api-output/task.json 在上传前保存 uid。result.json 是 data.result,可继续用于结构化处理;preview.md 是逐页正文合并,不保证图片已本地化;完整 MD 导出包为 export.zip。再次运行请选择新输出目录,避免覆盖已有结果。
错误排查
| 错误或现象 | 处理 |
|---|---|
| HTTP 401/403 | 检查 API Key、账户与权限;不要使用网页登录 Token 替代。 |
HTTP 429 / parse_task_limit_exceeded / parse_concurrency_limit | 减少并行任务,等已有任务完成,按账户任务数/处理页数限额调整。 |
parse_quota_limit | 核对开放平台剩余解析额度,勿连续重试。 |
parse_file_too_large / parse_page_limit_exceeded | 按正在使用的入口与实际返回限额拆分;不能用网页/CLI限制代替API合同。 |
parse_status_not_found | 检查保存的 uid、账号及结果是否过期;过期后先找本地备份。 |
parse_file_not_pdf / parse_file_invalid | 本地打开验证 PDF、加密与文件内容;改后缀无效。 |
parse_timeout / parse_error / parse_create_task_error | 保留任务 ID、错误码和脱敏 request_id;检查文件后再决定是否拆分或联系支持,不无限重建。 |
parse_file_lock | 文件可能因重复失败被临时锁定;检查兼容性并联系支持,不通过改名反复提交。 |
| 下载返回错误或文件损坏 | 先检查HTTP状态与导出状态;不要把错误JSON当ZIP保存。链接过期时查询原任务能否取回,勿先重跑OCR。 |
服务端既可能用 HTTP 错误,也可能在 HTTP 200 中返回业务错误。并发、页数与配额错误也可能在上传后查询状态时才出现。
实用集成
- pdfdeal 官方文档:先按其当前版本安装与配置;不要假设任意旧版本都有 v3 裁切辅助函数。
- MCP 工具:开放平台 API Key,适合 MCP 客户端提交、查询和保存结果。
- CLI:个人账户登录流程,与开放平台 API 不同。
- 扣子插件:可用性和参数以插件当前说明为准。