Language Guides
JavaScript / Node.js
Parse receipts from Node.js, edge functions, or the browser using the native fetch API. Requires Node 18+ or any modern browser.
No installation needed
The ReceiptConverter API uses standard HTTP. In Node 18+ or modern browsers you already have fetch, FormData, and File built in — no polyfills required.
Uploading a file (Node.js)
import { readFileSync } from "node:fs";
const API_KEY = process.env.RECEIPTCONVERTER_API_KEY;
async function parseReceipt(filePath) {
const bytes = readFileSync(filePath);
const file = new File([bytes], "receipt.jpg", { type: "image/jpeg" });
const form = new FormData();
form.append("file", file);
const res = await fetch("https://receiptconverter.com/api/v1/convert", {
method: "POST",
headers: { Authorization: `Bearer ${API_KEY}` },
body: form,
});
if (!res.ok) throw new Error(`API error ${res.status}: ${await res.text()}`);
return res.json();
}
const receipt = await parseReceipt("./receipt.jpg");
console.log(receipt.data.vendor, receipt.data.total);TypeScript version
import { readFileSync } from "node:fs";
const API_KEY = process.env.RECEIPTCONVERTER_API_KEY!;
interface TaxLine { label: string; amount: number; }
interface LineItem { name: string; quantity: number; unit_price: number; total_price: number; }
interface ReceiptData {
vendor: string | null;
date: string | null;
total: number | null;
subtotal: number | null;
currency: string | null;
payment_method: string | null;
category: string | null;
taxes: TaxLine[];
tip: number | null;
items: LineItem[];
}
interface ConvertResponse {
success: boolean;
processing_ms: number;
conversions_used: number;
conversions_limit: number;
data: ReceiptData;
}
async function parseReceipt(filePath: string): Promise<ConvertResponse> {
const bytes = readFileSync(filePath);
const file = new File([bytes], "receipt.jpg", { type: "image/jpeg" });
const form = new FormData();
form.append("file", file);
const res = await fetch("https://receiptconverter.com/api/v1/convert", {
method: "POST",
headers: { Authorization: `Bearer ${API_KEY}` },
body: form,
});
if (!res.ok) {
const err = await res.json().catch(() => ({}));
throw new Error(`${res.status} ${(err as { error?: string }).error ?? res.statusText}`);
}
return res.json() as Promise<ConvertResponse>;
}Parsing from a URL
const API_KEY = process.env.RECEIPTCONVERTER_API_KEY;
async function parseReceiptUrl(url, fileName) {
const res = await fetch("https://receiptconverter.com/api/v1/convert", {
method: "POST",
headers: {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ url, file_name: fileName }),
});
if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
return res.json();
}
const receipt = await parseReceiptUrl(
"https://example.com/receipts/lunch.pdf",
"lunch_march_2024.pdf"
);Browser / client-side
Works with a file <input> or drag-and-drop. Do NOT expose your API key in client-side code — proxy through your own backend.
// Your backend proxy — keeps the API key server-side
// POST /api/parse-receipt → forwards to ReceiptConverter
// Frontend (React example)
async function handleFile(file) {
const form = new FormData();
form.append("file", file);
const res = await fetch("/api/parse-receipt", {
method: "POST",
body: form,
});
const receipt = await res.json();
console.log(receipt.data.vendor, receipt.data.total);
}Error handling
async function parseReceiptSafe(filePath) {
try {
const bytes = readFileSync(filePath);
const file = new File([bytes], "receipt.jpg", { type: "image/jpeg" });
const form = new FormData();
form.append("file", file);
const res = await fetch("https://receiptconverter.com/api/v1/convert", {
method: "POST",
headers: { Authorization: `Bearer ${API_KEY}` },
body: form,
signal: AbortSignal.timeout(30_000),
});
if (res.status === 429) {
const { upgrade_url } = await res.json();
console.warn("Rate limit hit. Upgrade at:", upgrade_url);
return null;
}
if (res.status === 401) {
console.error("Invalid API key.");
return null;
}
if (!res.ok) throw new Error(`Unexpected ${res.status}`);
return res.json();
} catch (err) {
if (err.name === "TimeoutError") console.error("Request timed out");
else console.error("Request failed:", err.message);
return null;
}
}Batch processing
import { readFileSync } from "node:fs";
import { readdir } from "node:fs/promises";
import path from "node:path";
const API_KEY = process.env.RECEIPTCONVERTER_API_KEY;
const CONCURRENCY = 5; // Stay within rate limits
async function parseOne(filePath) {
const bytes = readFileSync(filePath);
const file = new File([bytes], path.basename(filePath));
const form = new FormData();
form.append("file", file);
const res = await fetch("https://receiptconverter.com/api/v1/convert", {
method: "POST",
headers: { Authorization: `Bearer ${API_KEY}` },
body: form,
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) return null;
return res.json();
}
// Process in chunks of CONCURRENCY
const files = await readdir("./receipts");
const results = [];
for (let i = 0; i < files.length; i += CONCURRENCY) {
const chunk = files.slice(i, i + CONCURRENCY);
const settled = await Promise.allSettled(
chunk.map((f) => parseOne(`./receipts/${f}`))
);
results.push(...settled.map((s) => s.status === "fulfilled" ? s.value : null));
}
console.log(`Processed ${results.filter(Boolean).length}/${files.length} receipts`);Next steps: Full API reference · Batch processing guide · No-code integrations