parsr.

For expense and spend-management platforms

Categorize receipts and statements with structured JSON.

Manual receipt entry is the largest single source of friction in every expense product we've talked to. Phones in pockets, blurry PDFs, German receipts in a French traveler's report, EUR / USD / JPY in the same week. parsr returns one schema — merchant, line items, tax, total, currency, FX hint — across every photo your users throw at it.

Why expense teams pick parsr

Built for the messy real-world receipt

01 / Languages

Receipts in any language your users travel through

All major EU languages (DE, FR, NL, IT, ES, PT, PL), plus Japanese, Arabic, and Cyrillic scripts. The schema is language-stable: merchant_name, line_items, tax_lines, total — same fields whether the slip is from Berlin, Tokyo, or Marrakech.

02 / Money

Multi-currency with FX rate detection

Receipts often print currency in two places (issuing currency + cardholder currency for foreign cards). parsr returns both, plus a detected FX rate when present. Your reimbursement engine gets the source of truth without you re-implementing it for every market.

03 / Outbound

Webhook fan-out into the accounting system the customer picked

HMAC-signed webhooks fire on every parse. Wire one consumer per destination — QuickBooks Online, Xero, NetSuite, Sage, Odoo. Same JSON shape on the way in, accounting-system-specific mapping on the way out, controlled by you.

The expense loop

Five steps from photo to ledger

The same flow we see in production at the Spendesk-shaped, the Pleo-shaped, and the in-house finance-ops platforms.

  1. 01

    01 / user.snap(receipt)

    User photographs a receipt from your mobile app — typical phone camera, ~150 DPI is enough. The app POSTs the JPG/HEIC/PDF to your server, which forwards it to parsr's /v1/parse/receipt endpoint.

  2. 02

    02 / parsr.parse → receipt.v2

    parsr returns merchant name, address, VAT number, line items with quantity and unit price, tax lines per rate, currency, total, payment method, and an FX hint when a foreign card was used. Each field carries a confidence score.

  3. 03

    03 / categorize.if(confidence > 0.85)

    Map merchant + line items to your category taxonomy. When the joint confidence clears your threshold (we recommend 0.85 for auto-tag), assign the category and mark the expense ready-to-submit. Below threshold, send to the user for one-tap confirmation.

  4. 04

    04 / approval.route(expense)

    Your normal approval workflow runs — manager review, policy checks, duplicate detection. parsr surfaces a duplicate_hint field based on merchant + amount + timestamp + total, so duplicate-receipt fraud (the same coffee submitted twice) is one boolean to check.

  5. 05

    05 / accounting.export(expense)

    On approval, fan out via webhook into QuickBooks Online, Xero, NetSuite, Sage, or Odoo. parsr's response includes the fields these systems require — vendor name, tax-rate code, GL hint — pre-mapped where possible.

The integration

Auto-tag at the confidence threshold

Parse the receipt, auto-categorize when confidence clears your threshold, queue the rest for human approval, and fan out to the accounting system the customer chose.

expense/process_receipt.pypython
import os
import json
import hmac
import hashlib
import httpx

API = "https://eu-api.tryparsr.dev"
KEY = os.environ["PARSR_KEY"]  # sk_eu_live_…
ACCOUNTING_WEBHOOK = os.environ["ACCOUNTING_WEBHOOK"]
WEBHOOK_SECRET = os.environ["WEBHOOK_SECRET"].encode()

AUTO_TAG_THRESHOLD = 0.85

async def process_receipt(image_bytes: bytes, expense_id: str) -> dict:
    async with httpx.AsyncClient(timeout=30) as client:
        resp = await client.post(
            f"{API}/v1/parse/receipt?wait=30",
            headers={
                "Authorization": f"Bearer {KEY}",
                "Idempotency-Key": expense_id,
            },
            files={"file": ("receipt.jpg", image_bytes, "image/jpeg")},
        )
        resp.raise_for_status()
        receipt = resp.json()

    # 1. Auto-tag if the parse is confident enough
    confidence = min(
        receipt["fields"]["merchant_name"]["confidence"],
        receipt["fields"]["total"]["confidence"],
    )
    auto_tagged = confidence >= AUTO_TAG_THRESHOLD

    payload = {
        "expense_id": expense_id,
        "merchant": receipt["fields"]["merchant_name"]["value"],
        "total": receipt["fields"]["total"]["value"],
        "currency": receipt["fields"]["currency"]["value"],
        "fx_rate": receipt["fields"].get("detected_fx_rate", {}).get("value"),
        "category_hint": receipt["fields"]["category_hint"]["value"],
        "auto_tagged": auto_tagged,
        "needs_review": not auto_tagged,
        "duplicate_hint": receipt.get("duplicate_hint", False),
    }

    # 2. Fan out to the customer's accounting system
    body = json.dumps(payload).encode()
    sig = hmac.new(WEBHOOK_SECRET, body, hashlib.sha256).hexdigest()
    async with httpx.AsyncClient(timeout=10) as client:
        await client.post(
            ACCOUNTING_WEBHOOK,
            content=body,
            headers={
                "Content-Type": "application/json",
                "X-Signature": f"sha256={sig}",
            },
        )

    return payload

What expense-product reviewers want

The features your customers' finance teams ask about

  • GDPR-compliant with a vendor DPA available before signup
  • Multi-currency: source currency + cardholder currency + FX hint when present
  • EU residency by default — Frankfurt + Paris regions, no US fall-through
  • Webhook fan-out to QuickBooks Online, Xero, NetSuite, Sage, Odoo
  • Spendesk / Pleo / Brex-style consumers welcome — webhook payloads are stable
  • SOC 2 Type II audit window opens Q3 2026 (ISO 27001 to follow)
  • HMAC-signed webhooks with replay-window protection
  • Idempotency-Key header for retry safety on flaky mobile uploads
  • 30-day file-hash cache — duplicate uploads don't double-bill
  • Sandbox keys (sk_*_test_…) return mock JSON without burning quota

Pricing fit

Where expense teams land on the ladder

Recommended tier

Starter to Scale, depending on your end-user volume

Most expense teams start at Starter (€29 / 1,500 pages) for early access and SMB pilots, move to Growth (€99 / 5,000 pages) once customers are scanning a few receipts a day each, and land on Scale (€399 / 25,000 pages) at mid-market scale. Beyond Scale we negotiate an Enterprise floor (~€2,000) with annual commit. Receipts are usually 1 page, so the page count maps cleanly onto receipt count.

Questions expense teams ask

FAQ

What photo quality do my users need to take?

About 150 DPI is the floor — that's a normal phone photo, taken indoors, of a printed receipt. We pre-process for skew, perspective, and uneven lighting before parsing. Images below ~80 DPI or with severe motion blur will return lower per-field confidence; you can use that confidence value to prompt the user to retake the photo client-side. We document the full image-quality guide in the receipt quickstart.

How do you handle currency conversion when my user's card is in a different currency than the receipt?

When a printed receipt shows two currencies (the issuing currency at the till, plus the cardholder currency printed by the foreign-card terminal), parsr returns both — primary_currency and cardholder_currency — plus a detected_fx_rate when one is present on the slip. We do not call an external FX provider; we surface what the receipt says. If you need authoritative FX you can layer that on top, but for reimbursement most teams use what the receipt printed.

Can I map parsr's output to my own category taxonomy?

Yes. parsr returns a stable set of category_hint values (food_beverage, travel_air, lodging, ground_transport, supplies, software, etc.) based on merchant signature and line-item content. You map those hints onto your customer's chart of accounts. We deliberately don't try to be the source of truth for category — every customer's GL is different — we give you a stable signal to map from.

Do you extract individual line items from a receipt, not just the total?

Yes. The receipt.v2 schema includes line_items[] with description, quantity, unit_price, line_total, and tax_rate. This matters for itemized expense policies (alcohol vs food, billable to client vs internal, per-diem caps). Confidence is reported per line, so a high-confidence total on a low-confidence line breakout is a known thing your code can react to.

What fraud signals does parsr surface?

Three soft signals you can layer your policies on. duplicate_hint flags receipts that match a recently parsed merchant+amount+timestamp+total within your org — useful for catching the same coffee submitted twice. tampered_hint flags suspicious overlays, font mismatches, and pixel-level inconsistencies in totals. confidence_low flags any field below your threshold. We don't make the reject decision; we make it cheap to.

Can my customers' auditors see who did what to a receipt?

Every parse writes an audit record — input hash, schema version, model version, requesting key fingerprint, region. Your application is the system of record for who approved which expense; parsr is the system of record for what the document said and when. Audit records are exportable as NDJSON. We don't claim a SOC 2 report yet — the audit window opens Q3 2026 — but the audit trail itself is production today.

200 free pages. No credit card. No sales call.

Ship receipt auto-tagging this sprint. If the multi-language or multi-currency handling doesn't match what your users actually upload, walk away — no lock-in, no procurement.

Start parsing receipts