SAGEA LogoDocs & API
CookbooksOCR Cookbooks

Extract Line Items from Invoices

Turn PDF invoices into structured JSON line items you can validate and post to accounting.

  • Request json output and read line items from blocks
  • Parse descriptions, quantities, and prices into clean records
  • Validate subtotal, tax, and grand total in Python

Time to complete: ~15 minutes

Prerequisites

  • A SAGEA_API_KEY exported as an environment variable
  • An invoice file in PDF, PNG, JPG, TIFF, or WEBP format, max 100MB, ideally 300 DPI
  • Python 3.9+ with the requests package installed
  • Set up document processing
  • ARVA OCR model card

Step 1: Send the invoice to ARVA OCR

Request output_format="json" so you get text, pages, confidence, and blocks back.

curl -X POST https://api.sagea.space/v1/ocr/process \
  -H "Authorization: Bearer $SAGEA_API_KEY" \
  -F model="arva-ocr" \
  -F [email protected] \
  -F output_format="json"

Expected JSON shape:

{"text": "...", "pages": 2, "confidence": 0.992, "blocks": [{"type": "paragraph", "page": 1, "bbox": [x0,y0,x1,y1], "confidence": 0.99, "text": "..."}], "model_used": "arva-ocr"}

ARVA OCR reaches 99.2% accuracy on clean scans. Standard pages cost $1.00 per 1000 pages.

Step 2: Parse blocks into line items

Filter blocks for line-item rows and convert them to dictionaries.

curl -X POST https://api.sagea.space/v1/ocr/process \
  -H "Authorization: Bearer $SAGEA_API_KEY" \
  -F model="arva-ocr" \
  -F [email protected] \
  -F output_format="json" \
  -o invoice.json
cat invoice.json

Step 3: Validate totals before posting

Recompute subtotal and grand total and compare against the extracted total.

import re
 
full_text = data["text"]
m_total = re.search(r"Total\s*\$?([\d,]+\.\d{2})", full_text)
m_tax = re.search(r"Tax\s*\$?([\d,]+\.\d{2})", full_text)
claimed_total = float(m_total.group(1).replace(",", "")) if m_total else None
claimed_tax = float(m_tax.group(1).replace(",", "")) if m_tax else 0.0
 
subtotal = round(sum(i["line_total"] for i in items), 2)
computed = round(subtotal + claimed_tax, 2)
print(f"subtotal={subtotal} tax={claimed_tax} computed={computed} claimed={claimed_total}")
 
assert claimed_total is not None, "no total found in OCR text"
assert abs(computed - claimed_total) < 0.01, f"total mismatch: {computed} vs {claimed_total}"
print("totals balance - safe to post")

Verify

ErrorCauseFix
401 UnauthorizedMissing or invalid API keyExport a valid key as SAGEA_API_KEY and retry
400 unsupported_formatFile type not in PDF, PNG, JPG, TIFF, WEBPConvert the invoice to PDF or PNG and retry
413 Payload Too LargeFile is over 100MBCompress or split the PDF, then retry
429 rate_limit_exceededToo many requests at onceBack off with retries and reduce concurrency

What's next

On this page