Thai OCR for invoices and receipts
Read Thai tax invoices, receipts, and supplier bills into Markdown or typed blocks, and post the fields to your ledger without keying them.
What comes back
# ใบกำกับภาษี / TAX INVOICE
**ผู้ขาย** บริษัท ศตวรรษการค้า จำกัด (สำนักงานใหญ่)
เลขประจำตัวผู้เสียภาษีอากร 0 1055 64078 12 3
| รายการ | จำนวน | หน่วยละ | จำนวนเงิน |
| --- | ---: | ---: | ---: |
| กระดาษถ่ายเอกสาร A4 80 แกรม | ๒๐ | 105.00 | 2,100.00 |
| หมึกพิมพ์ดำ | ๔ | 890.00 | 3,560.00 |
รวมเป็นเงิน 5,660.00
ภาษีมูลค่าเพิ่ม 7% 396.20
**จำนวนเงินรวมทั้งสิ้น 6,056.20**heading ร้านกาแฟบ้านสวน
paragraph เลขประจำตัวผู้เสียภาษี 0 9955 12034 55 1
table 3 rows x 3 columns
paragraph รวม 245.00 บาท
paragraph เงินสด 300.00 ทอน 55.00## หน้า 1
ใบแจ้งยอดประจำเดือน มิถุนายน ๒๕๖๙
## หน้า 2
| วันที่ | เลขที่เอกสาร | จำนวนเงิน |
| --- | --- | ---: |
| ๐๓/๐๖/๖๙ | INV-24418 | 12,400.00 |
| ๑๗/๐๖/๖๙ | INV-24506 | 8,150.00 |The reads above are illustrations of shape, not captured responses. The OCR page carries the model's own witnessed read of a real specimen, with its capture date.
How it fits your pipeline
A Thai tax invoice is not a free-form document. Section 86/4 of the Revenue Code fixes what a full one has to carry: the words tax invoice, the seller's name, address, and taxpayer number, the buyer's name and address, a serial number, a description of what was supplied, the VAT amount stated separately, and the date of issue. It also requires the document to be in Thai, in baht, in Thai or Arabic numerals. That is good news for a reader and bad news for a regular expression, because the same eight facts land in a different place on every supplier's template, in a script with no spaces between words.
Sources:Revenue Code, s. 86/4(opens in a new tab)
- Send the document as it arrivedBase64 the PDF or the photo and post it. The format is read from the bytes, so a scan, a phone photo, and a supplier's PDF all take the same call, up to 50 pages and 10 MB.
- Choose the shape your ledger wantsMarkdown keeps the document readable for a human reviewer. Structured output returns typed blocks, so a posting rule can read the line-item table without parsing the prose around it.
- Post the fields, keep the pageStore the returned text beside the original file. The read is per page, so a re-read after a mapping change costs the same as the first one.
What the statute fixes, and what it does not
1 the words ใบกำกับภาษี, prominently
2 seller name, address, taxpayer number
3 buyer name and address
4 serial number (book number, if any)
5 description, type, quantity, value
6 VAT amount, stated separately
7 date of issuance
8 further particulars as prescribed
not fixed: layout, order on the page, template,
label wording, numeral system per fieldThe law fixes the facts a full tax invoice carries and leaves the design to the seller, which is why field extraction is a reading problem rather than a template problem. Section 86/6 provides for a shorter abbreviated invoice used in retail.
A month of accounts payable, priced
| Workload | Pages | Credits |
|---|---|---|
| One invoice | 1 | 6.5 |
| One batch, at the 50-page ceiling | 50 | 325 |
| 2,000 invoices a month | 13,000 credits | |
Billing is per page at 6.5 credits, with no per-request minimum beyond the single page, so a one-page receipt costs exactly one page.
What comes back, and what stays yours
- The read
- Every line that is legible on the page, in the document's own order.
- The shape
- Markdown for a reviewer, or typed blocks for a posting rule that wants the table on its own.
- The mapping
- Which block is a supplier and which is a total is your code's decision, because your chart of accounts is where that answer lives.
The read stops where your ledger begins, which is why nothing here is a mapping to anyone's field names.
For developers
One request per document, Markdown by default; the structured variant returns the same read as typed blocks.
# Encode without line wrapping: wrapped base64 breaks the JSON string.
DOC=$(base64 < invoice.pdf | tr -d '\n')
# --max-time covers a multi-page document; curl defaults to no limit.
curl -X POST https://api.paxalabs.com/v1/ocr \
--max-time 300 \
-H "Authorization: Bearer $PAXA_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"document\": \"$DOC\", \"model\": \"paxa-ocr-lite-v1\"}"DOC=$(base64 < invoice.pdf | tr -d '\n')
curl -X POST https://api.paxalabs.com/v1/ocr \
--max-time 300 \
-H "Authorization: Bearer $PAXA_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"document\": \"$DOC\", \"model\": \"paxa-ocr-lite-v1\", \"output\": \"structured\"}"import base64
import os
import requests
with open("invoice.pdf", "rb") as file:
document = base64.b64encode(file.read()).decode()
response = requests.post(
"https://api.paxalabs.com/v1/ocr",
headers={"Authorization": f"Bearer {os.environ['PAXA_API_KEY']}"},
json={"document": document, "model": "paxa-ocr-lite-v1"},
# A multi-page document can run for minutes; give it room.
timeout=300,
)
response.raise_for_status()
body = response.json()
print(body["pages"][0]["markdown"], body["usage"]["credits"], "credits")Thai numerals and Thai spacing are the same problem twice
A tax invoice may state its quantities in Thai numerals and its money in Arabic ones, on the same line, because the statute permits either. Thai also writes without spaces between words, so a supplier name, an address, and a product description arrive as one run of characters that only a reader who knows Thai can break correctly. Both are ordinary for a Thai reader and both are where a template-matching extractor stops being reliable.
Common questions
- Do you return the fields, or the text?
- The text, in the shape you asked for. Markdown preserves the document's structure, and structured output returns typed blocks: headings, paragraphs, lists, tables, and figures, with the content in typed fields. Mapping a block to your ledger's field names stays in your code, where your chart of accounts lives.
- How accurate is it on Thai invoices?
- We do not publish accuracy figures, and we will not until the lab publishes its evaluations. Run your own documents through the playground or the free credits and judge the reads against what you actually receive.
- What happens with a photo taken on a phone?
- Send it. PNG, JPEG, and WebP are read as one page each, and the format is detected from the bytes rather than a declared field, so a photo and a scan take the same call.
- Can it read an abbreviated tax invoice?
- Yes. Section 86/6 provides for a shorter form used in retail, which carries fewer particulars than a full invoice. The read is the same call; what changes is how many of the eight fields are on the page to find.
- What does a failed read cost?
- Nothing. The page count is read before the charge, and a provider failure after it refunds automatically. A document we cannot read at all is refused before any charge, with a stable error code.
Start with free credits
Sign in with Google, GitHub, or Hugging Face and spend 100 one-time free credits on your own text.