Field SOP
Field SOP

Invoice OCR to Excel: Batch Recognition Without Manual Entry

A two-route SOP for turning a stack of invoices into one Excel sheet (division of labor with the site's deepseek-ocr local-deploy piece: that one gets the model running, this one gets batches structured and exported). Route A, Baidu AI Cloud's VAT invoice recognition: the vat_invoice endpoint accepts pdf_file directly, with free quotas of 1,000 calls/month for individuals and 2,000 for enterprises issued monthly, failed calls included in the count, and 2 QPS free / 10 QPS paid (official figures; the community-circulated 5/3 QPS is not adopted). Route B, local PaddleOCR (v3.7.0 with PP-OCRv6, 91,929 stars, Apache-2.0, CPU-runnable), with the key caveat in bold: the open-source edition has no invoice-specific model, so field mapping means writing your own regexes — that engineering gap is the core selection argument between the routes. The full pipeline runs as written: pathlib/glob batch collection, PyMuPDF rendering at 300dpi (with an AGPL/commercial dual-license warning and alternatives), per-file recognition into dicts, and styled xlsx export via pandas/openpyxl. Error playbook covers blur, stamp occlusion, skew, wrong ticket types and QPS throttling; authenticity checks go only through the national VAT invoice verification platform (5 checks per invoice per day, invoices from the last 5 years). Tencent/Alibaba comparisons and a large-model route (GLM-OCR) section both carry explicit sourcing limits, with no marketing accuracy numbers.

Published October 30, 202611 min read
<!-- invoice-ocr-to-excel-sop | sop | Invoice OCR to Excel: Batch Recognition Without Manual Entry -->

Every month-end, the same ritual: a stack of reimbursement invoices on the finance desk — VAT special invoices, general invoices, printed e-invoices, some stamped in red. Someone opens each file, types every field into Excel by hand, and by midnight the total column is still thirty rows short. That work can be handed to a script: point it at a folder, let OCR batch-process every invoice, and end up with one summary spreadsheet. This article walks through two fully reproducible routes, from installing dependencies to exporting an .xlsx file.

One boundary note first: our earlier DeepSeek-OCR deployment tutorial covers getting an OCR model running — it is tool-oriented. This piece covers turning a stack of invoices into one Excel sheet — it is task-oriented, and we will not repeat the deployment details.

Choose Your Route

There are two mainstream approaches: a cloud API route and a local open-source route. One table to see the difference:

DimensionRoute A: Cloud APIRoute B: Local PaddleOCR
Structured fieldsAPI returns named fields directly (invoice number, tax, etc.)The open-source release has no invoice-specific model; you write the field-mapping rules yourself
Data leaves machineImages are uploaded to the vendorNothing leaves your machine
CostBaidu AI Cloud offers a monthly free quotaFree locally, runs on CPU
EffortInstall dependencies and callInstall the stack and write regex rules
Best forDozens to a thousand invoices a month, minimal fussSensitive documents, large volume, willing to maintain a script

Short version: pick A for convenience, B for sensitive data or high volume. A sensible compromise is to prototype with A's free quota, then migrate to B.

Before touching code, organize the files: create an "invoices" folder, save the original PDF e-invoices there, photograph paper invoices flat and away from direct light, and keep invoice dates in the filenames so you can trace missed detections later. No strict layout is required; the scripts below assume that folder plus one output spreadsheet.

Route A: Cloud API in Six Steps

We use Baidu AI Cloud's VAT invoice recognition as the example. The official documentation defines the endpoint as POST https://aip.baidubce.com/rest/2.0/ocr/v1/vat_invoice, accepting exactly one of image, url, pdf_file, or ofd_file, in that priority order. A PDF invoice can be passed directly as pdf_file — no image conversion required. The base64-encoded and urlencoded payload must stay under 8M, image edges between 15px and 4096px; for multi-page PDFs, set pdf_file_num to pick the page, defaulting to page 1.

Step 1: install dependencies. One command covers all four libraries:

text
pip install pymupdf pandas openpyxl requests

One licensing note: PyMuPDF ships under a dual AGPL/commercial license — free for personal projects and AGPL-compatible open source, but closed-source commercial use requires a paid license from the vendor. If this runs inside a company tool, check the licensing boundary first; if that is a concern, switch to pdf2image (which leans on GPL components, so verify that too) or skip local rendering entirely by sending PDFs straight to the cloud API's pdf_file parameter.

Step 2: activate the service and manage keys. Create an OCR application in the Baidu AI Cloud console to get an API Key and Secret Key. Do not hardcode them; put them in environment variables. The official SDK guide says exactly that — never write your AccessKey into project code.

Step 3: exchange the keys for an access_token. Before calling the recognition endpoint, trade the two keys for a token and attach it as a URL parameter:

python
import os, requests

def get_token():
    # Keys live in environment variables, never in source code
    resp = requests.get(
        "https://aip.baidubce.com/oauth/2.0/token",
        params={
            "grant_type": "client_credentials",
            "client_id": os.environ["BAIDU_OCR_API_KEY"],
            "client_secret": os.environ["BAIDU_OCR_SECRET_KEY"],
        },
    )
    return resp.json()["access_token"]

Step 4: confirm invoice types and parameters. With the default type=normal, the endpoint covers VAT general invoices, special invoices, e-invoices (including fully digitalized ones), and blockchain invoices; pass type=roll for roll-type VAT invoices; seal_tag=true adds stamp-detection output. Mind the boundary: fixed-amount invoices, general machine-printed invoices, and medical invoices are outside this endpoint's scope — sending them fails or returns missing fields. For those, switch to Baidu's mixed-invoice endpoint multiple_invoice, which splits 13 common receipt types in one call.

Step 5: collect files and run the batch script. Use the standard library pathlib or glob to walk the folder for PDF, JPG, and PNG files — no third-party dependency needed. Render PDFs to 300dpi PNG with PyMuPDF: PDFs default to 72dpi, 150dpi is fine for web previews but too coarse for OCR on small text, 300dpi is the recommended input, and PNG beats JPG because compression artifacts smear thin strokes. Call the API per file, pack the words_result fields into a dict, and record failures without breaking the batch:

python
import os, time, base64, glob
import fitz  # PyMuPDF
import pandas as pd
import requests

VAT_URL = "https://aip.baidubce.com/rest/2.0/ocr/v1/vat_invoice"

def pdf_to_png(pdf_path, dpi=300):
    # Render PDF pages at 300dpi for reliable small-text recognition
    doc = fitz.open(pdf_path)
    pix = doc[0].get_pixmap(matrix=fitz.Matrix(dpi / 72, dpi / 72))
    return pix.tobytes("png")

def recognize(token, img_bytes):
    resp = requests.post(
        VAT_URL,
        params={"access_token": token},
        headers={"Content-Type": "application/x-www-form-urlencoded"},
        data={"image": base64.b64encode(img_bytes).decode()},
    )
    return resp.json()

def main():
    token = get_token()
    rows = []
    files = glob.glob("invoices/*.pdf") + glob.glob("invoices/*.jpg") + glob.glob("invoices/*.png")
    for path in files:
        row = {"file": os.path.basename(path), "status": "ok"}
        try:
            if path.lower().endswith(".pdf"):
                img = pdf_to_png(path)
            else:
                img = open(path, "rb").read()
            w = recognize(token, img).get("words_result", {})
            row.update({
                "invoice_number": w.get("InvoiceNum", ""),
                "invoice_date": w.get("InvoiceDate", ""),
                "seller_tax_id": w.get("SellerRegisterNum", ""),
                "amount": w.get("TotalAmount", ""),
                "tax": w.get("TotalTax", ""),
                "total_with_tax": w.get("AmountInFiguers", ""),
            })
        except Exception as e:
            row["status"] = f"failed: {e}"
        rows.append(row)
        time.sleep(0.6)  # free tier is capped at 2 QPS; serialize requests
    pd.DataFrame(rows).to_excel("invoice_summary.xlsx", index=False)

if __name__ == "__main__":
    main()

Note that converting to PNG first keeps the input uniform and leaves local copies for auditing; if you want the fewest steps, the endpoint accepts pdf_file directly and the conversion can be skipped.

Step 6: know your quota and rate limits. Per Baidu AI Cloud's official free-tier page (page updated 2026-10-22; verified 2026-10-30 for this article): VAT invoice recognition gets 1,000 calls/month for individually verified accounts and 2,000 calls/month for enterprise-verified accounts, granted monthly with no carry-over. The same page states explicitly that both successful and failed calls consume the free quota — a blurry test image still costs a call, so do not burn quota on junk inputs. For QPS, follow the official QA figure: 2 QPS before enabling payment, 10 QPS after; community claims of 5 or 3 QPS contradict the official wording and are not adopted here. At 2 QPS your script must serialize with time.sleep or back off exponentially on throttling errors — that is exactly what sleep(0.6) does above.

Other vendors as reference points: Tencent Cloud's mixed-invoice endpoint handles 14 standard reimbursement invoice types in one image at a default 5 requests/second; Alibaba Cloud's RecognizeInvoice covers VAT special, general, and e-invoices with the widest format support (including OFD and TIFF) and self-reports 98% overall accuracy — a vendor claim, not an independent benchmark. Tencent Cloud's free quota is described inconsistently across sources, so check the actual resource-package list in your console before committing.

Route B: Local PaddleOCR

Why bother: PaddleOCR is among the most-starred open-source OCR projects on GitHub (91,929 stars as measured on 2026-10-30), under the Apache-2.0 license. Its latest release, v3.7.0, shipped the PP-OCRv6 model family in tiny, small, and medium tiers, all runnable on CPU — the release notes cite a 5.2x CPU inference speedup via OpenVINO, so CPU-only batch processing is entirely viable, just slower than GPU, and with no VRAM pressure at all since no graphics card is needed.

text
pip install paddlepaddle paddleocr

Environment caveat: the repository's commit history mentions a bug in PaddlePaddle 3.3.0's native CPU inference affecting paddleocr models, and the docs once suggested rolling the version back. Pinning the PaddlePaddle version during setup is common practice; when inference misbehaves, check version compatibility before suspecting your code.

The key insight, and the trap this article most wants you to avoid: the open-source PaddleOCR ships no VAT-invoice-specific model. The correct usage is to run PP-OCRv6 as a general-purpose text detector and recognizer, get back a list of text blocks, then map fields with your own regex rules. That gap in engineering effort — versus a cloud API that returns named fields like InvoiceNum — is the core argument for choosing between the two routes. When you need table or layout structure, the PaddleOCR-VL models introduced in v3.6.0 work with the PP-StructureV3 pipeline, which supports table recognition and stamp detection toggles.

python
import re, glob
import pandas as pd
from paddleocr import PaddleOCR

ocr = PaddleOCR(ocr_version="PP-OCRv6", device="cpu")  # CPU is enough

def extract_fields(texts):
    # Rule-based field extraction is the real workload of the local route
    row = {}
    for t in texts:
        if re.fullmatch(r"\d{8}", t):          # 8-digit invoice number
            row["invoice_number"] = t
        m = re.search(r"(\d{4}年\d{1,2}月\d{1,2}日)", t)
        if m:
            row["invoice_date"] = m.group(1)
        m = re.search(r"(?:价税合计|合\s*计).*?([\d,]+\.\d{2})", t)
        if m:
            row.setdefault("total_with_tax", m.group(1))
    return row

rows = []
for path in glob.glob("invoices/*.png") + glob.glob("invoices/*.jpg"):
    result = ocr.predict(path)
    texts = result[0]["rec_texts"]             # generic text blocks
    rows.append({"file": path, **extract_fields(texts)})

pd.DataFrame(rows).to_excel("invoice_summary_local.xlsx", index=False)

The sample regex covers only the three most common fields. Real invoices are messier: fully digitalized e-invoice layouts, roll invoices, and creased scans all cause missed matches, so keep a manual-review status column in the local route's output.

Field Mapping and Excel Export

The cloud API returns English field names, so map them before the table lands in finance's hands. The key fields from Baidu AI Cloud's official documentation:

Returned fieldMeaningSuggested column
InvoiceNumInvoice numberinvoice_number
InvoiceDateIssue dateinvoice_date
SellerRegisterNumSeller's taxpayer IDseller_tax_id
TotalAmountAmount before taxamount
TotalTaxTotal taxtax
AmountInFiguersTotal incl. tax (numeric)total_with_tax
CommodityTaxRateTax rate (inside the item-details array)tax_rate

Fields in the item-details array, such as CommodityName and CommodityPrice, can be expanded into a child table as needed. For export, think pandas for the data and openpyxl for the styling: pandas' to_excel writes .xlsx through the openpyxl engine underneath, and the current pandas stable release, 3.0.6 (published 2026-09-18), requires Python 3.11+. A little office polish:

python
from openpyxl import load_workbook
from openpyxl.styles import Font

wb = load_workbook("invoice_summary.xlsx")
ws = wb.active
for cell in ws[1]:
    cell.font = Font(bold=True)   # bold header row
ws.freeze_panes = "A2"            # freeze header
for col in "FGH":                 # thousands separator on amount columns
    for cell in ws[col][1:]:
        cell.number_format = "#,##0.00"
wb.save("invoice_summary.xlsx")

Rule of thumb: think in tables, use pandas; think in cells and styles, use openpyxl. Note that openpyxl does not evaluate formulas — if the sheet contains them, open it in Excel or LibreOffice, let it recalculate and save, before reading values with load_workbook(data_only=True).

LLM Route: A Supplement, Not the Main Line

GLM-OCR (open-sourced by Zhipu, 0.9B parameters) supports text, formula, table, and custom modes; the custom mode takes a JSON-schema prompt for structured extraction, and community developers have published receipt extractions pulling invoice numbers, dates, line items, and totals. State the caveat in full, though: this rests on the vendor's own claims plus third-party general-purpose tests, with no invoice-specific public benchmark; the testers themselves concluded that structured extraction only stabilizes once the prompt constrains it explicitly. At 0.9B parameters its VRAM footprint is tiny, and it is worth trying when volume is low and one prompt can replace a pile of regex. For a production pipeline, the two routes above are more dependable. The full resource list lives in our GLM-OCR article (see related reading below).

Common Failures and Fixes

Separate "recognition failed" from "recognition wrong": the former means an API error or empty result, usually image quality; the latter means results with misaligned fields, usually layout and semantics. Work through four checks in order: image quality, layout and language, engine parameters, business fallback.

High-frequency capture-side causes: glare on laminated invoices under direct light, tilted shots distorting fields, creases and stains fading text, the red invoice stamp covering the tax ID or amount, and screenshot filters or over-compression destroying fine detail. Fixes: diffuse light and shoot flat, flatten creases, adjust the angle when a stamp blocks a field, and prefer the original PDF e-invoice over re-photographed screenshots. For a sense of scale: one Baidu AI Cloud technical article's experiments put a 23% accuracy drop at 15 degrees of tilt, with case studies showing stamp coverage corrupting 35% of amount fields — those are that article's own experimental figures, not industry-wide numbers.

On the API side, vendor error-code tables are your checklist (Tencent Cloud's documentation as an example; the logic is similar everywhere): ImageBlur means reshoot or preprocess; ImageDecodeFailed means verify the file really is PNG/JPG/PDF and uncorrupted; oversized files must be compressed or split. Invoice-type mismatches — fixed-amount or machine-printed invoices forced through the VAT endpoint — go to the mixed-invoice endpoints instead.

The LLM route has three signature failure modes worth knowing (per Tencent Cloud's official article): field shuffle (buyer and seller names swapping tax IDs), amount shuffle (net amount, tax, and gross total read out of order), and line-item truncation (multi-row details reduced to the first few). Professional OCR engines return field coordinates, which is why shuffle does not happen there.

Compliance Boundaries

Invoices carry business-sensitive data — tax IDs, buyer and seller names — so before uploading to a cloud API, review the provider's data agreement (Baidu AI Cloud's terms state that your business data belongs to you); for authenticity verification, the only official online channel is the National VAT Invoice Verification Platform — five checks per invoice per day, invoices issued within the last five years, same-day invoices verifiable the same day; and AI-assisted review supports but never replaces professional judgment.

Related reading

FAQ

Q1: Why does invoice OCR recognition fail?

Most often image quality: glare, blur, tilt, a stamp covering key fields, or over-compression. Second is invoice-type mismatch — fixed-amount and general machine-printed invoices are outside the VAT invoice endpoint's scope, so switch to a mixed-invoice endpoint. Cloud APIs in the free tier also enforce QPS caps, and excessive concurrency gets throttled.

Q2: How do I batch-recognize invoices into Excel?

A three-stage pipeline: collect PDF/JPG/PNG files with pathlib or glob; render PDFs to 300dpi images with PyMuPDF; call the OCR API or local model per file, pack fields into dicts, and export once with pandas' to_excel. The main script in Route A is a complete, runnable example.

Q3: Can PDF e-invoices be recognized directly, without converting to images first?

Baidu AI Cloud's endpoint accepts pdf_file directly, so no conversion is required. Still, rendering every PDF to a 300dpi PNG keeps inputs uniform and leaves local copies for auditing. The same goes for scans: prefer the original PDF over re-captured screenshots.

Q4: Can open-source PaddleOCR extract invoice fields out of the box?

No. The open-source release has no VAT-invoice-specific model; it returns generic text blocks, and you write the regex rules mapping invoice numbers, dates, and amounts yourself. If you want named fields out of the box, the cloud API route takes far less engineering.

Q5: How do I verify the authenticity of recognized invoices?

OCR only reads the data; verification is a separate step. The only official online channel is the National VAT Invoice Verification Platform — five checks per invoice per day, covering invoices issued within the last five years. Cloud vendors offer invoice-verification APIs for batch spot checks, but results are still anchored to the tax authority's platform.

This article is AI-assisted and human-edited. Last updated: 2026-10-30

FAQ

Why does invoice OCR recognition fail?
Most often image quality: glare, blur, tilt, a stamp covering key fields, or over-compression. Second is invoice-type mismatch — fixed-amount and general machine-printed invoices are outside the VAT invoice endpoint's scope, so switch to a mixed-invoice endpoint. Cloud APIs in the free tier also enforce QPS caps, and excessive concurrency gets throttled.
How do I batch-recognize invoices into Excel?
A three-stage pipeline: collect PDF/JPG/PNG files with pathlib or glob; render PDFs to 300dpi images with PyMuPDF; call the OCR API or local model per file, pack fields into dicts, and export once with pandas' to_excel. The main script in Route A is a complete, runnable example.
Can PDF e-invoices be recognized directly, without converting to images first?
Baidu AI Cloud's endpoint accepts pdf_file directly, so no conversion is required. Still, rendering every PDF to a 300dpi PNG keeps inputs uniform and leaves local copies for auditing. The same goes for scans: prefer the original PDF over re-captured screenshots.
Can open-source PaddleOCR extract invoice fields out of the box?
No. The open-source release has no VAT-invoice-specific model; it returns generic text blocks, and you write the regex rules mapping invoice numbers, dates, and amounts yourself. If you want named fields out of the box, the cloud API route takes far less engineering.
How do I verify the authenticity of recognized invoices?
OCR only reads the data; verification is a separate step. The only official online channel is the National VAT Invoice Verification Platform — five checks per invoice per day, covering invoices issued within the last five years. Cloud vendors offer invoice-verification APIs for batch spot checks, but results are still anchored to the tax authority's platform.

Related

Field SOP

A 3B Model That Chews 200k Pages a Day: DeepSeek-OCR Deploy SOP

A hands-on DeepSeek-OCR deployment SOP: the vision side squeezes a whole page into a few dozen to a few hundred vision tokens while the 3B MoE decoder keeps only about 570M active parameters — the official paper cites 200k+ pages of training data per day on one A100-40G, and a 24GB consumer card gets you in. The two official repos take different routes: environment and Transformers inference on generation 2 (Apache-2.0), upstream vLLM and API serving on generation 1's mature ecosystem (MIT). Verbatim commands for the cuda11.8+torch2.6.0+flash-attn 2.7.3 stack, a five-mode resolution token ledger (64 to 400+), the NGram anti-repetition processor with the 128821/128822 table whitelist, community-only VRAM guidance (nothing official), and a ten-item troubleshooting checklist.

Oct 25, 202610 min read
Field SOP

Transcribe 13-Minute Audio in 59 Seconds, Fully Local

A five-step local speech-to-text SOP (faster-whisper as the main engine, repo github.com/SYSTRAN/faster-whisper): pick by VRAM (24GB card large-v3+float16+beam 5; 8GB card int8_float16, step down to medium if tight; CPU-only small/base+int8), install (GPU needs CUDA 12 cuBLAS plus cuDNN 9, older stacks pin ctranslate2 3.24.0/4.4.0), a minimal transcription script, three speed-and-VRAM levers (fp16 to int8 saves about 1.6GB, batch 8 costs about 1.5GB for roughly 3.7x speedup, beam comes third), and advanced steps (Silero VAD, word timestamps, hand-rolled SRT, WhisperX diarization). One benchmark framing is stated everywhere: large-v2, beam_size=5, 13 minutes of audio on an RTX 3070 Ti 8GB — the original implementation takes 2m23s/4708MB, faster-whisper int8 59s/2926MB, batched 16s/4500MB; on CPU the piece honestly notes whisper.cpp fp32 (2m05s/1049MB) beats faster-whisper fp32, with no sweeping claims. The pitfall list includes never using pure int8 on GPU (CTranslate2 docs: compute capability 7.0/6.1+ required, older cards silently fall back to float32). A single closing paragraph points to NVIDIA's Nemotron 3.5 Transcribe as the new-engine option, leaving the benchmarks to the open-source column piece.

Oct 29, 202610 min read
Field SOP

How to Make AI Short Dramas: Jimeng Studio in Six Steps

Jimeng's Short Drama Studio went live on October 26 (official announcement), and this SOP walks the six steps from script to export: DreamScript for scripting (multi-genre generation, hit diagnostics, 100k-character long-form parsing), Anycook for art direction (characters and scenes), DreamShot for storyboards (canvas and storyboard modes plus a 3D director-stage preview), generation, post-production (clip re-shoots, voice reconstruction, smart cuts), and export. Costs are stated as published: six membership tiers from 725 to 54,600 credits, Seedance 2.5 promo at 0.19 RMB per second (0.18 via CLI), with the site's two inconsistent price pages deferred to the live pricing page; officially unpublished items (in-studio entry path, separate billing, supported redub languages) are left blank with do-it-yourself verification paths. Market context follows the DataEye figures: Douyin's AIGC comic-drama share went from 0 percent in January to 10.88 percent in December, 2.196 billion monthly views, up 181x for the year. Division of labor with the site's LibTV x WorkBuddy SOP (the platform-integration route) is stated.

Oct 28, 202611 min read