← Back to Blog

Building a Receipt Scanner with DocsRouter and Gemini 3 Flash

by DocsRouter Team

Building a Receipt Scanner with DocsRouter and Gemini 3 Flash

In this tutorial, we will build a production-ready receipt scanner around the new Gemini 3 Flash model. We will use DocsRouter to handle the API interactions, schema validation, and automatic retries.

By the end of this guide, you will have a Node.js script that can:

  1. Ingest a folder of receipt images (JPG/PNG).
  2. Extract structured data (Total, Date, Vendor, Line Items).
  3. Validate the data against a strict Zod schema.
  4. Export the results to a CSV file.

Why Gemini 3 Flash?

Implemention

1. Install Dependencies

npm install @tanstack/react-query axios

2. The API Route

We will create a simple API route in our Next.js app to proxy the request to DocsRouter. This keeps your API key secure.

// app/api/scan/route.ts
import { NextResponse } from 'next/server';

export async function POST(req: Request) {
  const { imageUrl } = await req.json();

  const response = await fetch('https://api.docsrouter.com/v1/ocr', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.DOCSROUTER_API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      url: imageUrl,
      model: 'google/gemini-3.0-flash', // The magic line
      schema: { // DocsRouter validates this for you!
        type: "object",
        properties: {
          merchant: { type: "string" },
          total: { type: "number" },
          date: { type: "string" }
        }
      }
    })
  });

  const data = await response.json();
  return NextResponse.json(data);
}

3. The Frontend

A simple form to handle the upload.

// app/page.tsx
'use client';
import { useState } from 'react';

export default function ReceiptScanner() {
  const [result, setResult] = useState(null);

  const handleUpload = async (e) => {
    // ... upload logic to getting public URL ...
    const res = await fetch('/api/scan', {
      method: 'POST',
      body: JSON.stringify({ imageUrl: publicUrl })
    });
    setResult(await res.json());
  };

  return (
    <div>
      <input type="file" onChange={handleUpload} />
      {result && (
        <div className="p-4 border rounded mt-4">
          <p>Merchant: {result.result.merchant}</p>
          <p>Total: ${result.result.total}</p>
        </div>
      )}
    </div>
  );
}

Conclusion

With less than 50 lines of code, you've built a robust receipt processing engine. No training models, no managing servers—just pure, intelligence-driven software development.