经过验证的工作流程 API

在使用 API 之前先验证工作流程

先在工作站中开始,确认已审核的输出符合您的工作流程,然后再使用 API 进行可重复的提取运行。

Base: https://pdf2text.ai/api/v1

身份认证

所有 API 请求都需要在 Authorization 请求头中提供 Bearer 令牌。在验证了可重复的工作流程后,请从账户设置中生成密钥。

管理 API 密钥
# Set your API key
export P2T_API_KEY="your_api_key_here"

# Include in all requests
curl -H "Authorization: Bearer $P2T_API_KEY" ...
import requests
import os

API_KEY = os.environ["P2T_API_KEY"]
headers = {"Authorization": f"Bearer {API_KEY}"}
const API_KEY = process.env.P2T_API_KEY;

const headers = {
  'Authorization': `Bearer ${API_KEY}`
};

使用限制

Firm 每月包含 1500 页,可用于已审核的工作站工作流程和经过验证的 API 提取运行。每处理一页,就会从该额度中扣除一页。

用量按处理的页数计算,而不是按 API 调用次数计算。上传文件以及下载结果或导出文件不会消耗页数,但再次运行提取会重新处理并再次消耗这些页数。

支持上传 PDF、PNG、JPEG 和 WebP,单个文件最大 50 MiB。当前部署还将整个 multipart 请求限制为 100 MiB,包括所有文件和表单开销。上传限制为每个请求 20 个文件、每个分组 100 个文件、每个文档或每次运行 500 页,以及每个分组存储 1,000 页。图像任一边最多 20,000 像素,且不超过 4000 万像素;以 OCR 渲染分辨率渲染后会超出这些栅格限制的 PDF 页面将被拒绝。

没有单独的 API 调用配额。提取运行是异步的,会进入处理队列,因此完成时间可能因文档大小和系统负载而有所不同。

合理使用请求限流的默认值为每个账户每小时 30 次上传、60 次运行启动和 600 次读取或导出。429 响应包含 Retry-After;请等待该时间间隔后再重试。

在依赖 API 自动化之前,请先使用工作站验证文档版式、审核规则和导出格式。

比较套餐
POST

上传文档

上传一个或多个 PDF、PNG、JPEG 或 WebP 文件以创建文档分组。在保存任何内容之前,系统会验证文件字节、页数和图像尺寸。上传成功后会返回 201 Created,并附带每个文档经过验证的 page_count。

端点

POST /api/v1/documents/upload/

参数

file / files binary PDF、PNG、JPEG 或 WebP(必填;上传多个文件时重复 files 字段)
grouping_id uuid 可选。添加到您现有的某个 API 分组中。匿名工作区无法通过 UUID 认领,且在运行进行期间,其所属分组在完成前不可更改。
document_type string 可选。分类提示。
password string 可选。在此请求中打开受密码保护的 PDF。仅用于打开文件,不会保存。未提供时,此类 PDF 返回 encrypted_pdf;密码错误返回 pdf_password_incorrect;同一文件多次输错则返回 429。
# Upload a PDF file
curl -X POST https://pdf2text.ai/api/v1/documents/upload/ \
  -H "Authorization: Bearer $P2T_API_KEY" \
  -F "file=@invoice.pdf"

# Add to existing group
curl -X POST https://pdf2text.ai/api/v1/documents/upload/ \
  -H "Authorization: Bearer $P2T_API_KEY" \
  -F "file=@invoice2.pdf" \
  -F "grouping_id=a1b2c3d4-..."
import requests

url = "https://pdf2text.ai/api/v1/documents/upload/"

# Upload single file
with open("invoice.pdf", "rb") as f:
    response = requests.post(
        url,
        headers=headers,
        files={"file": f}
    )

data = response.json()
group_id = data["group_id"]
const { readFile } = require('node:fs/promises');

const form = new FormData();
form.append(
  'file',
  new Blob([await readFile('invoice.pdf')], { type: 'application/pdf' }),
  'invoice.pdf'
);

const response = await fetch(
  'https://pdf2text.ai/api/v1/documents/upload/',
  {
    method: 'POST',
    headers,
    body: form
  }
);
if (!response.ok) throw new Error(`Upload failed: ${response.status}`);
const { group_id } = await response.json();
201 Created
{
  "status": "success",
  "group_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "documents": [
    {
      "doc_id": 42,
      "uid": "1847f600-...",
      "grouping_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "original_filename": "invoice.pdf",
      "file_type": "pdf",
      "page_count": 3,
      "status": "pending"
    }
  ]
}

仅可在分组首次运行之前向其追加文件。对于之后的发票,请创建新的分组;否则,未经筛选的运行会处理该分组中已有的每一份文档。

POST

启动提取任务

为文档分组启动异步处理。如需 PDF2TEXT 在提取前对文档进行分类,请使用自动路由。

每个启动请求都要发送 Idempotency-Key 请求头。使用相同的键和请求体重复请求会返回同一次运行;将该键用于不同的请求会返回 409。在运行以失败终止后,请使用新的键来有意创建替代运行。新加入队列的运行会返回 202 Accepted。每个分组同一时间只能有一个运行处于活动状态,活动运行会预留其可处理的账户页数。

202 响应仅确认运行已被接受,并不代表所请求的每一页都有足够额度。如果共享账户余额小于该运行所需,API 会处理已预留的页面,并以 partial 状态结束,同时返回 credit_limit_reached=true。在将导入视为完成之前,请检查运行的最终状态。

回调 URL 必须使用公网 HTTPS,且不能重定向到其他目标地址。投递是异步的,请求超时时间为 10 秒;对于暂时性连接错误、HTTP 429 和 HTTP 5xx 响应,最多重试四次,退避间隔为 30 秒、2 分钟和 10 分钟。

提供 callback_secret 时,请对完整的原始请求体计算 HMAC-SHA256,并与 sha256= 的值进行比较,以验证 X-PDF2TEXT-Signature。使用 X-PDF2TEXT-Run-ID 或 X-PDF2TEXT-Idempotency-Key 对投递进行去重。

The callback JSON body is {"event": "idp.run.completed", "run": {...}}; run uses the same full result shape as GET ?include=results. A signature header is sent only when callback_secret was included in the start request.

端点

POST /api/v1/groups/{group_id}/runs/

请求体

auto_route boolean 自动分类(默认:true)
extraction_mode enum auto、raw_text、financial_table、general_table 或 unified。如需使用自定义架构,请使用 template_id 或 template_slug。
template_id / template_slug uuid / string 仅使用其中一个来强制指定模板
selected_pages array 可选的非空 {docUid, pageNumber} 列表;任何无效页面都会导致整个请求被拒绝
construction_project_id uuid 可选,API 账户拥有的项目
callback_url string Webhook URL
callback_secret string HMAC 签名密钥
# Start extraction with auto-routing
curl -X POST https://pdf2text.ai/api/v1/groups/$GROUP_ID/runs/ \
  -H "Authorization: Bearer $P2T_API_KEY" \
  -H "Idempotency-Key: invoice-import-123" \
  -H "Content-Type: application/json" \
  -d '{"auto_route": true}'
# Start extraction run
url = f"https://pdf2text.ai/api/v1/groups/{group_id}/runs/"

response = requests.post(
    url,
    headers={**headers, "Idempotency-Key": "invoice-import-123"},
    json={"auto_route": True}
)

run_data = response.json()
run_id = run_data["run_id"]
// Start extraction run
const response = await fetch(
  `https://pdf2text.ai/api/v1/groups/${groupId}/runs/`,
  {
    method: 'POST',
    headers: {
      ...headers,
      'Idempotency-Key': 'invoice-import-123',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ auto_route: true })
  }
);

const { run_id } = await response.json();
202 Accepted
{
  "status": "success",
  "run_id": "b3143b91-...",
  "job_id": "idp-run-b3143b91-...",
  "group_name": "ocr_job_a1b2c3d4...",
  "idempotent_replay": false,
  "run": {
    "id": "b3143b91-...",
    "status": "queued",
    "job_id": "idp-run-b3143b91-...",
    "total_pages": 3,
    "pages_processed": 0
  }
}
GET

获取结果

轮询运行端点或使用 Webhook 获取提取结果。支持导出为 JSON 或 Excel 格式。

默认的运行响应为轻量格式,适合频繁轮询,且从不缓存。请读取嵌套 run 对象中的 run.status。最终状态为 complete、partial 和 error。达到最终状态后,请使用 include=results 请求一次,以获取 full_text、structured_output 和 documents。

保留 credits_remaining 是为了兼容,它记录该次运行后的历史余额。credits_remaining_after_run 明确表示同一个历史值,而 account_credits_remaining 报告生成响应时账户当前的共享余额。

端点列表

GET /api/v1/runs/{run_id}/ GET /api/v1/runs/{run_id}/?include=results GET /api/v1/runs/{run_id}/export.json GET /api/v1/runs/{run_id}/export.xlsx GET /api/v1/runs/{run_id}/export.xlsx?date_format=MM/DD/YYYY

对于银行对账单,export.xlsx 还包含 QuickBooks CSV 和 Xero CSV 工作表。其日期使用您账户中保存的日期格式,否则使用对账单所在国家的顺序。传入 date_format(DD/MM/YYYY、MM/DD/YYYY、YYYY/MM/DD 或 YYYY-MM-DD)即可指定;其他工作表保持 YYYY-MM-DD。

响应状态码

200 401 404
# Lightweight status poll
curl https://pdf2text.ai/api/v1/runs/$RUN_ID/ \
  -H "Authorization: Bearer $P2T_API_KEY"

# Fetch completed results once, then export
curl "https://pdf2text.ai/api/v1/runs/$RUN_ID/?include=results" \
  -H "Authorization: Bearer $P2T_API_KEY"

curl -o results.xlsx \
  https://pdf2text.ai/api/v1/runs/$RUN_ID/export.xlsx \
  -H "Authorization: Bearer $P2T_API_KEY"
import time

run_url = f"https://pdf2text.ai/api/v1/runs/{run_id}/"
terminal = {"complete", "partial", "error"}
delay = 2

# Poll lightweight state with bounded exponential backoff
while True:
    response = requests.get(run_url, headers=headers)
    if response.status_code == 429:
        retry_after = int(response.headers.get("Retry-After", delay))
        time.sleep(retry_after)
        delay = min(30, max(10, delay * 2))
        continue
    response.raise_for_status()
    run = response.json()["run"]

    if run["status"] in terminal:
        break

    time.sleep(delay)
    delay = min(30, max(10, delay * 2))

if run["status"] == "error":
    raise RuntimeError(run["error_message"])

results = requests.get(run_url, params={"include": "results"}, headers=headers).json()["run"]

excel = requests.get(
    f"{run_url}export.xlsx",
    headers=headers
)
with open("results.xlsx", "wb") as f:
    f.write(excel.content)
// Poll lightweight state
const pollResults = async (runId) => {
  const runUrl = `https://pdf2text.ai/api/v1/runs/${runId}/`;
  const terminal = new Set(['complete', 'partial', 'error']);
  let delay = 2000;
  while (true) {
    const response = await fetch(runUrl, { headers });
    if (response.status === 429) {
      const retryAfter = Number(response.headers.get('Retry-After') || delay / 1000);
      await new Promise(r => setTimeout(r, retryAfter * 1000));
      delay = Math.min(30000, Math.max(10000, delay * 2));
      continue;
    }
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    const { run } = await response.json();

    if (terminal.has(run.status)) {
      if (run.status === 'error') throw new Error(run.error_message);
      const resultResponse = await fetch(`${runUrl}?include=results`, { headers });
      return (await resultResponse.json()).run;
    }

    await new Promise(r => setTimeout(r, delay));
    delay = Math.min(30000, Math.max(10000, delay * 2));
  }
};

const results = await pollResults(runId);
使用 include=results 的响应
{
  "status": "success",
  "run": {
    "id": "b3143b91-...",
    "status": "complete",
    "total_pages": 3,
    "pages_processed": 3,
    "credits_remaining_after_run": 497,
    "account_credits_remaining": 497,
    "full_text": "...",
    "structured_output": { /* grouped output */ },
    "documents": [
      {
        "doc_id": 42,
        "document_name": "invoice.pdf",
        "ocr_text": "...",
        "structured_output": { /* document output */ }
      }
    ]
  }
}
GET

模板

列出可用的提取模板。使用模板 ID 强制指定提取模式。

端点

GET /api/v1/templates/
管理模板
# List available templates
curl https://pdf2text.ai/api/v1/templates/ \
  -H "Authorization: Bearer $P2T_API_KEY"
# List available templates
response = requests.get(
    "https://pdf2text.ai/api/v1/templates/",
    headers=headers
)

templates = response.json()["templates"]
for t in templates:
    print(f"{t['name']}: {t['id']}")
// List available templates
const response = await fetch(
  'https://pdf2text.ai/api/v1/templates/',
  { headers }
);

const { templates } = await response.json();
templates.forEach(t => console.log(`${t.name}: ${t.id}`));
响应
{
  "status": "success",
  "templates": [
    {
      "id": "198aa5be-ea8b-4d67-bdb5-625f5d7d29e8",
      "slug": "invoice",
      "name": "Invoice",
      "description": "Standard invoice extraction",
      "extraction_mode": "financial_table"
    },
    {
      "id": "4317707a-f0da-4c2f-a1ec-d0119d96b55c",
      "slug": "receipt",
      "name": "Receipt",
      "description": "Receipt and expense extraction",
      "extraction_mode": "financial_table"
    }
  ]
}

需要帮助?

使用审核队列检查提取出的字段,并保留审核上下文。