Bank statement API: Statements in over HTTP, transactions out as JSON.
POST a bank statement PDF, CSV, XLSX, or JSON file to one endpoint and get the parsed, balance-checked transactions back in the response. Or queue it, and let a signed webhook tell your system when it is done. No template per bank, no parser to maintain.
50 free page credits · No card · Cancel anytime
- One-shot parse endpoint
- Signed webhooks
- OpenAPI spec
Forward statements to
statements@mail.bankflow.app
Workflows watch
Google Drive
OneDrive
DropboxPOST /api/v1/parse
Authorization: Bearer bnfw_••••
200 { "transactions": 142 }
Every bank prints its statement differently, and keeps changing it.
Building statement parsing in-house starts as a regex for one bank's layout and becomes a maintenance line item: a second bank, a redesigned PDF, a scanned upload, a card statement whose credits sit in the wrong column. Each fix is small and none of them end. The part of your product that needs the transactions does not care how they were read; it needs them structured, with a signal when the numbers do not add up. That is the part worth buying rather than building.
How it works
Wiring statements into your product
- 01
Create a token
Generate a personal access token in the app. Tokens carry a bnfw_ prefix, can be revoked at any time, and authenticate every request as a Bearer header.
- 02
Parse in one call
POST the file to /api/v1/parse as a raw PDF body or multipart form data. The response carries the stored statement, with its bank, currency, and balances, and the transactions inline. Files up to 50 MB.
- 03
Or queue it and listen
Upload to /api/v1/statements and return immediately. When the parse finishes, transactions.created delivers the full transaction list to your endpoint; statement.failed tells you when a file could not be read.

A webhook endpoint, the events it subscribes to, and its signing secret. - 04
Read, correct, export
List a statement's transactions, update a transaction's category, notes, or tags, pull a statement's insights, or ask for an export in any of the product's formats, all over the same API.
In code
One POST, and the transactions are back
The one-shot endpoint holds the request open while the file is read, checks the rows against the balances the statement prints, and returns both the statement and its transactions. No polling, no job id to track.
Request
curl -X POST https://api.bankflow.app/api/v1/parse \
-H "Authorization: Bearer bnfw_live_xxx" \
-H "Content-Type: application/pdf" \
--data-binary @statement.pdfResponse
{
"data": {
"id": 8412,
"fileName": "statement.pdf",
"status": "completed",
"bankName": "Chase",
"currency": "USD",
"transactionCount": 38,
"openingBalance": 4820.11,
"closingBalance": 6190.44
},
"transactions": [
{
"id": 90211,
"date": "2026-08-01",
"description": "STRIPE PAYOUT 4AF2C9",
"amount": 4820.50,
"type": "credit",
"category": "Income",
"tags": [],
"balance": 9640.61
}
]
}Files up to 50 MB, as a raw body or multipart form data. Queue the same file at POST /api/v1/statements instead and the result arrives as a signed webhook.
The API surface
Structured transactions
Date, description, amount, type, category, and the statement's opening and closing balances, as JSON your code can use directly.
Five webhook events
statement.completed, transactions.created, statement.failed, statement.deleted, and transaction.updated, each signed with a secret you can rotate.
More than PDFs
CSV, XLSX, and JSON statements go through the same endpoints and come back in the same shape, with a column-detection endpoint for tabular files.
Revocable tokens
Create, copy, and revoke tokens from the app. A leaked token is one click from useless.
An OpenAPI spec
A machine-readable specification at /api/v1/openapi.json for client generation and Swagger-style tooling.
Other ways in, same events
A statement forwarded by email fires the same transactions.created event as an API upload, so one handler covers both routes in.
Endpoints at a glance
The full reference, with request and response shapes, is in the API docs.
| Endpoint | What it does |
|---|---|
| POST /api/v1/parse | Parse a file and return its transactions inline |
| POST /api/v1/statements | Queue a file; results arrive by webhook |
| GET /api/v1/statements/:id/transactions | List a statement's transactions |
| GET /api/v1/statements/:id/export | Export a statement in a chosen format |
| PATCH /api/v1/transactions/:id | Update category, notes, or tags |
| POST /api/v1/webhooks | Register an endpoint and receive its signing secret |
Base URL https://api.bankflow.app. Every endpoint takes a Bearer token. Requests are budgeted per key, at 60 a minute on the free plan and 120, 300, or 600 on the paid ones, and every response carries RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset so a client can pace itself rather than discover the limit by tripping it.
FAQ
Common questions
Is parsing synchronous or asynchronous?
Both are available. /api/v1/parse holds the request open and returns the transactions in the response; /api/v1/statements accepts the file and returns straight away, with the result delivered by webhook.
What happens when a statement does not balance?
The parse validates running balances against the statement's printed opening and closing figures. A statement that cannot be reconciled is flagged for review rather than returned as if it were clean.
How are webhooks secured?
Each endpoint gets a signing secret, shown once when you register it, and deliveries are signed with it. The secret can be rotated through the API without re-registering the endpoint.
What does a parsed page cost?
One credit, where a credit is one standard page, and the same credit whether the file arrives through the API or the app. That works out at about 5 cents a page on the $14.99 plan, 4 cents on the $39.99 plan, and 3.3 cents on the $99.99 plan, with top-up packs at 4 cents. API access itself costs nothing extra and is on every plan, the free one included. Note that page-priced and document-priced APIs are not comparable until you know your page count: a per-document price looks cheaper until a statement runs to twelve pages.
What are the rate limits?
Sixty requests a minute per key on the free plan, and 120, 300, or 600 on the paid plans. Uploads are bounded separately by how many documents an account can parse at once and by a daily document limit. Every response carries RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, and RateLimit-Policy, and a 429 carries Retry-After, so backing off correctly takes no guesswork.
How is this different from open banking, such as Plaid or TrueLayer?
An open banking provider connects to the account itself and needs the account holder to authenticate, which gives you live transactions and typically 90 days to 24 months of history depending on the bank. A statement API needs no connection and no credentials: it reads the document your user already has, which means older history than any feed reaches, banks no aggregator covers, closed accounts, and files a user can hand over by email. Plenty of products use both: a feed for ongoing data, statements for the history and the banks the feed cannot see.
Which banks are supported?
There is no per-bank template. The parser reads the statement's layout itself, including scanned and password-protected PDFs, so a bank's first statement works the same way as its hundredth.
Is there a sandbox?
The free tier is the sandbox: 50 page credits, the full API, and webhooks, with no card required.
Keep exploring
For Bookkeepers
Speed up monthly reconciliation and recurring client work.
For Accountants
Cut statement data entry out of close and year-end prep.
For Accounting Firms
Standardize statement intake across every client account.
For Small Businesses
Categorized books and a runway figure, without hiring help.
For Freelancers
Split business from personal and hand over a clean year.
Parse your first statement with curl.
Create a free account, generate a token, and POST a PDF. The transactions come back in the response.