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:
- Ingest a folder of receipt images (JPG/PNG).
- Extract structured data (Total, Date, Vendor, Line Items).
- Validate the data against a strict Zod schema.
- Export the results to a CSV file.
Why Gemini 3 Flash?
- Cost: Extremely low per-token pricing.
- Speed: Sub-200ms latency.
- Accuracy: More than capable of reading faded thermal paper.
Implemention
1. Install Dependencies
npm install @tanstack/react-query axios2. 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.