Bank statement API
Send a PDF bank or credit card statement, get every transaction back as JSON — or as Excel, CSV, QuickBooks, OFX, QIF or Xero files. The same reader as the website, with the running-balance check. Free, no limits.
1. Get a key
Create a free account, confirm your email, and create a key on your API page. Keys look like sg_live_… and are shown once.
2. Convert a statement
curl https://statementeasy.com/api/v1/convert \ -H "Authorization: Bearer sg_live_your_key" \ -F [email protected]
Answer (shortened):
{
"bank": { "slug": "barclays", "name": "Barclays" },
"pages": 2,
"period": { "from": "2025-09-25", "to": "2025-10-01" },
"opening_balance": 90601.11,
"closing_balance": 122693.89,
"total_in": 40657.5,
"total_out": 8564.72,
"check": { "status": "ok", "checked_rows": 7, "mismatched_rows": 0,
"message": "Balances match on all 7 checked rows" },
"count": 8,
"transactions": [
{ "date": "2025-10-01", "value_date": null,
"description": "Bill Payment ARTHUR J GALLAGHER 548838138 BBP",
"amount": -566.72, "money_in": null, "money_out": 566.72,
"balance": 122693.89, "balance_check": "ok" }
]
}
check.status is ok when every row matches the running balance, totals when the statement has no running balance but opening + in − out = closing, warn when something does not add up (look at rows with "balance_check": "mismatch"), and none when there is nothing to check against.
3. Or get a file
curl https://statementeasy.com/api/v1/convert \ -H "Authorization: Bearer sg_live_your_key" \ -F [email protected] -F format=qbo -o statement.qbo
Parameters
| Field | Values | Default |
|---|---|---|
file | The statement (multipart), up to 25 MB: a PDF (text or scanned), a photo (JPG, PNG, WebP, HEIC, TIFF), or a CSV, Excel (.xlsx), OFX, QFX, QBO, QIF, MT940 or CAMT.053 bank file | required |
format | json, xlsx, csv, qbo, qfx, ofx, qif, iif, mt940, camt, xero, qbocsv | json |
pages | Only these pages of a PDF, e.g. 2-5,8 | all |
password | Password of a protected PDF | — |
date_format | iso, dmy, mdy (files only) | iso |
layout | split (money in / out) or signed (one amount), for xlsx/csv | split |
categories | true adds a category to each transaction | false |
payee | true adds a Payee column (clean merchant name) to xlsx/csv. JSON always includes payee. | false |
rules | JSON array of your rules: [{"match":"amazon","payee":"Amazon","category":"Shopping","sign":"out"}]. A rule applies when the description contains match (any case, accents ignored); the first match wins; sign is optional. | — |
insights | true adds insights to the JSON: months, categories, top payees, average daily balance, days below zero, regular income, recurring payments, large deposits, returned payments and overdraft fees | false |
document_check | true adds document_check to the JSON: verdict (clean, review or edited) and the reasons (editing tools, saves after creation, amounts in a different font, balance breaks). Text PDFs only. It finds traces of editing; it cannot prove a file is genuine. | false |
mask | true hides account and card numbers, IBANs and emails (last 4 digits kept) | false |
credit_card | true when the PDF is a card statement with unsigned amounts | false |
date_order | DMY or MDY to force how 03/04 is read | detected |
bank | A bank slug from supported banks | detected |
currency | ISO code for OFX/QBO, e.g. GBP | from the bank, else USD |
Scans and photos
Scanned PDFs and photos are read with OCR on our server, then converted like any other statement. The call waits up to about 100 seconds; most one- or two-page scans are back well before that. If it takes longer, you get 202 with a job id:
{ "status": "processing", "job": "9a8c8c2a-…", "status_url": "https://statementeasy.com/api/v1/jobs/9a8c8c2a-…" }
Call GET /api/v1/jobs/{id} with the same key: 202 while it runs, then the same answer /convert would have given (JSON or file). Results are kept for 30 minutes. Add webhook_url=https://… to be called with {"id", "status"} when the job ends, and ocr_language=fr (or ar, es, pt, de) for statements that are not in English.
Errors
Errors come back as {"error": {"code": "…", "message": "…"}} with an HTTP status: 401 missing or invalid key, 413 file too large, 422 for not_a_pdf, password_required, wrong_password, ocr_failed, no_table, no_movements, 429 above 60 requests a minute per key, and 503 when the converter is busy (retry after a few seconds).
Check a key
curl https://statementeasy.com/api/v1/me -H "Authorization: Bearer sg_live_your_key"
OpenAPI and Postman
The full description is in openapi.json (OpenAPI 3). Import it into Postman, Insomnia or a code generator to get a ready client.
What happens to the file
Unlike the website, the API needs the PDF on our server: it is written to a private temporary file, converted, and deleted as soon as the answer is ready — usually within a second or two. It is never stored, logged or looked at. We keep a usage record per call (time, size, pages, rows, result) to run the service, without any statement content.
Scans and photos are read by our OCR worker: the file is deleted as soon as it has been read, and the converted result is kept encrypted for 30 minutes so you can collect it, then deleted.