Blog → How to Extract Data from a PDF Invoice (Without Building an OCR Pipeline)
How to Extract Data from a PDF Invoice (Without Building an OCR Pipeline)
2026-05-22 · 5 min read
You have a PDF invoice. You need the data inside it — vendor name, invoice number, total, line items — in a format your code can use. Here's the fastest path from PDF to structured data.
1
API call
~3s
processing time
0
regex needed
Free
to start
The Problem
The Two Approaches (and Why One Is Better)
Approach 1 — Build it yourself: Use pdfplumber, PyMuPDF, or Camelot to extract raw text, then write regex patterns to find the invoice number, dates, and totals. Handle the edge cases when the text extraction fails on scanned PDFs. Write different patterns for different vendor formats. Maintain all of it when vendors update their invoice templates.
This takes 1–3 weeks for a working version, and it never fully works — there's always another vendor format that breaks it.
Approach 2 — Use an API: Send the PDF to an endpoint. Receive structured JSON. Done.
This guide covers Approach 2.
Build it yourself — weeks of work
import pdfplumber
import re
# regex for every vendor format
# breaks when template changes
# no scanned PDF support
# 300+ lines of brittle code
→API call
Use an API — one afternoon
response = requests.post(
"https://docuparseapi.com/api/v1/extract",
files={"file": f}
)
data = response.json()
# merchant, total, tax, date
# line_items — all ready
import sqlite3
import json
def store_invoice(conn: sqlite3.Connection, data: dict):
conn.execute("""
CREATE TABLE IF NOT EXISTS invoices (
document_id TEXT PRIMARY KEY,
vendor TEXT,
invoice_number TEXT,
invoice_date TEXT,
due_date TEXT,
currency TEXT,
total REAL,
tax REAL,
line_items TEXT
)
""")
conn.execute("""
INSERT OR IGNORE INTO invoices VALUES (?,?,?,?,?,?,?,?,?)
""", (
data["document_id"],
data.get("merchant"),
data.get("invoice_id"),
data.get("date"),
data.get("due_date"),
data.get("currency"),
float(data.get("total") or 0),
float(data.get("tax") or 0),
json.dumps(data.get("line_items", []))
))
conn.commit()
Push to QuickBooks or Xero
The extracted fields map directly to the bill/invoice fields in accounting APIs:
python · 18 lines
# QuickBooks bill creation (using quickbooks-online SDK)
def create_quickbooks_bill(invoice_data: dict, vendor_id: str):
from quickbooks.objects.bill import Bill
from quickbooks.objects.billline import BillLine
bill = Bill()
bill.VendorRef = {"value": vendor_id}
bill.TxnDate = invoice_data["date"]
bill.DueDate = invoice_data.get("due_date")
bill.TotalAmt = float(invoice_data["total"])
for item in invoice_data.get("line_items", []):
line = BillLine()
line.Amount = float(item.get("total", item.get("amount", 0)))
line.Description = item.get("description")
bill.Line.append(line)
return bill.save(qb=client)
Send to a webhook or downstream service
python · 17 lines
import httpx
async def forward_invoice(invoice_data: dict, webhook_url: str):
"""Forward extracted invoice data to a webhook endpoint."""
async with httpx.AsyncClient() as client:
response = await client.post(webhook_url, json={
"event": "invoice.extracted",
"invoice": {
"vendor": invoice_data.get("merchant"),
"number": invoice_data.get("invoice_id"),
"date": invoice_data.get("date"),
"amount": invoice_data.get("total"),
"currency": invoice_data.get("currency"),
"items": invoice_data.get("line_items", []),
}
})
return response.status_code == 200
Handling Scanned PDFs
If the invoice was created by scanning a paper document, the PDF contains an image, not machine-readable text. Regular PDF parsers return nothing from these files. DocuParseAPI detects scanned PDFs automatically and applies OCR — your request is identical:
python · 2 lines
# Works for both digital PDFs and scanned PDFs
invoice = extract_invoice("scanned_supplier_invoice.pdf")
Common Issues and Fixes
Issue: EXTRACTION_FAILED error
The document may be too degraded to extract cleanly (heavy compression, very low scan resolution, or a non-invoice document). Try with a higher quality scan, or submit a digital PDF if available.
Issue: Missing fields return null
Some invoices don't have due dates; some don't have line items. Check for None before using a field:
python · 3 lines
due_date = invoice.get("due_date") # May be None
if due_date:
# use it
Issue: Total is a string, not a number
The API returns monetary values as strings to preserve decimal precision. Convert before arithmetic:
python · 1 line
total = float(invoice["total"])
Issue: Line items list is empty
Not all invoices have extractable line items — some use image-embedded tables or unusual formats. The total field is always extracted when possible, even if line items are missing.
Pricing
Free tier: 20 PDF invoices/month, no credit card required