space ocr
指南文章價格文件

發票 OCR API/送貨單 OCR 轉 CSV ── 發票資料擷取 API 實作指南

讓開發者擺脫發票、送貨單手動輸入與 Excel 亂碼的指南。宣告要擷取的欄位後把影像 POST 到 /ocr/fields,即可取得結構化的客戶、日期、合計與明細,每個值都附帶原始影像座標(box、quad),需要複查的欄位則整理在 review 清單裡。內含 curl、Python 程式碼、CSV 輸出、Webhook 與收費說明。

你是不是還在用手把發票、送貨單一格一格敲進 Excel?日期、客戶、未稅、含稅,還有明細的每一行 ── 一到月底就得對著堆積如山的紙張,把數字一格一格謄寫過去。中途差了一位數,合計兜不攏,又得從頭核對一遍。那段時間,真的很想省下來。

想把掃描好的 PDF 複製出來,卻發現文字根本選不起來。丟去 OCR 跑一遍,明細又全部擠進同一個儲存格,換行和欄位都不見了。CSV 用 Excel 一打開又亂碼,品名完全看不懂。明明只是想匯進會計軟體,卻每次都卡在這道關卡前 ── 這就是天天和文件打交道的人最熟悉的「日常」。

這篇文章,就是要教開發者怎麼把這些作業換成一支 API。把發票、送貨單的影像 POST 到 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) 在欄位固定的表單裡,以 1 張=1 列的方式擷取出來 → (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 不是驗證失敗,而是你碰到了該金鑰權限之外的資源 ── 例如另一把金鑰建立的 job。所有回應都會帶上 X-Request-Id(格式 req_xxx)標頭,記進 log 裡,日後向客服詢問時會比較安心。如果想自動產生用戶端,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

請求主體參數的正式名稱是駝峰式命名。 請使用 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 為鍵的扁平座標/驗證 map。用 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,靠它把 1 筆明細展開成 1 列。

點一下文字,跳到原始位置

累積進表單之後,點一下值,原始影像的對應位置就會亮起。這是批次抽查時最快的方法 ── 不必把整份文件看一遍,視線會直接飛到對應位置。你也不需要逐一核對所有欄位:只打開 data.review.flagged 列出的項目 ── 字元沒對上、違反了你宣告的規則、必填卻沒有回傳 ── 要確認的位置就由 cells[path] 的座標直接指出來。

橫跨已擷取的發票進行搜尋,一口氣跳到命中的儲存格 ── 以及它在原始影像中的對應位置。

非同步大量處理 ── 批次上傳、Job、Webhook

POST /ocr/fields 是同步的,最適合放在 request/response 迴圈裡的單張處理。如果要整批處理一整個發票、送貨單的資料夾,就對表單用 POST /upload(重複 multipart 的 files)丟進去。預設會立刻回傳 job 陣列。

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

取得結果有兩種方式。輪詢 GET /jobs/{jobId},或是註冊 Webhook。Webhook 每個 space 一個 URL,所有事件都會用 X-Spaceocr-Signature 標頭做 HMAC-SHA256 簽章。值得關注的事件有 upload.received、item.created、ocr.completed(擷取結果在 data.result)、ocr.failed。在信任 payload 之前,請務必驗證簽章。

冪等性、請求追蹤、速率限制

為了讓正式環境的 pipeline 能安全地重試,有幾個標頭可以用。

標頭作用
Idempotency-Key在 /ocr/fields、/create、/upload,相同金鑰的重送會重播 24 小時內的快取回應(X-Idempotent-Replay: true)── 不會重複計費,可安心重試。
X-Request-Id所有回應都會帶(req_xxx)。記進 log 供客服使用。
X-RateLimit-Remaining這一分鐘內剩下的呼叫次數。

速率限制為每把金鑰 60 次/分、每個 uid 600 次/分。超過時會回傳 429 與 error.code: "rate_limited",要等待的秒數放在 Retry-After 標頭裡。

429 回應 body
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)、弥生(彌生)、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(マネーフォワード)、彌生(弥生)等的 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(マネーフォワード)或彌生(弥生)嗎?
可以透過 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 每個 space 一個 URL,所有事件都會用 X-Spaceocr-Signature 標頭做 HMAC-SHA256 簽章,所以接收端可以先驗證簽章再處理。也可以用冪等金鑰(Idempotency-Key)防止重試時的重複計費。

你的第一張發票,一次呼叫就擷取完成

免費額度 ── 每月 100 張,免信用卡。每一個值,都附帶它是從原始影像哪裡讀出來的座標一併回傳。

相關