跳转到主要内容
本文档帮助你从旧版 pdf_to_markdown 接口平滑迁移到新版 xParse API,并介绍新增的异步处理能力。
文中所有参数映射、字段名与默认值均以 TextIn 官方接口文档为准。

为什么迁移

新版 xParse API 对旧版 pdf_to_markdown 做了全面升级,核心优势:

更轻量的数据结构

标准化的 Element 模型,语义类型清晰(Title / NarrativeText / Table / Image / Formula),JSON 体积更小

标准输入输出协议

统一的请求配置(config)与响应结构(schema),便于集成与迁移

引擎可替换

支持 TextIn / MinerU / PaddleOCR / GUI 多引擎,可按场景选最优或对比效果

异步处理能力

新增异步接口,适合大文件(建议 >10 页)和批量场景,避免 HTTP 超时;支持 Webhook 回调

坐标系标准化

归一化坐标(0~1),不再依赖 dpi 参数

更丰富的内嵌元素

行内公式、手写体、复选框(checkbox)、内嵌图片

接口变更总览

维度旧版新版
URL(同步)/ai/service/v1/pdf_to_markdown/api/v1/xparse/parse/sync
URL(异步)❌ 不支持/api/v1/xparse/parse/async
任务状态查询❌ 不支持GET /api/v1/xparse/parse/async/{job_id}
文件上传Body 二进制流(application/octet-streammultipart/form-data,字段名 file
URL 文件方式Body 文本(text/plainmultipart/form-data,字段名 file_url
参数传入位置Query Stringconfig JSON 字段(form-data 内)
参数开关类型整数 0 / 1Boolean false / true
响应根节点resultdata
坐标系绝对像素(受 dpi 影响)归一化值(0~1)
页码起始page_id(起始值 1-based)page_number(1-based,第一页为 1)

入参迁移指南

请求方式变更

POST https://api.textin.com/ai/service/v1/pdf_to_markdown?parse_mode=auto&table_flavor=html&page_details=1
Content-Type: application/octet-stream
x-ti-app-id: <your_app_id>
x-ti-secret-code: <your_secret>

[二进制文件内容]

新版 config 结构总览

新版参数不再是扁平的 Query String,而是放在 form-data 的 config 字段里。该字段的值是一个 JSON,内部又分四个区块(注意:其中一个区块也叫 config,下文以「config 字段值 → config 区块」指代,切勿写成 {"config":{"config":{...}}} 之外的多层嵌套):
{
  "document": { "password": "..." },
  "capabilities": { "table_view": "html", "pages": true, "...": "..." },
  "scope": { "page_range": "1-10" },
  "config": {
    "force_engine": "textin",
    "engine_params": { 
      "parse_mode": "auto", 
      "formula_level": 0,
      "image_output_type": "url" 
    }
  }
}
区块作用
document文档本身处理参数(如加密 PDF 密码)
capabilities解析策略与返回格式开关(决定返回哪些字段、表格格式等)
scope处理范围(如页码区间)
config(区块)引擎选择(force_engine)+ 引擎级自定义参数(engine_params
关键认知:旧版的 parse_modeformula_level 等”引擎行为参数”在新版并未消失,而是下沉到了 config 区块的 engine_params 里。它们与 capabilities 里的”返回内容开关”是两个不同维度,迁移时不要混淆。(注:dpi 是例外,新版已彻底移除。)

参数字段映射表(核心)

下表已按官方文档逐项核对。「新版参数」列给出在 config 字段值内的相对路径(engine_paramsconfig 区块下的 engine_params)。
旧版参数
(Query)
旧默认新版参数新默认变更说明
pdf_pwddocument.password仅改位置
page_start +
page_count
0 / 1000scope.page_range
(如 "1-10"
合并为区间字符串
apply_document_tree
(0/1)
1capabilities.include_hierarchytrueint→bool
table_flavor
(md/html/none)
htmlcapabilities.table_view
(markdown/html)
html”md” → “markdown"
"none” 已移除
get_image
(none/page/objects/both)
nonecapabilities.include_image_data
(是否返回)
false详见下方说明
image_output_type
(base64str/default)
defaultengine_params.image_output_type
(url/base64)
url取值体系不同:
旧 base64str/default
→ 新 url/base64
formula_level
(0/1/2)
0engine_params.formula_level
(0/1)
0⚠️ 保留在引擎参数,
取值改为 0/1
page_details
(0/1)
1capabilities.pagesfalse⚠️ 默认反转,需显式 true
char_details
(0/1)
0capabilities.include_char_detailsfalseint→bool
catalog_details
(0/1)
0capabilities.title_treefalse改名
crop_dewarp
(0/1)
0capabilities.crop_dewarpfalseint→bool
remove_watermark
(0/1)
0capabilities.remove_watermarkfalseint→bool
parse_mode
(auto/scan/lite/parse)
scanengine_params.parse_mode
(auto/scan/parse/lite/vlm)
见下方说明✅ 保留;新增 vlm 模式
raw_ocr
(0/1)
0近似改用
capabilities.include_char_details
false⚠️ 语义非完全等价
dpi
(72/144/216)
144❌ 已移除新版用归一化坐标,
无对应参数
get_excel0❌ 已移除新版不支持 Excel 输出
markdown_details1❌ 已移除elements 始终返回
get_image 迁移注意:旧版 get_image 有 none/page/objects/both 四档语义(整页图 / 子图 / 两者)。新版通过组合 capabilities.pagescapabilities.include_image_data 实现:
  • pages=true 时,include_image_data=true/false 对应 get_image=both/page
  • pages=false 时,include_image_data=true/false 对应 get_image=objects/none
图片格式由 engine_params.image_output_type(url/base64)控制。
  • parse_mode 在新版的默认值:旧版默认 scan;新版 parse/sync 接口默认值以官方文档为准
  • force_engine 是新增的”引擎选择”维度(textin/mineru/paddle_ocr/textin_gui),不是 parse_mode 的替代品;在textin引擎下,parse_mode可同时配置

新增能力(仅新版支持)

参数默认说明
capabilities.include_inline_objectsfalse返回文本内的细粒度行内对象:公式、手写、复选框、内嵌图片
capabilities.include_table_structurefalse返回表格结构化数据(行列数、单元格坐标、跨行跨列、单元格内容类型)
config.force_enginetextin指定引擎:textin(默认)/ mineru / paddle_ocr / textin_gui。各引擎适用场景与限制以官方文档为准
config.engine_params引擎自定义参数(专家模式),不同引擎支持的参数不同

出参迁移指南

顶层结构变化

{
  "code": 200,
  "message": "success",
  "result": { "...": "主体数据" },
  "version": "v1.0",
  "duration": 1234
}

主体数据字段映射

旧版 result.*新版 data.*变更说明
markdownmarkdown不变
detailelements改名,结构重组(见下节)
pagespages字段名相同,内部结构重组
catalog(含 toctitle_tree改名,结构变为节点数组
total_page_numbermetadata.page_count下沉至 metadata
valid_page_numbersuccess_count改名
excel_base64❌ 已移除新版不支持 Excel
(无)schema_version / file_id / job_id / metadata新增字段

元素结构变化(detail → elements)

旧版 detail[] 与新版 elements[] 的字段对照:
旧版 detail[]新版 elements[]变更说明
paragraph_id(整数)element_id(字符串,如 el_001ID 类型由整数改为字符串
type(“paragraph” / “table” 等)type(语义字符串)改为 Title / NarrativeText / Table / Image / Formula 等语义类型
texttext不变
position(绝对像素)coordinates(归一化 0~1)⚠️ 坐标系变化,需换算
page_idpage_number页码起始位置相同(具体说明见下节)
outline_levelmetadata.category_depth层级信息下沉至 metadata
cells(表格单元格)table_structure.cells下沉至 table_structure 子对象
caption_idmetadata.ref_element_id关联关系下沉至 metadata
tags
(formula/handwritten)
objects[](内嵌对象)改为结构化的行内对象数组
(无)metadata.parent_id / children_ids新增:父子层级关系
(无)image_data新增:图片 URL / MIME / base64
(无)char_details新增:字符级详情

新版元素示例

{
  "element_id": "el_001",
  "type": "Title",
  "text": "检验结果",
  "page_number": 1,
  "coordinates": [0.1, 0.12, 0.32, 0.12, 0.32, 0.16, 0.1, 0.16],
  "metadata": {
    "category_depth": 0,
    "children_ids": ["el_002", "el_003"],
    "is_continuation": false
  }
}

坐标系与页码换算

坐标换算

# 旧版:绝对像素坐标(受 dpi 影响),字段名 position
# position = [x1, y1, x2, y2, x3, y3, x4, y4]  单位:像素

# 新版:归一化坐标(范围 0~1),字段名 coordinates
# 如需换算为像素,从 data.pages[] 取该页宽高:
page_width  = page["page_width"]
page_height = page["page_height"]
pixel_coords = [
    (coords[i] * page_width, coords[i + 1] * page_height)
    for i in range(0, 8, 2)
]

页码说明

  • 旧版page_id,1-based(第一页为 1)
  • 新版page_number,1-based(第一页为 1)
两者起始值相同,均为 1-based,遍历/定位页面的逻辑可直接迁移。
归一化 coordinates 8 个值的点序为:左上 → 右上 → 右下 → 左下。

异步接口使用指南

旧版不支持异步。新版异步接口适合大文件(建议 >10 页)或批量处理,避免 HTTP 连接超时。

接口概览

接口HTTPURL说明
提交异步任务POST/api/v1/xparse/parse/async上传文件,返回 job_id
查询状态/结果GET/api/v1/xparse/parse/async/{job_id}轮询状态,完成后含 result_url

任务状态枚举

状态含义
pending排队等待中
in_progress处理中
completed已完成(响应含 result_url,下载该 URL 获取完整结果)
failed处理失败(响应含 message 错误说明)
异步提交成功仅返回 {"data": {"job_id": "..."}},完整解析结果需通过 result_url 二次下载。result_url 返回的数据结构与同步接口 data 一致。

异步调用完整流程(Python)

import requests, time, json

APP_ID  = "your_app_id"
SECRET  = "your_secret_code"
HEADERS = {"x-ti-app-id": APP_ID, "x-ti-secret-code": SECRET}


def submit_async_job(file_path: str, config: dict = None) -> str:
    """提交异步解析任务,返回 job_id"""
    url = "https://api.textin.com/api/v1/xparse/parse/async"
    with open(file_path, "rb") as f:
        files = {"file": (file_path, f, "application/pdf")}
        data  = {"config": json.dumps(config)} if config else {}
        resp  = requests.post(url, headers=HEADERS, files=files, data=data)
    resp.raise_for_status()                       # 先校验 HTTP 状态
    result = resp.json()
    if result.get("code") != 200:                 # 再校验业务 code,避免 KeyError
        raise RuntimeError(f"提交失败: {result.get('message')}")
    return result["data"]["job_id"]


def poll_result(job_id: str, interval: int = 3, max_wait: int = 300) -> dict:
    """轮询任务状态,完成后返回完整解析结果"""
    url     = f"https://api.textin.com/api/v1/xparse/parse/async/{job_id}"
    elapsed = 0
    while elapsed < max_wait:
        resp = requests.get(url, headers=HEADERS)
        resp.raise_for_status()
        job_data = resp.json()["data"]
        status   = job_data["status"]
        if status == "completed":
            # result_url 鉴权要求待确认,此处带 headers 以求稳妥
            return requests.get(job_data["result_url"], headers=HEADERS).json()
        elif status == "failed":
            raise RuntimeError(f"任务失败: {job_data.get('message')}")
        print(f"  {status},已等待 {elapsed}s ...")
        time.sleep(interval)
        elapsed += interval
    raise TimeoutError(f"超时({max_wait}s),job_id={job_id}")


config = {
    "capabilities": {
        "include_hierarchy":  True,
        "table_view":         "html",
        "include_image_data": True,   # ⚠️ 默认 false,需显式开启
        "pages":              True,   # ⚠️ 默认 false,需显式开启
        "title_tree":         True,
    }
}
job_id = submit_async_job("large_document.pdf", config)
result = poll_result(job_id)
print(result["data"]["markdown"])

Webhook 回调(推荐生产环境)

提交任务时附带 webhook 参数,任务完成/失败后系统主动 POST 推送,无需轮询:
# 注意:file_url 同样走 multipart/form-data 提交。
# requests 用 files= 触发 multipart;下方用 data= 会变成 urlencoded;
# 服务端通常也能解析表单字段,但若严格遵循 multipart 协议,建议统一用 files=。
fields = {
    "file_url": (None, "https://your-storage.com/document.pdf"),
    "webhook": (None, "https://your-server.com/callback/textin"),
    "config": (None, json.dumps({"capabilities": {"table_view": "html"}})),
}

resp = requests.post(
    "https://api.textin.com/api/v1/xparse/parse/async",
    headers=HEADERS,
    files=fields
)
resp.raise_for_status()
job_id = resp.json()["data"]["job_id"]

# 你的服务器收到的 Webhook Body(Method: POST, Content-Type: application/json):
# {
#   "job_id": "xxx",
#   "status": "completed",
#   "result_url": "https://..."
# }

完整代码迁移示例

import requests

def parse_pdf_old(file_path, app_id, secret):
  url = "https://api.textin.com/ai/service/v1/pdf_to_markdown"
  params = {
      "parse_mode": "auto",
      "table_flavor": "html",
      "get_image": "objects",
      "page_details": 1,
      "catalog_details": 1,
      "apply_document_tree": 1,
  }
  headers = {
      "x-ti-app-id": app_id,
      "x-ti-secret-code": secret,
      "Content-Type": "application/octet-stream",
  }
  with open(file_path, "rb") as f:
      resp = requests.post(url, headers=headers,
                           params=params, data=f)
  resp.raise_for_status()
  data = resp.json()
  if data.get("code") != 200:
      raise RuntimeError(data.get("message"))
  return {
      "markdown":    data["result"]["markdown"],
      "total_pages": data["result"]["total_page_number"],
      "elements":    data["result"]["detail"],
      "catalog":     data["result"]["catalog"],
      "pages":       data["result"]["pages"],
  }

迁移检查清单

1

更新请求 URL

/ai/service/v1/pdf_to_markdown/api/v1/xparse/parse/sync
2

更改请求方式

Query String + 二进制 Body → multipart/form-data + config JSON
3

转换参数类型

整数开关(0/1)全部改为 boolean(false/true)
4

显式声明必要参数

⚠️ 显式声明 capabilities.include_image_data: true(默认已反转)
⚠️ 显式声明 capabilities.pages: true(默认已反转)
5

迁移引擎参数

parse_mode / formula_level / image_output_type 迁移到 config 区块的 engine_params(非 capabilities)
6

评估引擎选择

评估是否需要 force_engine 选择特定引擎(新增能力,textin引擎下可同时配置 parse_mode
7

更新响应解析

  • 响应根节点:resultdata
  • total_page_numberdata.metadata.page_count
  • valid_page_numberdata.success_count
  • detaildata.elementsparagraph_idelement_idtype 由 “paragraph” 等改为语义字符串)
  • catalogdata.title_tree
8

转换坐标系统

坐标:position(绝对像素)→ coordinates(归一化,× page_width/height 换算)
9

更新表格参数

table_flavor='md'table_view='markdown';‘none’ 已移除,需调整逻辑
10

更新图片参数

image_output_type:取值由 base64str/default 改为 url/base64
11

处理已移除参数

  • 如用 get_excel:新版不支持 Excel 输出,需另寻替代
  • 如用 raw_ocr:近似改用 capabilities.include_char_details(语义非完全等价,需验证)
  • dpi:新版无对应参数,坐标改为归一化输出
12

增强错误处理

所有响应在读取 ["data"] 前先校验 HTTP 状态与业务 code,避免鉴权/参数错误变成 KeyError
13

考虑异步接口

大文件(建议 >10 页)评估迁移至异步接口
14

配置 Webhook

生产环境异步场景配置 Webhook 回调替代轮询