← All posts
tutorialtoolsocr

Build an OCR invoice processor in a weekend with /tools/ocr

Mahesh Naidu·9 min read

Invoice processing is one of those problems every finance team has and nobody wants to solve manually. A junior accountant spending 20 minutes per invoice on data entry is $800/month in time for a 50-invoice business. An OCR-powered processor that extracts the same data in 3 seconds and pushes it to a spreadsheet or accounting system is a $50/month SaaS product people actually pay for.

This tutorial walks through building exactly that — a Next.js app that accepts an invoice image (photo or scan), extracts structured data with the APlicious /tools/ocr endpoint, and returns a clean JSON payload ready to push to QuickBooks, Xero, or a Google Sheet.

What the /tools/ocr endpoint does

The OCR endpoint accepts either a multipart file upload or a JSON body with an image_url. It runs the image through GPT-4o vision and returns extracted text. For invoice processing, you pair OCR extraction with a structured prompt — either in a thin server layer you write, or by using the raw text output and parsing it yourself.

# Basic OCR call
curl -X POST https://aplicious.com/api/v1/tools/ocr \
  -H "X-API-Key: YOUR_KEY" \
  -F "image=@invoice.jpg"

# Response
{
  "success": true,
  "data": {
    "text": "INVOICE\nVendor: Acme Corp\nDate: 2026-06-15\nInvoice #: INV-0042\n...",
    "confidence": 0.97,
    "word_count": 312
  }
}

Project setup

Create a Next.js app and install one dependency — we only need the APlicious API, no OCR libraries:

npx create-next-app@latest invoice-processor --typescript --app
cd invoice-processor

# Add your APlicious key to .env.local
echo "APLICIOUS_KEY=lapi_live_..." >> .env.local

The upload route

Create src/app/api/process/route.ts. This receives the invoice image from the frontend, forwards it to APlicious OCR, then parses the extracted text into structured fields:

import { NextRequest, NextResponse } from "next/server";

export async function POST(req: NextRequest) {
  const formData = await req.formData();
  const file = formData.get("invoice") as File;
  if (!file) return NextResponse.json({ error: "No file" }, { status: 400 });

  // Forward to APlicious OCR
  const ocrForm = new FormData();
  ocrForm.append("image", file);

  const ocrRes = await fetch("https://aplicious.com/api/v1/tools/ocr", {
    method: "POST",
    headers: { "X-API-Key": process.env.APLICIOUS_KEY! },
    body: ocrForm,
  });

  const ocr = await ocrRes.json();
  if (!ocr.success) return NextResponse.json({ error: "OCR failed" }, { status: 500 });

  // Parse the raw text into invoice fields
  const text: string = ocr.data.text;
  const invoice = parseInvoiceText(text);

  return NextResponse.json({ success: true, invoice, raw_text: text });
}

function parseInvoiceText(text: string) {
  // Regex-based extraction — works for most standard invoice formats
  const lines = text.split("\n").map(l => l.trim()).filter(Boolean);

  const get = (pattern: RegExp) => (text.match(pattern)?.[1] ?? "").trim();

  return {
    vendor:       get(/(?:from|vendor|bill from|company)[:\s]+([^\n]+)/i),
    invoice_no:   get(/(?:invoice\s*#?|inv[\s-]*no\.?)[:\s]+([A-Z0-9-]+)/i),
    date:         get(/(?:invoice date|date)[:\s]+([\d\/\-]+)/i),
    due_date:     get(/(?:due date|payment due)[:\s]+([\d\/\-]+)/i),
    total:        get(/(?:total|amount due)[:\s]+\$?([\d,\.]+)/i),
    currency:     text.match(/\b(USD|EUR|GBP|INR|AUD|CAD)\b/)?.[1] ?? "USD",
    line_items:   extractLineItems(lines),
  };
}

function extractLineItems(lines: string[]) {
  // Look for lines matching: Description ... $amount
  return lines
    .filter(l => /\$[\d,\.]+/.test(l) && l.length > 8)
    .map(l => {
      const amountMatch = l.match(/\$([\d,\.]+)/);
      const amount = amountMatch ? parseFloat(amountMatch[1].replace(",", "")) : 0;
      const description = l.replace(/\$[\d,\.]+/, "").trim();
      return { description, amount };
    })
    .filter(item => item.description.length > 2 && item.amount > 0);
}

The frontend

A minimal upload form in src/app/page.tsx:

"use client";
import { useState } from "react";

export default function Home() {
  const [result, setResult] = useState<any>(null);
  const [loading, setLoading] = useState(false);

  async function handleUpload(e: React.ChangeEvent<HTMLInputElement>) {
    const file = e.target.files?.[0];
    if (!file) return;

    setLoading(true);
    const form = new FormData();
    form.append("invoice", file);

    const res = await fetch("/api/process", { method: "POST", body: form });
    const data = await res.json();
    setResult(data.invoice);
    setLoading(false);
  }

  return (
    <main style={{ maxWidth: 640, margin: "60px auto", padding: "0 24px" }}>
      <h1>Invoice Processor</h1>
      <input type="file" accept="image/*,.pdf" onChange={handleUpload} />
      {loading && <p>Processing...</p>}
      {result && (
        <pre style={{ background: "#111", color: "#a9b1d6", padding: 20, borderRadius: 10, marginTop: 20 }}>
          {JSON.stringify(result, null, 2)}
        </pre>
      )}
    </main>
  );
}

What the output looks like

{
  "vendor": "Acme Corp",
  "invoice_no": "INV-0042",
  "date": "2026-06-15",
  "due_date": "2026-07-15",
  "total": 4750.00,
  "currency": "USD",
  "line_items": [
    { "description": "Software consulting (40 hrs)", "amount": 4000.00 },
    { "description": "Cloud infrastructure setup",   "amount": 750.00 }
  ]
}

Turning this into a $50/month SaaS

The extraction logic above handles the majority of standard invoice formats. From here, the product grows in two directions: integrations (push extracted data to QuickBooks, Xero, Google Sheets) and reliability (handle edge cases, add manual correction UI, support multi-page PDFs via /tools/pdf/split before OCR).

Pricing model: charge per invoice processed, or a flat monthly fee for up to N invoices. At 5 credits per OCR call and the APlicious Starter plan at $19/month (10,000 credits), you can process 2,000 invoices for $19 in upstream costs. Charge customers $0.10/invoice or $49/month for 500 — the margin is real from day one.

Your API key from the dashboard is all you need to run it locally in under 5 minutes.

Ready to ship this?

The OCR endpoint page has the full curl reference, parameter docs, and revenue math for three invoice-processor business ideas.

Try APlicious free
One key. 10 live namespaces. 250 free calls per month — no credit card required.
Get your free API key →
← Back to all posts