Document parsing and data extraction
To turn contracts, invoices and statements into structured data, send images and scans to a model that can read images, extract the text of PDFs and Word files that have a text layer and send it to a text model, ask both for JSON, and validate the fields in code. For 10,000 scanned pages plus 5,000 text contracts a month, at live prices in each model’s cheapest group, model costs are about ¥178 a month.
Prices updated Nov 12, 2026, 8:17 PM|
01
The business problem
Finance, legal and supply-chain teams copy data from invoices, contracts, statements and packing lists into their systems every day. With many layouts, OCR-plus-rules setups are hard to maintain, and a supplier changing its template means reconfiguring.
A vision-capable LLM reads invoices and tables in any layout and returns JSON with the field names you give it. It can also misread, skip fields or guess values it cannot see, so results must be validated before they reach your database.
The reliable approach is a fixed field list per document type, a model that outputs exactly that list, code that checks formats and totals, and human review for anything that fails.
02
Recommended models
Images and scans (vision model)
gemini-3.8-flashGoogleinput ¥1.5 / output ¥7.5 per 1M tokens· Standard groupReads invoice photos, scanned contracts and table screenshots and returns the fields as JSON. Images count as input tokens, and higher resolution means more tokens, so scale images down to the point where text is still clearly readable.
Text documents (chat model)
deepseek-v4.1-flashDeepSeekinput ¥2.8 / output ¥11.2 per 1M tokens· China Models groupFor PDFs with a text layer, Word files and email bodies, extract the text in code and let a text model pull out clauses, dates and amounts. No vision needed, a lower price, and a good fit for long contracts.
Recommended models are picked from the live catalog; prices are each model's cheapest group, updated Nov 12, 2026, 8:17 PM. Click a model to see every group's price.
03
Reference architecture
- 1
Classify and preprocess
Identify the document type (invoice, contract, statement). Take the text layer where there is one; convert scans and photos to images at a sensible resolution.
- 2
Define the fields
Write a field list per document type with names, types and formats (dates as YYYY-MM-DD, amounts as numbers) and put it in the prompt.
- 3
Call the model
Images go to the vision model and text to the text model, with temperature 0 and response_format asking for JSON. Some models do not support response_format; ask for JSON only in the prompt and cut the JSON out in code.
- 4
Validate in code
Check required fields and date and amount formats; for invoices, check that line items add up to the total; for contracts, check the order of dates. Retry once on failure.
- 5
Human review
Documents that still fail, or exceed an amount threshold, go to a review queue. Write reviewed results back, and add typical mistakes to the prompt as examples.
04
Cost estimate
Assumptions: 10,000 invoice and scan pages a month at about 2,000 input tokens each (about 1,500 for the image, 500 for instructions) and 400 output tokens; plus 5,000 contracts with a text layer at about 6,000 input and 600 output tokens each.
| Item | Model | Usage assumed | Cost |
|---|---|---|---|
| Scans and images | gemini-3.8-flash | 10,000 × (2,000 input + 400 output tokens) | ¥60 |
| Text contracts | deepseek-v4.1-flash | 5,000 × (6,000 input + 600 output tokens) | ¥117.6 |
| Total (per month) | ¥177.6 | ||
Calculated from live prices in each model's cheapest group, updated Nov 12, 2026, 8:17 PM.
Model usage only: preprocessing, storage and human review are not included. How many tokens an image becomes depends on the model and resolution; the table assumes about 1,500 per page, and the usage field in each response is what you are billed on. Twice the pages means roughly twice the cost.
05
Sample code
Encode the image as base64 in image_url, ask for the listed fields as JSON, then check that every field is present.
import base64
import json
import os
from openai import OpenAI
client = OpenAI(base_url="https://open.tutujin.com/v1", api_key=os.environ["TUTUJIN_API_KEY"])
FIELDS = ["invoice_number", "issue_date", "seller_name", "buyer_name", "total_amount", "currency"]
PROMPT = (
"Extract these fields from the invoice image and reply with one JSON object only: "
+ ", ".join(FIELDS)
+ ". Use null for anything you cannot read. Dates as YYYY-MM-DD, amounts as numbers."
)
def parse_json(text):
"""Some models add text around the JSON; keep the outermost object."""
return json.loads(text[text.find("{"): text.rfind("}") + 1])
def extract(path):
with open(path, "rb") as f:
data_url = "data:image/png;base64," + base64.b64encode(f.read()).decode()
resp = client.chat.completions.create(
model="gemini-3.8-flash",
messages=[{
"role": "user",
"content": [
{"type": "text", "text": PROMPT},
{"type": "image_url", "image_url": {"url": data_url}},
],
}],
response_format={"type": "json_object"},
temperature=0,
)
record = parse_json(resp.choices[0].message.content)
missing = [k for k in FIELDS if k not in record]
if missing:
raise ValueError("missing fields: " + ", ".join(missing))
return record
print(extract("invoice.png"))
# Text-layer PDFs and Word files: extract the text yourself and send it to deepseek-v4.1-flash with the same field list.FAQ
What does processing 15,000 documents a month cost?
With 10,000 scanned pages at about 2,000 input tokens each plus 5,000 text contracts at about 6,000 input tokens each, the cheapest groups come to about ¥178 a month. Image resolution and contract length matter most.
Is the extracted data guaranteed to be correct?
No. Models can misread blurry digits or miss fields, so validate the output in code (formats, totals, required fields) and keep human review for important documents.
Can I send a PDF directly?
For PDFs with a text layer, extract the text in code and send it to a text model, which costs less. Convert scanned PDFs to images and send one page per image to the vision model.
What if the model does not return JSON?
Use response_format to ask for JSON where it is supported. Otherwise say "JSON only" in the prompt, cut out everything from the first opening brace to the last closing brace in code, and retry if parsing fails.
Can I process contracts with sensitive information?
Requests are forwarded through this service to the model provider for processing. Redact personal data and trade secrets such as ID and bank account numbers before sending, and assess the setup against your own data-compliance rules.