This guide covers adding invoice and receipt parsing to a Node.js application using DocuParseAPI. You'll find working code for raw Node.js, Express, Next.js App Router, and TypeScript patterns.
Node 18+
native fetch
TS
types included
~3s
extraction time
Free
20 documents/month — free forever
Setup
Prerequisites
const fs = require("fs");
async function parseInvoice(filePath) {
const form = new FormData();
form.append("file", new Blob([fs.readFileSync(filePath)]), "invoice.pdf");
const res = await fetch("https://docuparseapi.com/api/v1/extract", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.DOCUPARSE_API_KEY}` },
body: form,
});
const data = await res.json();
if (!data.success) throw new Error(data.error.message);
return data;
}- Node.js 18+ (for native
fetch) - A DocuParseAPI key — free at docuparseapi.com/signup
Test the API before writing a line of code
Upload any PDF invoice. Get back the JSON your Node.js app will receive.
const invoice = await parseInvoice("invoice.pdf");
console.log(invoice.merchant); // "Acme Corp"
console.log(invoice.total); // "1320.00"
Free tier · 20 documents/month — free forever · No credit card · No account needed for the demo
Quick Start
The Minimal Request
Node.js 18+ has fetch built in, so no HTTP library is required:
javascript · 27 lines
const fs = require("fs");
async function parseInvoice(filePath) {
const fileBuffer = fs.readFileSync(filePath);
const blob = new Blob([fileBuffer]);
const form = new FormData();
form.append("file", blob, require("path").basename(filePath));
const response = await fetch("https://docuparseapi.com/api/v1/extract", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.DOCUPARSE_API_KEY}` },
body: form,
});
const data = await response.json();
if (!data.success) {
throw new Error(`[${data.error.code}] ${data.error.message}`);
}
return data;
}
// Usage
const invoice = await parseInvoice("./invoice.pdf");
console.log(`${invoice.merchant}: $${invoice.total}`);TypeScript
TypeScript Types
typescript · 65 lines
interface LineItem {
description: string | null;
quantity: number | null;
unit_price: string | null;
total: string | null;
}
interface ExtractedDocument {
success: boolean;
document_id: string;
document_type: "invoice" | "receipt";
merchant: string | null;
date: string | null;
due_date: string | null;
currency: string | null;
subtotal: string | null;
tax: string | null;
tax_rate: string | null;
total: string | null;
invoice_id: string | null;
receipt_id: string | null;
payment_method: string | null;
line_items: LineItem[];
processing_time_ms: number;
}
interface ApiError {
success: false;
error: {
code:
| "MISSING_API_KEY"
| "INVALID_API_KEY"
| "REVOKED_API_KEY"
| "LIMIT_EXCEEDED"
| "UNSUPPORTED_FILE_TYPE"
| "FILE_TOO_LARGE"
| "NO_FILE_PROVIDED"
| "EXTRACTION_FAILED"
| "INTERNAL_ERROR";
message: string;
};
}
type ApiResponse = ExtractedDocument | ApiError;
async function parseInvoiceTyped(filePath: string): Promise<ExtractedDocument> {
const fileBuffer = await fs.promises.readFile(filePath);
const blob = new Blob([fileBuffer]);
const form = new FormData();
form.append("file", blob, path.basename(filePath));
const response = await fetch("https://docuparseapi.com/api/v1/extract", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.DOCUPARSE_API_KEY!}` },
body: form,
});
const data: ApiResponse = await response.json();
if (!data.success) {
throw new Error(`[${data.error.code}] ${data.error.message}`);
}
return data;
}Express
Express Middleware
javascript · 59 lines
// middleware/parseReceipt.js
const multer = require("multer");
const upload = multer({
storage: multer.memoryStorage(),
limits: { fileSize: 10 * 1024 * 1024 },
fileFilter: (req, file, cb) => {
const allowed = ["application/pdf", "image/jpeg", "image/png"];
cb(null, allowed.includes(file.mimetype));
},
});
/**
* Express middleware: parses an uploaded document and attaches
* the extracted data to req.extractedDocument
*/
async function parseUploadedDocument(req, res, next) {
if (!req.file) {
return res.status(400).json({ error: "No file provided" });
}
try {
const blob = new Blob([req.file.buffer], { type: req.file.mimetype });
const form = new FormData();
form.append("file", blob, req.file.originalname);
const response = await fetch("https://docuparseapi.com/api/v1/extract", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.DOCUPARSE_API_KEY}` },
body: form,
});
const data = await response.json();
if (!data.success) {
if (data.error.code === "LIMIT_EXCEEDED") {
return res.status(429).json({ error: "Document limit reached" });
}
return res.status(422).json({ error: data.error.message });
}
req.extractedDocument = data;
next();
} catch (err) {
next(err);
}
}
// Usage in route
app.post(
"/api/invoices/upload",
upload.single("invoice"),
parseUploadedDocument,
(req, res) => {
const doc = req.extractedDocument;
// Create your invoice record from doc.merchant, doc.total, doc.line_items, etc.
res.json({ invoice: doc });
}
);Next.js
Next.js App Router API Route
typescript · 58 lines
// app/api/invoices/upload/route.ts
import { NextRequest, NextResponse } from "next/server";
export async function POST(request: NextRequest): Promise<NextResponse> {
const apiKey = process.env.DOCUPARSE_API_KEY;
if (!apiKey) {
return NextResponse.json({ error: "Server misconfiguration" }, { status: 500 });
}
// Parse the incoming form data
let formData: FormData;
try {
formData = await request.formData();
} catch {
return NextResponse.json({ error: "Invalid form data" }, { status: 400 });
}
const file = formData.get("file");
if (!file || !(file instanceof File)) {
return NextResponse.json({ error: "No file provided" }, { status: 400 });
}
// Validate type
const allowedTypes = ["application/pdf", "image/jpeg", "image/png"];
if (!allowedTypes.includes(file.type)) {
return NextResponse.json(
{ error: "Only PDF, JPG, and PNG files are supported" },
{ status: 400 }
);
}
// Forward to DocuParseAPI
const upstream = new FormData();
upstream.append("file", file);
try {
const response = await fetch("https://docuparseapi.com/api/v1/extract", {
method: "POST",
headers: { Authorization: `Bearer ${apiKey}` },
body: upstream,
});
const data = await response.json();
if (!data.success) {
return NextResponse.json(
{ error: data.error.message, code: data.error.code },
{ status: response.status }
);
}
return NextResponse.json(data);
} catch (error) {
console.error("DocuParseAPI error:", error);
return NextResponse.json({ error: "Extraction service error" }, { status: 502 });
}
}Your Node.js app is ready for this.
Get your API key in 60 seconds. Works in any Node.js environment.
Errors
Error Handling Reference
javascript · 25 lines
async function safeParseInvoice(filePath) {
try {
const result = await parseInvoice(filePath);
return { success: true, data: result };
} catch (error) {
// Parse error code from message
const match = error.message.match(/\[(\w+)\]/);
const code = match?.[1];
switch (code) {
case "LIMIT_EXCEEDED":
return { success: false, reason: "Monthly document limit reached" };
case "UNSUPPORTED_FILE_TYPE":
return { success: false, reason: "File type not supported" };
case "FILE_TOO_LARGE":
return { success: false, reason: "File exceeds 10MB" };
case "EXTRACTION_FAILED":
return { success: false, reason: "Document could not be parsed" };
case "INVALID_API_KEY":
return { success: false, reason: "Invalid API key" };
default:
return { success: false, reason: "Unexpected error" };
}
}
}