Stirling-PDF 3.2.0 API 使用详解

Stirling-PDF 3.2.0 REST API 实战详解:免认证/JWT/API Key 三种认证链路实测、319 端点 28 分组全景、general/convert/security/misc 核心端点请求-响应逐字回显、结构化错误码(E004/企业版 403)与排障实录、文件响应与分页通用约定。

Stirling-PDFStirling-PDFPDF2026/10/22

Stirling-PDF 3.2.0 API 使用详解

目标:讲清 Stirling-PDF 3.2.0 的 REST API 体系——三种认证方式(免认证探测/会话 JWT/API Key)的完整实测链路、319 个端点的 28 个功能分组、文件类与信息类端点的请求-响应实测对照、结构化错误响应与错误码,以及企业版端点的行为边界。全部请求与响应均在部署环境(Ubuntu 22.04、Stirling-PDF 3.2.0,服务地址 http://192.168.146.128:8129)用 curl 实测取得,逐字照抄。部署与配置背景见 《Stirling-PDF 3.2.0 Docker Compose 部署指南》、《Stirling-PDF 3.2.0 配置参数详解》。

一、环境信息

本篇 API 实测环境与版本基线如下:

  • 服务端:Stirling-PDF 3.2.0(Java/Spring Boot,REST API 与 Web 界面同端口同服务)
  • 操作系统:Ubuntu 22.04.4 LTS
  • 服务地址:http://192.168.146.128:8129(API 基路径 http://192.168.146.128:8129/api/v1)
  • 客户端工具:curl 7.81.0(文件上传用 -F multipart/form-data,JSON 接口用 -d)
  • 测试文件:test1.pdf(ReportLab 生成、2 页、有文本层)、scanned.pdf(扫描样式件)
  • 管理账号:admin(角色 ROLE_ADMIN,界面生成 API Key)
  • 接口清单来源:服务端 OpenAPI 描述("Stirling PDF API",openapi 3.0.3,实测共 319 个端点),在线文档 https://registry.scalar.com/@stirlingpdf/apis/stirling-pdf-processing-api/

二、前置条件

动手前确认三件事:

  1. 服务已部署并可达(验证:curl http://192.168.146.128:8129/api/v1/info/status 返回 {"version":"3.2.0","status":"UP"},该端点免认证,部署方法见部署篇);
  2. 拥有 admin 账号与密码(本文实测用 admin / q1w2e3r4;首次登录后请修改,修改后以新密码调用登录接口);
  3. metrics.enabled 为默认值 true(信息类接口可用性受该开关控制,关闭时 /api/v1/info/uptime 等返回 403,排查见配置篇 3.3 节实测)。

三、API 总览

3.1 协议与风格

  • 协议与风格:HTTP + REST,端点前缀 /api/v1;文件处理类端点全部为 POST multipart/form-data(字段名 fileInput 传文件),信息/会话类端点为 GET 或 POST application/json;
  • 响应两类:处理类端点直接回传二进制文件流(application/pdf、application/octet-stream、text/markdown 等,按 Content-Disposition 重命名);信息类端点回传 JSON 或纯文本;
  • 版本:API 路径版本 v1;应用版本经 GET /api/v1/info/status 的 version 字段核实;
  • 本地接口文档:服务内置 Swagger UI 页面 http://192.168.146.128:8129/swagger-ui/index.html(实测 200);完整 OpenAPI 3.0.3 描述文档以官方 Scalar 站点为准(本机 schema 端点被前端页面路由接管,实测 /v3/api-docs 返回网页而非 JSON)。

3.2 认证方式约定

方式凭据适用
免认证无少数探测端点(/api/v1/info/status)
会话 JWTAuthorization: Bearer <access_token>登录接口换取令牌后的全部接口
API KeyX-API-KEY: <apiKey>服务间集成(每用户一枚,界面生成或接口签发)

3.3 端点分组全景

服务端 OpenAPI 描述实测共 319 个端点、28 个分组,分组与规模如下(数量为实测统计):

分组数量分组数量分组数量
convert(格式转换)33security(加密签名)31admin(管理)29
misc(压缩/提取等杂项)29general(页面级操作)27proprietary(付费功能)24
user(用户/密钥)18storage(文件存储)16form(表单)14
info(服务信息)12processing-folders(流水线目录)9analysis(分析)8
ui-data(界面数据)8audit(审计)6filter(筛选)6
invite(邀请)6team(团队)6workflow(工作流)6
auth(认证会话)9database(数据库管理)4ownership(所有权)4
ai(AI 问答)3integration/integrations(集成)3+3docparse(文档理解)2
pipeline(流水线执行)1usage(用量)1其他1

关键解读:日常集成的核心是 general(页面操作)+ convert(转换)+ security(加密签名)+ misc(压缩提取) 四组(第五章 5.4–5.7 节逐个实测);proprietary 组 24 个端点为 Pro/Enterprise 许可功能,社区版调用返回 403(实测见 5.8 节)。

四、认证与首次调用

4.1 无凭据调用:401 行为

除 info/status 等探测端点外,未认证请求一律 401:

curl -s -w '\nHTTP %{http_code}\n' http://192.168.146.128:8129/api/v1/info/health

实测回显:

{"error":"Authentication required"}
HTTP 401

4.2 登录换取会话 JWT

curl -s -X POST http://192.168.146.128:8129/api/v1/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"username":"admin","password":"q1w2e3r4"}'

实测回显(节选,token 中部截断;expires_in 单位秒,即 1 天):

{"session":{"expires_in":86400,"access_token":"eyJraWQiOiJqd3Qta2V5LTIwMjYtMTAtMjItMDA1MzEyLTFjZDY3MzdmIiwiYWxnIjoiUlMyNTYifQ.eyJyb2xlIjoiUk9MRV9BRE1JTiIsImF1dGhUeXBlIjoiV0VCIiwic3ViIjoiYWRtaW4iLCJpc3MiOiJodHRwczovL3N0aXJsaW5nLmNvbSIsImlhdCI6MTc5MjYzMDQ1NiwiZXhwIjoxNzkyNzE2ODU2fQ.…(截断)…"},"user":{"role":"ROLE_ADMIN","team":{"name":"Default","id":1},"enabled":true,"orgOwner":true,"id":1,"username":"admin","email":"admin"}}

用 JWT 调 GET /api/v1/auth/me 核实身份:

curl -s -H "Authorization: Bearer $TOKEN" http://192.168.146.128:8129/api/v1/auth/me

实测回显:

{"user":{"role":"ROLE_ADMIN","portalAccess":true,"team":{"name":"Default","id":1},"enabled":true,"orgOwner":true,"user_metadata":{"firstLogin":false},"loginLandingView":"editor","id":1,"authenticationType":"web","email":"admin","teamLead":false,"username":"admin","app_metadata":{"provider":"web"}}}

💡 说明:access_token 是 RS256 签名的 JWT(payload 实测含 role: ROLE_ADMIN、iss: https://stirling.com、exp 等),有效期 1440 分钟(配置项 security.jwt.tokenExpiryMinutes,见配置篇 5.2 节)。

4.3 签发 API Key(服务间集成推荐)

每用户一枚 API Key,接口签发(需先持有会话 JWT 或界面操作):

curl -s -X POST -H "Authorization: Bearer $TOKEN" http://192.168.146.128:8129/api/v1/user/get-api-key

实测回显:

{"apiKey":"b07d2b3a-ffda-4893-a32d-196881535fc2"}

之后所有请求用 X-API-KEY 头即可,等效于该用户身份(重复调 get-api-key 返回同一枚;update-api-key 轮换新值):

curl -s -H "X-API-KEY: b07d2b3a-ffda-4893-a32d-196881535fc2" http://192.168.146.128:8129/api/v1/auth/me

实测回显(与 JWT 方式完全一致的用户信息):

{"user":{"role":"ROLE_ADMIN","portalAccess":true,"team":{"name":"Default","id":1},"enabled":true,"orgOwner":true,"user_metadata":{"firstLogin":false},"loginLandingView":"editor","id":1,"authenticationType":"web","email":"admin","teamLead":false,"username":"admin","app_metadata":{"provider":"web"}}}

⚠️ 注意:API Key 是长期凭据,泄露后在界面「账户设置」或经 update-api-key 立即轮换;本文的 Key 仅为实测演示值。

五、各 API 域详解

5.1 info 域:服务状态与运行信息

GET /api/v1/info/status(免认证,健康检查同款端点):

curl -s http://192.168.146.128:8129/api/v1/info/status

实测回显:

{"version":"3.2.0","status":"UP"}

GET /api/v1/info/uptime(需认证;服务运行时长,纯文本):

curl -s -H "X-API-KEY: b07d2b3a-ffda-4893-a32d-196881535fc2" http://192.168.146.128:8129/api/v1/info/uptime

实测回显(重启后 32 秒):

0d 0h 0m 32s

GET /api/v1/info/load(需认证;系统负载,纯文本单值):

curl -s -H "X-API-KEY: b07d2b3a-ffda-4893-a32d-196881535fc2" http://192.168.146.128:8129/api/v1/info/load

实测回显:

0.0

💡 说明:info 域共 12 个端点(status/health/uptime/load/requests 等,清单见第三章);除 status 外都受 metrics.enabled 开关与认证控制。

5.2 general 域:页面级操作

POST /api/v1/general/merge-pdfs(合并多个 PDF;multipart 字段 fileInput 重复传多份):

curl -s -o merged.pdf -w 'HTTP %{http_code}  type=%{content_type}  size=%{size_download}\n' \
  -H "X-API-KEY: b07d2b3a-ffda-4893-a32d-196881535fc2" \
  -F "fileInput=@test1.pdf" -F "fileInput=@scanned.pdf" \
  http://192.168.146.128:8129/api/v1/general/merge-pdfs
file merged.pdf

实测回显:

HTTP 200  type=application/pdf  size=39026
merged.pdf: PDF document, version 1.5, 4 pages

POST /api/v1/general/rotate-pdf(旋转页面;参数 fileInput + angle,实测响应头):

curl -s -D - -o rotated.pdf -H "X-API-KEY: b07d2b3a-ffda-4893-a32d-196881535fc2" \
  -F "fileInput=@test1.pdf" -F "angle=90" \
  http://192.168.146.128:8129/api/v1/general/rotate-pdf

实测回显(响应头节选 + 落盘验证):

HTTP/1.1 200 OK
Cache-Control: private, no-store
Content-Type: application/pdf
Content-Disposition: form-data; name="attachment"; filename="test1_rotated.pdf"
X-Frame-Options: DENY
Content-Length: 1469
rotated.pdf: PDF document, version 1.6

要点解读:输出文件名按「原名_操作后缀.pdf」规则重命名(test1.pdf → test1_rotated.pdf),客户端落盘时应取响应头 Content-Disposition 中的 filename 而不是自造名字。

POST /api/v1/general/split-pages(按页码区间拆分;返回 zip 包):

curl -s -o split.zip -w 'HTTP %{http_code}  %{content_type}  %{size_download}\n' \
  -H "X-API-KEY: b07d2b3a-ffda-4893-a32d-196881535fc2" \
  -F "fileInput=@test1.pdf" -F "pageNumbers=1-2" \
  http://192.168.146.128:8129/api/v1/general/split-pages
file split.zip

实测回显:

HTTP 200  application/octet-stream  1585
split.zip: Zip archive data, at least v2.0 to extract, compression method=deflate

POST /api/v1/general/remove-pages(删除指定页;参数 fileInput + pageNumbers):

curl -s -o removed.pdf -w 'HTTP %{http_code}  %{content_type}  %{size_download}\n' \
  -H "X-API-KEY: b07d2b3a-ffda-4893-a32d-196881535fc2" \
  -F "fileInput=@test1.pdf" -F "pageNumbers=1" \
  http://192.168.146.128:8129/api/v1/general/remove-pages

实测回显:

HTTP 200  application/pdf  1160

5.3 security 域:加密与解密

POST /api/v1/security/add-password(加密;核心参数 fileInput、password(打开密码)、ownerPassword(权限密码)、keyLength(默认 256)):

curl -s -o locked.pdf -w 'HTTP %{http_code}  %{content_type}  %{size_download}\n' \
  -H "X-API-KEY: b07d2b3a-ffda-4893-a32d-196881535fc2" \
  -F "fileInput=@test1.pdf" -F "password=q1w2e3r4" -F "ownerPassword=q1w2e3r4" \
  http://192.168.146.128:8129/api/v1/security/add-password

实测回显:

HTTP 200  application/pdf  2085
locked.pdf: PDF document, version 1.6

POST /api/v1/security/remove-password(解除加密;传原密码):

curl -s -o unlocked.pdf -w 'HTTP %{http_code}  %{content_type}  %{size_download}\n' \
  -H "X-API-KEY: b07d2b3a-ffda-4893-a32d-196881535fc2" \
  -F "fileInput=@locked.pdf" -F "password=q1w2e3r4" \
  http://192.168.146.128:8129/api/v1/security/remove-password

实测回显:

HTTP 200  application/pdf  1473
unlocked.pdf: PDF document, version 1.6

POST /api/v1/security/add-cert-sign 等证书签名、时间戳端点同在 security 组(共 31 个端点),参数结构与 add-password 同风格(AddPasswordRequest 实测 schema 含 preventPrinting、preventModify 等 11 个权限布尔项)。

5.4 convert 域:格式转换

POST /api/v1/convert/pdf/text(PDF → 纯文本;⚠️ 实测发现 outputFormat 为必填,缺省直接 400——见 5.8 节排障实录):

curl -s -o out.txt -w 'HTTP %{http_code}  %{size_download}\n' \
  -H "X-API-KEY: b07d2b3a-ffda-4893-a32d-196881535fc2" \
  -F "fileInput=@test1.pdf" -F "outputFormat=txt" \
  http://192.168.146.128:8129/api/v1/convert/pdf/text
cat out.txt

实测回显:

HTTP 200  158
Stirling-PDF Test File 1
Page 1 of test document 1 - generated for API testing
Stirling-PDF Test File 1
Page 2 of test document 1 - generated for API testing

POST /api/v1/convert/pdf/markdown(PDF → Markdown,中文文档实测):

curl -s -o out.md -w 'HTTP %{http_code}  %{content_type}  %{size_download}\n' \
  -H "X-API-KEY: b07d2b3a-ffda-4893-a32d-196881535fc2" \
  -F "fileInput=@report.pdf" \
  http://192.168.146.128:8129/api/v1/convert/pdf/markdown
head out.md

实测回显(对一份含中文标题/列表的「备份任务记录」文档;首行节选):

HTTP 200  text/markdown  166
# 备份任务记录

- 每日 00:00 自动导出数据库
- 校验和已生成

convert 组共 33 个端点覆盖 pdf/text|markdown|html|word|xlsx|csv|epub|img|pdfa 等双向转换(URL→PDF 端点存在但服务端默认禁用,配置见配置篇 5.3 节)。

5.5 misc 域:压缩与提取

POST /api/v1/misc/compress-pdf(压缩;参数 fileInput、optimizeLevel(整数档位)、grayscale 等):

curl -s -o compressed.pdf -w 'HTTP %{http_code}  %{size_download}\n' \
  -H "X-API-KEY: b07d2b3a-ffda-4893-a32d-196881535fc2" \
  -F "fileInput=@scanned.pdf" \
  http://192.168.146.128:8129/api/v1/misc/compress-pdf

实测回显(默认档位压缩扫描样式件):

HTTP 200  25366

misc 组另含 extract-images(提取图片,返回 zip)、ocr-pdf(Tesseract OCR,参数含 languages 语言代码如 eng/chi_sim)等 29 个端点。

5.6 错误响应结构与错误码

所有结构化错误遵循统一 JSON 结构(detail 描述、instance/path 端点、status、title、type 错误类型路径、timestamp,部分带 hints 修复建议与 errorCode)。实测各场景:

参数校验错误(rotate 传非 90 倍数角度):

{"detail":"Angle must be a multiple of 90","instance":"/api/v1/general/rotate-pdf","status":400,"title":"Invalid Argument","type":"/errors/invalid-argument","timestamp":"2026-10-22T00:58:12.669751372Z","path":"/api/v1/general/rotate-pdf","title":"Invalid Argument","hints":["Review the error message and adjust the parameter value.","Consult the API docs for accepted ranges and formats.","Ensure required parameters are present."],"actionRequired":"Correct the invalid argument and retry."}

受密码保护文件未提供/密码错(remove-pages 遇到加密件,errorCode E004):

{"detail":"The PDF Document is passworded and either the password was not provided or was incorrect","instance":"/api/v1/general/remove-pages","status":400,"title":"PDF Password Required","type":"/errors/pdf-password","timestamp":"2026-10-22T00:57:59.661486988Z","path":"/api/v1/general/remove-pages","title":"PDF Password Required","errorCode":"E004"}

端点不存在(404):

{"detail":"No endpoint found for GET /api/v1/no/such/endpoint","instance":"/api/v1/no/such/endpoint","status":404,"title":"Endpoint Not Found","type":"/errors/not-found","timestamp":"2026-10-22T00:57:00.810937570Z","path":"/api/v1/no/such/endpoint","title":"Endpoint Not Found","method":"GET","hints":["Verify the URL path and HTTP method are correct.",...]}

方法不允许(405,对 GET-only 端点 POST):

{"detail":"HTTP method POST is not supported for this endpoint. Supported methods: GET","instance":"/login","status":405,"title":"HTTP Method Not Allowed","type":"/errors/method-not-allowed","timestamp":"2026-10-22T00:49:11.098460849Z","path":"/login","title":"HTTP Method Not Allowed","method":"POST","supportedMethods":["GET"],...}

付费功能越权(社区版调 Enterprise 端点):

curl -s -H "X-API-KEY: b07d2b3a-ffda-4893-a32d-196881535fc2" 'http://192.168.146.128:8129/api/v1/audit/data?page=0&size=2'

实测回显:

{"detail":"This endpoint requires an Enterprise license","instance":"/api/v1/audit/data","status":403,"title":"Forbidden","type":"/errors/403","timestamp":"2026-10-22T02:29:18.092617205Z","path":"/api/v1/audit/data","title":"Forbidden"}

信息接口被配置关闭(metrics.enabled: false 时):

This endpoint is disabled.
HTTP 403

关键解读:排障先看 detail 与 errorCode(如 E004=PDF 密码问题);403 有两类根因——许可(requires an Enterprise license)与配置开关(This endpoint is disabled.),按文案区分。

六、通用约定:文件响应、分页与重试

文件响应约定(处理类端点实测归纳):

  • 成功一律 HTTP 200 + 二进制流;Content-Type 随产物类型(application/pdf/application/octet-stream/text/markdown…);
  • Content-Disposition 形如 form-data; name="attachment"; filename="<原名>_<操作>.pdf",多文件产物(拆分、提取图片)为 zip 包(application/octet-stream);
  • 响应默认带 Cache-Control: private, no-store、X-Frame-Options: DENY、X-Content-Type-Options: nosniff 安全头,客户端不应缓存产物。

分页与过滤约定(实测 OpenAPI 参数清单):管理类列表端点用 page + size(如 GET /api/v1/audit/data?page=0&size=2),付费侧 ui-data 组用 page/pageSize/limit;文件处理类端点无分页概念(单请求单任务)。社区版调用 audit 分页端点受许可限制(5.6 节实测 403)。

幂等与重试:处理类端点天然幂等(同一输入产出同一结果),网络超时后直接重发即可;大文件转换(OCR/压缩)耗时可达数十秒,curl 侧建议 -m 300 设超时上限,服务端对应超时由配置篇 5.5 节 processExecutor.timeoutMinutes.* 管理(默认 20–30 分钟)。

七、常见问题

Q1:处理端点返回 400 且响应体为空?

最常见是必填 multipart 参数缺失。实测案例:/api/v1/convert/pdf/text 不带 outputFormat 时稳定返回 400、0 字节(5.4 节补齐 outputFormat=txt 后 200)。逐个端点核对必填字段(文件 fileInput、输出格式、页码等),参数名以 OpenAPI 描述为准。

Q2:401 {"error":"Authentication required"} 但确实带了 Key?

三步排查:头名是否精确为 X-API-KEY(全大写连字符);Key 是否被轮换过(update-api-key 后旧值立即失效);端点是否本身要求会话而非 API Key(管理类 admin 域端点部分仅限会话登录)。

Q3:合并/转换大文件超时或 502?

自托管直连服务时先确认是服务端慢还是中间有反代超时:直连 curl 同请求复测;服务端处理耗时受 processExecutor.timeoutMinutes.*(默认 30 分钟)约束,一般不会先超时。经 Nginx 等反代时同步调大 proxy_read_timeout 与 client_max_body_size(上传限制另见配置篇 Q5)。

Q4:This endpoint is disabled.(403)?

metrics.enabled: false 所致(配置篇 3.3 节实测:uptime/load/requests 等信息接口 403、status 保留)。改回 true 并重启服务。

Q5:This endpoint requires an Enterprise license(403)?

调用了付费端点(audit 导出、S3 存储配置、集群管理等 proprietary/enterprise 能力)。社区版在免费四组(general/convert/security/misc)+ info/auth/user 内完成绝大多数集成。

Q6:响应文件名乱码或不对?

取 Content-Disposition 的 filename 值(服务端按「原名_操作后缀」生成);curl -o 自定义文件名不影响该头,用 curl -w '%{filename_effective}' 配合 --remote-name 可自动采用服务端命名。

八、参考链接