FlowParse
REST API · v1

FlowParse API

Validate, reconcile, export and Smart-Merge financial data programmatically — Excel, CSV, XML and direct mappings for 14 accounting packages including Xero, QuickBooks, Sage, DATEV, Zoho Books and NetSuite, plus consolidating up to 100 documents into one workbook. The same engine that powers the app, behind a simple REST API.

Get your API key
Base URL https://flowparse.io/api/v1

Introduction

The FlowParse API lets you run the platform's deterministic validation engine and export engine from your own code. Send extracted invoice or bank-statement data as JSON; get back a validated quality score or a ready-to-import file. Everything you can do in the app is available here: extraction from a PDF, the quality score, Excel/CSV/XML export, and import files for 14 accounting packages — Xero, QuickBooks, Sage, DATEV, Zoho Books, NetSuite, MYOB, Wave, FreshBooks, FreeAgent, the OFX/QBO/QFX bank-file family and 1С.

Validate

Quality Score + every check

Export

Excel · CSV · XML

Accounting

Xero · QuickBooks · Sage · DATEV · +10

Reconcile

Match invoices ↔ transactions

Smart Merge

100 docs → one workbook

Authentication

All requests require an API key sent as a Bearer token. Get a key in one click, or manage existing keys in your dashboard → API tab. The full key (pf_live_…) is shown only once at creation — store it securely. You can also pass it as X-API-Key.

Authorization header
Authorization: Bearer pf_live_xxxxxxxxxxxxxxxxxxxx

Keys are stored as SHA-256 hashes — FlowParse never stores your plaintext key. Revoke a compromised key instantly from the dashboard.

Pricing & billing

0.035 per page — flat, for every document, whatever it takes to process. No range to estimate, no complexity multiplier: a page is a page, and that is what a top-up converts euros into pages at, too.

Every /extract response carries a priceobject — what the document came to and what that worked out at per page — so you can reconcile spend without estimating. API usage draws from its own balance — top-up pages, which never expire — separate from your account's monthly site-plan pages, which the API never spends.

A document is charged once. /extract bills the pages it read, and asking it for a file in the same call — "export": { "format": "xlsx" } — costs nothing extra, in any number of formats. Calling /export on its own bills per page, because that data was never extracted here. /validate, /reconcile, /usage and accounting previews are free, and a document we cannot convert is never billed.

curl — balance & spend
curl https://flowparse.io/api/v1/usage \
  -H "Authorization: Bearer pf_live_xxx"
# → { "plan":"PRO", "pricePerPageEur":0.035,
#     "balance": { "pages":812, "monthlyRemaining":712, "bonusPages":100 },
#     "thisMonth": { "requests":143, "pages":288, "spendEur":8.18 } }
curl — buy pages without leaving your app
curl -X POST https://flowparse.io/api/v1/topup \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "eur": 25, "successUrl": "https://your-app.com/billing/done" }'
# → { "url":"https://checkout.stripe.com/c/pay/cs_live_…", "pages":880,
#     "amountEur":25, "creditedOn":"payment_confirmed" }
# Send your user to `url`; the pages land the moment Stripe confirms the payment.

Add funds to your balance

Bonus pages never expire and are used after your monthly budget.

Add funds

GET /usage

Balance, plan, allowance and month-to-date spend, broken down per key and per day. Free, and it keeps answering after your pages run out — so your own code can decide whether to process, queue or top up before it sends a document.

curl
curl https://flowparse.io/api/v1/usage \
  -H "Authorization: Bearer pf_live_xxx"
# → { "plan":"PRO", "pricePerPageEur":0.035,
#     "balance": { "pages":812, "monthlyRemaining":712, "bonusPages":100 },
#     "thisMonth": { "requests":143, "pages":288, "spendEur":8.18 } }

POST /topup

Buy pages from inside your own application. Send eur for a custom amount (€5–€5000) or pages for a fixed pack, optionally with your own successUrl and cancelUrl. You get a Stripe Checkout link; the pages are credited when the payment settles, never before. Card details never touch your servers or ours. GET /topup returns the same catalogue without starting a payment.

curl
curl -X POST https://flowparse.io/api/v1/topup \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "eur": 25, "successUrl": "https://your-app.com/billing/done" }'
# → { "url":"https://checkout.stripe.com/c/pay/cs_live_…", "pages":880,
#     "amountEur":25, "creditedOn":"payment_confirmed" }
# Send your user to `url`; the pages land the moment Stripe confirms the payment.

Data model

Every request describes a document via type plus its data.

Request body shapes
// Invoice
{ "type": "invoice", "data": { /* invoice fields */ } }

// Bank statement
{ "type": "bank_statement", "data": { /* statement fields */ } }

// Mixed (both on one document)
{ "type": "mixed", "invoice": { ... }, "bank_statement": { ... } }
Invoice fields
{
  "supplier_name": "string",   "supplier_address": "string",
  "customer_name": "string",   "customer_address": "string",
  "invoice_number": "string",  "invoice_date": "YYYY-MM-DD",
  "due_date": "YYYY-MM-DD",    "vat_number": "string",
  "currency": "EUR",
  "subtotal": 100.0, "tax_amount": 19.0, "total": 119.0,
  "line_items": [
    { "description": "string", "quantity": 1, "unit_price": 0,
      "tax_rate": 19, "amount": 0 }
  ]
}
Bank statement fields
{
  "bank_name": "string", "account_holder": "string",
  "account_number_masked": "string", "statement_period": "string",
  "currency": "EUR", "opening_balance": 0.0, "closing_balance": 0.0,
  "transactions": [
    { "date": "YYYY-MM-DD", "description": "string",
      "category": "string", "amount": -12.50, "balance": 0.0 }
  ]
}

POST /extract

Convert a document into structured JSON in one call. Send a PDF, scanned image (PNG/JPG), XLSX or CSV as base64 in the file field (with an optional filename); the same AI pipeline the FlowParse workspace uses classifies the document, reads digital text geometry, OCRs scanned pages, and returns the typed data schema below. Billed per page; a not-convertible document is free. The output is the exact payload the /validate, /export and /reconcile endpoints accept.

curl
curl -X POST https://flowparse.io/api/v1/extract \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "file": "JVBERi0xLjcK...", "filename": "october.pdf" }'
# → { "type":"bank_statement", "pages":4, "billedPages":4,
#     "price": { "eur":0.14, "perPageEur":0.035, "complexity":"standard" },
#     "data": { "type":"bank_statement", "data": { "transactions":[ ... ], ... } } }

Add "validate": true for the quality score the workspace shows, and "export" to get the finished file back in the same response. Both are free — the extraction is the only thing billed.

curl — extract, validate and export in one call
curl -X POST https://flowparse.io/api/v1/extract \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "file": "JVBERi0xLjcK...", "filename": "october.pdf",
        "validate": true, "export": { "format": "xero" } }'
# One charge: the extraction. Returns data + quality score + the Xero import file.

POST /validate

Runs the deterministic validation engine (12 invoice + 8 bank checks). Returns a 0–100 Quality Score, grade (green/yellow/red), every check and a summary.

curl
curl -X POST https://flowparse.io/api/v1/validate \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "invoice",
    "data": {
      "supplier_name": "Acme Ltd",
      "invoice_number": "INV-1024",
      "invoice_date": "2026-05-01",
      "currency": "EUR",
      "subtotal": 100.00,
      "tax_amount": 19.00,
      "total": 119.00,
      "line_items": [
        { "description": "Widget", "quantity": 2, "unit_price": 50, "tax_rate": 19, "amount": 100 }
      ]
    }
  }'
Response
{
  "validations": [
    {
      "documentType": "invoice",
      "score": { "value": 98, "grade": "green" },
      "summary": { "passed": 11, "warnings": 1, "errors": 0 },
      "issues": [ { "severity": "pass", "title": "Totals reconcile", ... } ]
    }
  ]
}

POST /export

Generates a file in the requested format. Spreadsheet formats return text (CSV/XML) or base64 (XLSX); accounting formats also include a preview of the mapped columns and rows.

formatTargetOutput
xlsxExcelStyled multi-sheet workbook (base64)
csvCSVUTF-8, 1:1 source columns + line items
xmlXMLStructured invoice / statement XML
sheetsGoogle SheetsNEWJSON grid — every field, 1 row/line-item
xeroXeroBills & sales invoices CSV
quickbooksQuickBooksImport-ready CSV (plus .QBO bank files)
sageSageSage 50 invoice & transaction CSV
datevDATEVEXTF Buchungsstapel
10 moreSEE BELOWZoho Books, NetSuite, MYOB, Wave, FreshBooks, FreeAgent, OFX/QBO/QFX, 1С
curl — Excel (base64)
curl -X POST https://flowparse.io/api/v1/export \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "format": "xlsx", "type": "invoice", "data": { ... } }'
# → { "format":"xlsx", "filename":"...", "encoding":"base64", "content":"UEsDBB..." }

Accounting exports

Set format to any target below and you get the import file that package expects — not a generic CSV you have to reshape. Each response also carries a preview of the mapping. GET /export returns this list at runtime, so a client never has to hard-code it.

formatPackageWhat you getFile
xeroXeroXero bills & invoices CSV.csv
quickbooksQuickBooksQuickBooks-ready CSV import.csv
sageSageSage 50 invoice & transaction CSV.csv
datevDATEVDATEV EXTF Buchungsstapel.csv
zohoZoho BooksZoho Books invoice import CSV.csv
netsuiteNetSuiteNetSuite CSV import.csv
myobMYOBMYOB sales import CSV.csv
freshbooksFreshBooksFreshBooks transactions CSV.csv
freeagentFreeAgentFreeAgent bank statement CSV.csv
waveWaveWave bank/transaction CSV.csv
qboQuickBooks (.QBO)QuickBooks Web Connect bank file.qbo
qfxQuicken (.QFX)Quicken Web Connect bank file.qfx
ofxOFXOpen Financial Exchange bank file.ofx
onec1С exchange format.txt

Mapping defaults are sensible but never silent: pass options to set the Xero account code and tax type or the invoice direction (xeroMode: bills or sales invoices), the DATEV consultant, client and SKR accounts, or the 1С account and organisation — and onecLanguage when you need 1С output in a particular language. Whatever you override comes back in the preview, so the mapping is visible before anything is imported.

curl — Xero
curl -X POST https://flowparse.io/api/v1/export \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "format": "xero", "type": "invoice", "data": { ... },
        "options": { "xeroMode": "ACCPAY", "accountCode": "400", "xeroTaxType": "Tax Exclusive" } }'
# → { "format":"xero", "filename":"xero-invoices.csv", "encoding":"utf-8",
#     "content":"ContactName,InvoiceNumber,InvoiceDate,DueDate,Description,…",
#     "preview": { "columns":[...], "rows":[...], "notes":[...] } }
curl — QuickBooks
curl -X POST https://flowparse.io/api/v1/export \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "format": "quickbooks", "type": "invoice", "data": { ... } }'

Accounting preview

Add "preview": true to any accounting export — Xero, QuickBooks, Sage, DATEV, any of them — to get back just the mapped table (columns, first rows, total count and mapping notes) without generating the file. Ideal for a confirmation screen before import, and free.

curl — Xero export preview
curl -X POST https://flowparse.io/api/v1/export \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "format": "xero", "preview": true, "type": "invoice", "data": { ... } }'
# → free. { "format":"xero", "preview": { "columns":[...], "rows":[...], "totalRows":1 } }
curl — 1С preview, with the language option
curl -X POST https://flowparse.io/api/v1/export \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "format": "onec", "preview": true, "onecLanguage": "ru", "type": "invoice", "data": { ... } }'
# → { "format":"onec", "preview": { "columns":[...], "rows":[...], "totalRows": 1, "notes":[...] } }

The preview mirrors the app's in-product preview exactly, so what you show your users is what imports.

POST /reconcile

Matches each invoice to your bank transactions and returns an Invoice Status Report — paid, unpaid, partial, duplicate or mismatch — with a confidence score per match. Pass invoices plus either transactions (array) or a bank_statement object. Analysis is free and tracked per key like validation.

curl — reconcile invoices against transactions
curl -X POST https://flowparse.io/api/v1/reconcile \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "invoices": [
      { "supplier_name": "Acme Ltd", "invoice_number": "INV-1024",
        "total": 119.00, "currency": "EUR", "invoice_date": "2026-05-01" }
    ],
    "transactions": [
      { "date": "2026-05-03", "description": "ACME LTD INV-1024", "amount": -119.00 }
    ]
  }'
# → { "report": {
#       "summary": { "totalInvoices":1, "matched":1, "unpaid":0, "duplicate":0,
#                    "mismatch":0, "partial":0, "unmatchedTransactions":0 },
#       "invoices": [ { "status":"paid", "confidence":0.98, "matches":[...] } ],
#       "unmatched": [], "currency":"EUR", "generatedAt":"..." } }

POST /merge NEW

Smart Merge consolidates 2–100 already-extracted documents into one Excel workbook: invoices become an Invoice Register (one row per document) and bank statements become a unified Transactions sheet whose columns are matched across different bank formats (e.g. Transaction Date, Datum and Date collapse into one column). Every source row is preserved 1:1 — the response summary returns transactionRows and sourceTransactionRows so you can assert no row was dropped.

FieldTypeNotes
documentsarray (2–100)Each item is a validate/export-shaped document (type + data), with an optional fileName.
previewbooleanWhen true, returns the summary + a 20-row peek per sheet, with no file. Free.
curl — merge many documents into one workbook
curl -X POST https://flowparse.io/api/v1/merge \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "documents": [
      { "fileName": "jan.pdf", "type": "bank_statement", "data": { ... } },
      { "fileName": "feb.pdf", "type": "bank_statement", "data": { ... } },
      { "fileName": "acme-invoice.pdf", "type": "invoice", "data": { ... } }
    ]
  }'
# → { "format":"xlsx", "filename":"smart-merge-2026-06-17.xlsx",
#     "encoding":"base64", "content":"UEsDBB...",
#     "summary": { "fileCount":3, "transactionRows":842, "sourceTransactionRows":842,
#                  "schemaGroups":2, "currencies":["USD"], ... }, "billedPages": 9 }
curl — free preview (summary + sample rows, no file)
curl -X POST https://flowparse.io/api/v1/merge \
  -H "Authorization: Bearer pf_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "preview": true, "documents": [ { "type":"bank_statement", "data":{...} }, ... ] }'
# → free. { "summary": {...}, "sheets": [ { "name":"Transactions", "columns":[...],
#     "rowCount":842, "sampleRows":[...] } ], "billedPages": 0 }

Billed per page summed across all documents; the free preview lets you check the merged schema and row count before you spend pages.

Code examples

Node.js

JavaScript (fetch)
const res = await fetch("https://flowparse.io/api/v1/export", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.FLOWPARSE_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ format: "xero", type: "invoice", data: invoice }),
})
const { content, filename } = await res.json()
require("fs").writeFileSync(filename, content, "utf8")

Python

Python (requests)
import os, base64, requests

r = requests.post(
    "https://flowparse.io/api/v1/export",
    headers={"Authorization": f"Bearer {os.environ['FLOWPARSE_API_KEY']}"},
    json={"format": "xlsx", "type": "bank_statement", "data": statement},
)
out = r.json()
with open(out["filename"], "wb") as f:
    f.write(base64.b64decode(out["content"]))  # xlsx is base64

Errors

Errors return a JSON body { "error": "message" } with a standard HTTP status.

400

Bad Request

Malformed body, missing data, unsupported format, or fewer than 2 documents for /merge.

401

Unauthorized

Missing, invalid or revoked API key.

422

Unprocessable

No mergeable / reconcilable data in the supplied documents.

429

Too Many Requests

Page balance exhausted — top up to continue.

500

Server Error

Unexpected error generating the response.

Rate limits

Throughput is governed by your page balance rather than a fixed request cap. If your top-up pages are exhausted, requests that consume pages return 429 until you top up. Validation of already-extracted JSON is free.

Ready to build? Generate your first key in one click.

Get API key