space ocr
指南文章价格文档

发票 OCR API/送货单 OCR 转 CSV ── 发票数据提取 API 实战指南

把发票、送货单从手工录入和 Excel 乱码中解放出来的开发者指南。声明要提取的字段后向 POST /ocr/fields 上传图片,即可返回交易方、日期、合计、明细等结构化数据,每个值都带有原图坐标(box、quad),需要复核的字段则汇总在 review 列表里。含 curl/Python 代码、CSV 导出、Webhook 与价格。

你是不是到现在还在手动把发票、送货单一个个敲进 Excel?日期、交易方、税前、税后,还有明细的每一行 ── 一到月底,就对着堆成山的纸张较劲,把数字一格一格地誊抄。中途某一位数字错了位,合计对不上,又得从头核对一遍。这些时间,真希望能省下来。

想复制扫描出来的 PDF,结果文字根本选不中。拿去做 OCR,明细又全挤进了一个单元格,换行和列全没了。用 Excel 打开 CSV,又是一片乱码,品名压根读不出来。明明只想导进财务软件,却总是卡在这最后一步 ── 这就是天天和单据打交道的人都熟悉的「老大难」。

本文是一份开发者指南,教你把这些活儿换成一个 API。向 POST /ocr/fields 上传发票、送货单的图片,交易方、日期、合计等字段,以及明细的每一行,都会以带类型的结构化数据返回。更关键的是,返回的每一个值都附带它在原图中被识别出来的坐标(box、quad),所以你不必盲目相信提取结果,而是可以拿原件逐一比对校验。下面配合 curl 和 Python 代码,从最快上手到生产环境,完整走一遍。

先上手试试 ── 无需上传,10 秒体验

写代码之前,先看看实际输出。下面是解析一张真实小票的结果。把光标移到某个字段上,就会高亮出这个值是从图片的哪个位置读出来的。发票、送货单的表现完全一样 ── 提取出的每一个值都对应到它被读取的那块像素,而没有对上的字段会进入 data.review.flagged,作为需要复核的条目列出来。

Delivery slip with extracted-field bounding boxes
Verified fields
Delivery slip

Each value with a box carries a verified on-page location — in data.cells[path], that is box + 4-point quad + evidence.match_ratio — on a 0–1000 normalized grid (0,0 top-left → 1000,1000 bottom-right), the same shape the live API returns. Hover a field to trace it back to the pixels it came from.

「原图 → 提取表格 → 高亮对应位置 → 导出 CSV」的流程

space ocr 的用法,归根结底就是 4 步。(1) 上传收据、发票、送货单的图片 → (2) 按固定列结构提取成一张表格,一图=一行 → (3) 点击某个值,原图的对应位置就会点亮,方便和原件核对 → (4) 直接导出 CSV 导入财务软件。先从上传一张、看着字段被自动填满开始。

拖入一张发票,具名字段就会自动填好 ── 和 API 返回的是同一份数据,只不过呈现在 UI 上。

鉴权与基础 URL

公开 API 只有一个基础地址 https://api.space-ocr.com ── 没有 /v1 这样的路径版本号。每个请求都用以 spocr_ 开头的密钥,通过 HTTP Bearer Token 进行鉴权。

1
Authorization: Bearer spocr_xxxxxxxxxxxxxxxx

请求头缺失或密钥无效会返回 401(error.code: "invalid_api_key")。403 不是鉴权失败,而是你访问了该密钥权限之外的资源 ── 例如另一把密钥创建的任务。所有响应都带有 X-Request-Id(格式为 req_xxx)请求头,建议记录到日志里,方便日后联系支持时排查。如果想自动生成客户端,可在 GET /openapi.json 获取公开的 OpenAPI 规范。

最快路径 ── 声明你要提取的字段

在 fields 里用 FieldSpec 数组声明想提取的字段 ── 发票是交易方、开票日期、单据号、合计,送货单是送货日期、品名、数量、单价。name 会直接成为响应 JSON 的键,所以可以把公司内部的表结构原样抄进请求里。遇到不清楚有哪些字段的版式,也可以用 autoFields: true 让模型先提议一份 schema。图片既可以传 URL,也可以传纯 base64,并用 imageType 说明是哪一种。

声明本身不会改变提取结果。type(number / integer / date)、pattern、min / max、required 都不会传给模型,所以声不声明,values 返回的读数都一样。声明产生的是两样东西:把同一读数按该类型解析后的 data.normalized 层,以及违反规则时立起的复核理由(type_mismatch、out_of_range、pattern_mismatch、missing)。明细这类重复行不要去数行数,用 type: "array" 加 children 只声明一行的结构 ── 返回几行由页面决定,而且子字段的坐标是在各自的行内解析的,所以每一行都重复出现的「数量」「金额」表头也不会串行。

POST /ocr/fields ── 声明送货单字段后调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
curl -X POST https://api.space-ocr.com/ocr/fields \
  -H "Authorization: Bearer spocr_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "image": "https://example.com/docs/delivery-0831.jpg",
    "imageType": "url",
    "fields": [
      { "name": "customer", "type": "string",
        "description": "交易方(收货方)公司名称",
        "near": ["御中", "様"],
        "not_near": ["登録番号", "TEL", "〒"] },
      { "name": "delivery_no", "type": "string", "required": true,
        "pattern": "^[A-Z]{2}-[0-9]{4,8}$",
        "description": "单据号" },
      { "name": "delivery_date", "type": "date",
        "label": "納品日", "description": "送货日期" },
      { "name": "items", "type": "array",
        "description": "每条明细对应一个元素",
        "children": [
          { "name": "name",       "type": "string" },
          { "name": "qty",        "type": "integer" },
          { "name": "unit_price", "type": "number" },
          { "name": "amount",     "type": "number" }
        ] },
      { "name": "total", "type": "number", "required": true,
        "label": "合計", "description": "合计金额" }
    ]
  }'
Why it matters

请求体参数的正式写法是驼峰式(camelCase)。 请使用 imageType / autoFields。旧的下划线写法(image_type / auto_fields)虽然也能用,但已不推荐。imageType 是必填的,必须明确写成 "url" 或 "base64" ── 系统不会根据值的形态自动判断。另外,fields 内部的属性名(required、label、pattern、near、not_near 等)属于 schema 一侧,请照 API 文档里 FieldSpec 表格的写法书写。

响应的结构 ── 每个值都带「出处」

成功时会返回 { status: "success", data: { ... } }。data 分成几层,业务数据和校验信息不会混在一起。

  • data.values ── 与你声明的 schema 完全一致的纯业务数据。不掺入保留键,可以直接入库。
  • data.cells ── 以 path 为键的扁平坐标/校验映射。用 total 或 items[0].amount 这样的 path 查询,就能拿到该值的 box({ xmin, ymin, xmax, ymax } 轴对齐矩形)、quad(顺着单据倾斜方向的 4 个点)、verified(判定)、review(复核理由)与 evidence(比对证据)。坐标是归一化到 0–1000的整数,换算的基准不是你上传的文件,而是 data.image ── pixel_x = box.xmin / 1000 × data.image.width。
  • data.review ── 单张单据的汇总,以及需要复核的字段清单 flagged。它是 { path, reasons } 的数组,path 与 cells 的键语法相同,可直接查询。数量就是 flagged.length。
  • data.normalized ── 只有在声明了标量类型或 pattern / enum 时才会出现的一层。它是与 values 形状完全相同的树,只有叶子被解析成了该类型。
  • data.image ── 实际读取的那一页的 width / height(像素)。这是应用 EXIF 方向之后的尺寸,所以把坐标叠回图片时要以它为准。

evidence 里的 text_match(字符比对是否通过)和 match_ratio(该值的字符中在页面上找到的比例)是支撑判定的证据。与其自己定一个阈值去遍历所有字段,不如直接把 review.flagged 当作待办清单来用。

POST /ocr/fields → 响应(节选)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
{
  "status": "success",
  "data": {
    "values": {
      "customer": "株式会社サンプル商事",
      "delivery_no": "DN-100482",
      "delivery_date": "令和8年8月31日",
      "items": [
        { "name": "A4 复印纸", "qty": "5", "unit_price": "480", "amount": "2,400" }
      ],
      "total": "2,400"
    },
    "cells": {
      "customer":      { "box": { "xmin": 62, "ymin": 118, "xmax": 384, "ymax": 152 },
                         "quad": [{"x":62,"y":118},{"x":384,"y":118},{"x":384,"y":152},{"x":62,"y":152}],
                         "verified": false,
                         "review": { "reasons": ["near_conflict"] },
                         "evidence": { "text_match": true, "source": "vision_symbol_match",
                                       "match_ratio": 1.0,
                                       "not_near": { "matched": "登録番号", "distance": 0.4 } } },
      "delivery_date": { "box": { "xmin": 612, "ymin": 96, "xmax": 812, "ymax": 124 },
                         "quad": [{"x":612,"y":96},{"x":812,"y":96},{"x":812,"y":124},{"x":612,"y":124}],
                         "verified": true, "review": null,
                         "evidence": { "text_match": true, "source": "vision_symbol_match", "match_ratio": 1.0 },
                         "normalized": { "value": "2026-08-31", "type": "date", "method": "deterministic" } },
      "items[0].qty":  { "box": { "xmin": 512, "ymin": 470, "xmax": 536, "ymax": 496 },
                         "quad": [{"x":512,"y":470},{"x":536,"y":470},{"x":536,"y":496},{"x":512,"y":496}],
                         "verified": true, "review": null,
                         "evidence": { "text_match": true, "source": "token_id", "match_ratio": 1.0 },
                         "normalized": { "value": 5, "type": "integer", "method": "deterministic" } },
      "total":         { "box": { "xmin": 595, "ymin": 974, "xmax": 781, "ymax": 1000 },
                         "quad": [{"x":594,"y":975},{"x":781,"y":972},{"x":781,"y":998},{"x":595,"y":1000}],
                         "verified": true, "review": null,
                         "evidence": { "text_match": true, "source": "vision_symbol_match", "match_ratio": 0.93 },
                         "normalized": { "value": 2400, "type": "number", "method": "deterministic" } }
    },
    "review": {
      "unit": "field",
      "declared": 8,
      "returned": 8,
      "boxed": 8,
      "verified": 7,
      "flagged": [
        { "path": "customer", "reasons": ["near_conflict"] }
      ],
      "by_reason": { "near_conflict": 1 }
    },
    "normalized": {
      "delivery_date": "2026-08-31",
      "items": [ { "qty": 5, "unit_price": 480, "amount": 2400 } ],
      "total": 2400
    },
    "image": { "width": 1654, "height": 2339 }
  }
}
✓ Verified

坐标并不会盲目相信 AI 的一面之词。 语言模型返回的只是每个值的文本,并不返回坐标本身。引擎会把这段文本,与 OCR 在页面上实际检测到的符号逐字符比对 ── 所以矩形会落到这些字符真正出现的像素上。比对是否通过记录在 evidence.text_match,匹配了多少记录在 evidence.match_ratio。单元格的 verified 则位于它们之上,是 review 的镜像判定:只要立起任何一条复核理由就是 false,什么都没立且确实跑了比对就是 true,没有可比对的对象(例如行的并集)就是 null。因此 verified: false 配 text_match: true 并不矛盾,而是「字符对上了,但你声明的规则拦下了它」这种正常组合。不过,两个引擎也可能在同一个误读上达成一致,那个值就会通过 ── 出处校验和你自己的业务规则(required、pattern、enum、near)是互补的两层,生产环境两者都要跑。详情请参阅用边界框让 OCR 可审计的机制。

收件方和开票方,就印在同一页上

日本发票、送货单里最棘手的错误,不是把字读错。而是字读得完全正确,却取自错误的位置。同一张纸上印着两个公司名 ── 收件方和开票方 ── 就算取错了一边,字符比对照样一致,于是以 verified: true 通过。把交易方主数据交给 enum 也分不开,因为两边都是已登记的合法名称。

负责这一层的是 near 和 not_near。对收件方公司名,把「本应印在值旁边的词」声明为 near: ["御中", "様"],把「不该出现在旁边的词」声明为 not_near: ["登録番号", "TEL", "〒"]。如果该值的任何一处出现都不在 near 词的邻域里,就立起 near_mismatch;如果有出现在邻域里、但拿到坐标的是另一处副本,就是 near_ambiguous;如果值就坐在开票方区块的词旁边,则是 near_conflict。判定的具体依据会原样放进 cells[path].evidence.near / evidence.not_near。

当声明的 near 词在整页上都没有印出来时,near 会放弃判定,并把这件事以 issue: "near_unresolved" 记入 review.notes ── 不能因为一张不印「御中」的事务表单就惩罚它。而当事人被张冠李戴,恰恰多发生在这类版式上,能够触及它们的是 not_near。词允许出现在印刷词的哪个位置由 match 指定:默认的 boundary,以及 suffix(御中、様)、prefix(〒、TEL)、standalone、anywhere ── 正是这一项,避免让工程名「中野様邸増築工事」里的「様」成为收件方判定的证人。两种声明都不会传给模型,所以提取到的值不会改变。请把它理解为不是替你挑对,而是让挑错的值显形的机制。

声明字段 schema,把明细写入 CSV(送货单)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import requests, base64, csv

with open("delivery.jpg", "rb") as f:
    b64 = base64.b64encode(f.read()).decode()

resp = requests.post(
    "https://api.space-ocr.com/ocr/fields",
    headers={"Authorization": "Bearer spocr_xxxxxxxxxxxxxxxx"},
    json={
        "image": b64,
        "imageType": "base64",
        "fields": [
            {"name": "customer", "type": "string",
             "description": "交易方(收货方)公司名称",
             "near": ["御中", "様"],
             "not_near": ["登録番号", "TEL", "〒"]},
            {"name": "delivery_no", "type": "string", "required": True,
             "pattern": "^[A-Z]{2}-[0-9]{4,8}$",
             "description": "单据号"},
            {"name": "delivery_date", "type": "date",
             "label": "納品日", "description": "送货日期"},
            {"name": "items", "type": "array",
             "description": "每条明细对应一个元素",
             "children": [
                 {"name": "name", "type": "string", "description": "品名"},
                 {"name": "qty", "type": "integer", "description": "数量"},
                 {"name": "unit_price", "type": "number", "description": "单价"},
                 {"name": "amount", "type": "number", "description": "金额"},
             ]},
            {"name": "total", "type": "number", "required": True,
             "label": "合計", "description": "合计金额"},
        ],
    },
    timeout=200,  # 同步处理上限为 180 秒
)

data = resp.json()["data"]
values = data["values"]
norm_items = data.get("normalized", {}).get("items", [])

# 先取需要复核的字段。数量就是 flagged 的长度
for flag in data["review"]["flagged"]:
    cell = data["cells"].get(flag["path"])
    print(flag["path"], flag["reasons"], cell["box"] if cell else None)

# 明细写入 CSV:展示列用 values,计算列用 normalized
with open("delivery.csv", "w", encoding="utf-8-sig", newline="") as out:
    w = csv.writer(out)
    w.writerow(["品名", "数量", "单价", "金额", "金额(数值)"])
    for i, row in enumerate(values.get("items", [])):
        n = norm_items[i] if i < len(norm_items) else {}
        w.writerow([row["name"], row["qty"], row["unit_price"],
                    row["amount"], n.get("amount")])
Why it matters

values 是模型从页面上读出的字符串,并不是逐字节的复制品。 字符比对会先把全角、括号、空白折叠后再比较,所以 (税抜) 写成 (税抜) 这种程度的差异是能通过的 ── 需要精确匹配时,请使用 cells[path].evidence.printed_text,也就是 OCR 在那个坐标上读到的字符。想按数字或日期处理时,不要去改写 values,而是声明 type 并读取 data.normalized("令和8年8月31日" → "2026-08-31","2,400" → 2400)。解析是确定性的,不会额外调用模型。解析不了的叶子在 normalized 里是 null,原因放在 cells[path].normalized.error。所以在 CSV 里,展示的列取自 values,参与计算的列取自 normalized,这样分开更稳妥。

反过来也要注意:像数量栏的「一式」、付款期限的「翌月末払い」这种本来就会正式印出非数值写法的字段,一旦声明了类型,单据明明没错也会每次都以 type_mismatch 进入复核清单 ── 只给一定是数字或日期的字段声明类型。应对 CSV 乱码的做法是:要用 Excel 打开的 CSV,请用 UTF-8 BOM(utf-8-sig)导出。值里会原样带上半角 ¥(U+00A5)之类的字符,所以请保持 UTF-8,不要转成 cp932 / Shift_JIS。另外,防止明细「被挤进一个单元格」的关键就是 type: "array" + children,它会把一条明细展开成一行。

点一下文字,跳到原图对应位置

数据沉淀到表格之后,点击某个值,原图的对应位置就会点亮。这是批量抽查时最快的办法 ── 不必把整张单据扫一遍,视线直接落到那个位置。你也不需要逐一核对所有字段:只打开 data.review.flagged 里列出的条目 ── 字符没对上、违反了你声明的规则、必填却没有返回 ── 要看的位置由 cells[path] 的坐标直接指出来。

跨已提取的发票做全局检索,命中的单元格 ── 以及它在原图中的对应位置 ── 一键直达。

异步批量处理 ── 批量上传、任务、Webhook

POST /ocr/fields 是同步的,最适合放进请求/响应循环里做单张处理。如果要整批处理一整个发票、送货单文件夹,就对某张表格用 POST /upload(multipart 里重复 files)提交。默认会立即返回一个任务数组。

1
{ "path": "...", "jobs": [ { "uniqueKey": "...", "jobId": "...", "status": "pending" } ] }

获取结果有两种方式:轮询 GET /jobs/{jobId},或者注册 Webhook。Webhook 每个空间一个 URL,所有事件都用 X-Spaceocr-Signature 请求头做 HMAC-SHA256 签名。值得关注的事件有 upload.received、item.created、ocr.completed(提取结果在 data.result 中)、ocr.failed。在信任载荷之前,请务必先校验签名。

幂等性、请求追踪与限流

为了让生产流水线能安全重试,有几个请求头可用。

请求头作用
Idempotency-Key在 /ocr/fields、/create、/upload 上,同一 key 的重发会在 24 小时内回放缓存响应(X-Idempotent-Replay: true)── 既能安全重试又不会重复扣费。
X-Request-Id所有响应都会带(req_xxx)。记录到日志便于支持排查。
X-RateLimit-Remaining当前这一分钟内剩余的调用次数。

限流为每个密钥 60 请求/分钟、每个 uid 600 请求/分钟。超出时会返回 429 和 error.code: "rate_limited",需要等待的秒数放在 Retry-After 响应头里。

429 响应体
1
2
3
4
5
6
7
{
  "error": {
    "code": "rate_limited",
    "message": "Rate limit exceeded",
    "requestId": "req_8fa2c1"
  }
}

从提取,到可查询的表格

把发票提取进表格之后,要回头读取数据时,不必再跑一次 OCR。GET /view 会在已沉淀的行上执行服务端查询 ── where、sort、select、limit、offset ── 既不重跑 OCR,也不计费。坐标默认会一起返回,想轻量化时再加 boxes=0。比如用 where=total>=40000 只看高额发票,用 sort=-invoice_date 按从新到旧排序。从这里导出 CSV(带 UTF-8 BOM,所以 Excel 和 CJK 都能正常打开),就能用于导入财务软件 ── 详情请参阅把扫描单据转成 CSV和把收据转成 CSV。所有端点的规范都汇总在 API 文档里。

Note

PDF 需先把页面转成图片再发送。 OCR 引擎直接解析的是栅格图像(JPEG、PNG、GIF、BMP、TIFF、WebP)。如果直接调用 API,请先把 PDF 的每一页渲染成 PNG 等图片再发送(如果是拖入 Web 应用,应用会自动把页面图像化,所以可以直接丢 PDF 进去)。与 freee、マネーフォワード(Money Forward)、弥生(Yayoi)、kintone 的对接并非官方 API 集成,而是默认通过导入导出的 CSV 来完成。另外,是否符合发票留存制度(インボイス制度)或电子账簿保存法(電子帳簿保存法),请结合各家的运营与要求自行确认(本服务并不保证满足法定要求)。

价格

POST /ocr/fields 是每次调用 $0.05(含税),POST /upload 是 $0.05 × N 张。失败不计费 ── 图片读不出来的 400(invalid_image)和超过同步处理上限的 504 本来就不计费,502 引擎错误和 ocr.failed 事件会自动退款。只读端点(GET /space、/view、/amount、/health)免费。免费额度无需信用卡,每月 100 个额度;Pro 为 $39/月。完整的方案列表见价格页面。

用 API 提取发票、送货单的步骤

  1. 准备 API 密钥
    登录后签发以 spocr_ 开头的 API 密钥,在每个请求里加上 Authorization: Bearer spocr_...。基础 URL 为 https://api.space-ocr.com。
  2. 准备图片(PDF 先把页面图像化)
    把发票、送货单准备成 JPEG/PNG 等栅格图像。如果直接调用 API,PDF 要先把每一页渲染成 PNG 再发送(拖入 Web 应用时应用会自动图像化)。图片可用 URL 或纯 base64 传入,并用 imageType 指定 url / base64。
  3. 调用 POST /ocr/fields
    把要提取的字段用 FieldSpec({name, type, description, required, label, pattern, near, not_near, children})声明在 fields[] 里。明细不要去数行数,用 type:"array" + children 只声明一行的结构,返回几行交给页面决定。对不清楚有哪些字段的版式,也可以用 autoFields: true 让模型提议 schema。
  4. 校验响应
    把 data.review.flagged 里的 {path, reasons} 当作待办清单打开,再用该 path 查 data.cells[path],确认 box、quad 坐标和 evidence(text_match、match_ratio)。声明了类型的字段还会在 data.normalized 里给出解析后的值,解析失败的原因放在 cells[path].normalized.error。
  5. 导出 CSV 导入财务软件
    把提取结果导出为带 UTF-8 BOM 的 CSV(明细作为数组行展开),交给 freee、マネーフォワード(Money Forward)、弥生(Yayoi)等的 CSV 导入。数据沉淀后,可用 GET /view 在不重跑 OCR、不计费的情况下查询。
发票、送货单 OCR API 支持中文吗?
支持。语言完全自动识别,无需指定提示。中文、日文、英文、韩文都由同一个引擎处理,全角/半角、括号之类的写法差异会在字符比对时先折叠再比较。values 里返回的是模型从页面上读出的字符串,所以和历日期、品名都是印刷上的读法。不过它不是逐字节的复制品:需要精确匹配时,请使用 cells[path].evidence.printed_text,也就是 OCR 在那个坐标上读到的字符。想按日期或数字处理,就声明 type,然后从 data.normalized 这一层取确定性的解析结果。
支持 PDF 格式的发票、送货单吗?
支持。不过 OCR 引擎直接解析的是栅格图像(JPEG、PNG、GIF、BMP、TIFF、WebP)。如果直接调用 API,请先把 PDF 的每一页渲染成 PNG 等图片再发送。如果是把 PDF 拖入 Web 应用,应用会自动把页面图像化,所以可以直接丢 PDF 进去做 OCR。
提取出的数据能导入 freee、マネーフォワード(Money Forward)、弥生(Yayoi)吗?
可以通过 CSV 导入。表格能导出为 CSV(为兼容 Excel 与 CJK,带 UTF-8 BOM),明细会作为数组行展开,因此可以交给各家财务软件的 CSV 导入功能。这并非官方 API 集成,而是默认通过导出的 CSV 来导入运作。
提取精度如何保证?结果可信吗?
每个值都有对应的 cells[path]:box(0–1000 归一化网格上的轴对齐矩形)和 quad(顺着页面倾斜的 4 个点)指出它在原图中被读取的位置。坐标不是 AI 凭空生成的,而是把提取出的文本与 OCR 在页面上实际检测到的符号逐字符比对后推导出来的。比对是否通过在 evidence.text_match,匹配了多少在 evidence.match_ratio,单元格的 verified 则是它们之上的判定。需要复核的条目会以 {path, reasons} 的待办清单形式返回在 data.review.flagged 里,你不必自己定阈值再遍历所有字段。不过两个引擎仍可能在同一个误读上达成一致,所以请与 required、pattern、enum、near 这些业务规则一起使用。
个人信息和数据如何处理?失败时如何计费?
失败不计费。图片读不出来的 400 和超过同步处理上限的 504 本来就不计费,502 引擎错误或 ocr.failed 事件会自动退款。Webhook 每个空间一个 URL,所有事件都用 X-Spaceocr-Signature 请求头做 HMAC-SHA256 签名,接收方可以先校验签名再处理。用幂等键(Idempotency-Key)还能防止重试时重复扣费。

一次调用,提取你的第一张发票

免费额度 ── 每月 100 张,无需信用卡。每一个值都附带它在原图中被读取位置的坐标一起返回。

相关