Developer API
Bank statement OCR & parsing API
Turn statement PDFs, scans, and photos into structured transactions with three REST calls. Vision-based extraction works with any bank; every row is verified against the statement's running balance before you ever see it.
Quickstart
Create a key at /dashboard/api (Business plan or higher), then it's upload → poll → export:
# 1. Upload
curl -X POST https://parsebank.io/api/v1/statements \
-H "Authorization: Bearer sk_live_..." \
-F "[email protected]"
# → { "data": { "id": "abc123", "status": "processing", "pages": 12 } }
# 2. Poll until completed
curl https://parsebank.io/api/v1/statements/abc123 \
-H "Authorization: Bearer sk_live_..."
# 3. Download the export
curl -o statement.csv \
"https://parsebank.io/api/v1/statements/abc123/export?format=csv" \
-H "Authorization: Bearer sk_live_..."Authentication
Every request carries your key as a Bearer token: Authorization: Bearer sk_live_…. Keys are shown once at creation and can be revoked instantly from the dashboard. We store only a hash — treat the key like a password and keep it server-side.
Node.js
const BASE = "https://parsebank.io/api/v1";
const KEY = process.env.STATEMENT_API_KEY; // sk_live_...
const headers = { Authorization: `Bearer ${KEY}` };
// 1. Upload
const form = new FormData();
form.append("file", new Blob([await fs.readFile("statement.pdf")]), "statement.pdf");
const upload = await fetch(`${BASE}/statements`, {
method: "POST",
headers: { ...headers, "Idempotency-Key": "invoice-2026-07-run1" },
body: form,
});
const { data: { id } } = await upload.json();
// 2. Poll until completed
let statement;
do {
await new Promise((r) => setTimeout(r, 2000));
statement = (await (await fetch(`${BASE}/statements/${id}`, { headers })).json()).data;
} while (statement.status === "processing");
if (statement.status === "failed") throw new Error(statement.error.message);
console.log(statement.validation.balance_check, statement.transactions.length, "rows");
// 3. Export as CSV
const csv = await (await fetch(`${BASE}/statements/${id}/export?format=csv`, { headers })).text();Python
import os, time, requests
BASE = "https://parsebank.io/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['STATEMENT_API_KEY']}"}
# 1. Upload
with open("statement.pdf", "rb") as f:
r = requests.post(
f"{BASE}/statements",
headers={**HEADERS, "Idempotency-Key": "invoice-2026-07-run1"},
files={"file": ("statement.pdf", f, "application/pdf")},
)
r.raise_for_status()
statement_id = r.json()["data"]["id"]
# 2. Poll until completed
while True:
data = requests.get(f"{BASE}/statements/{statement_id}", headers=HEADERS).json()["data"]
if data["status"] != "processing":
break
time.sleep(2)
if data["status"] == "failed":
raise RuntimeError(data["error"]["message"])
print(data["validation"]["balance_check"], len(data["transactions"]), "rows")
# 3. Export as CSV
csv = requests.get(
f"{BASE}/statements/{statement_id}/export", params={"format": "csv"}, headers=HEADERS
).textErrors
Failures return { "error": { "code", "message" } }:
| Code | HTTP | Meaning |
|---|---|---|
| invalid_api_key | 401 | Missing, malformed, or revoked key |
| plan_required | 403 | API needs the Business plan or higher |
| insufficient_credits | 402 | Document exceeds remaining page credits |
| file_too_large | 413 | Over 50MB or over 100 pages |
| unsupported_format | 415 | Not a PDF, JPEG, or PNG |
| password_required | 422 | Protected PDF — send the password field |
| password_incorrect | 422 | Wrong PDF password |
| extraction_failed | — | Job failed; credits refunded automatically |
| not_found | 404 | No statement with this id on your account |
| rate_limited | 429 | Over 60 req/min — honor Retry-After |
Rate limits & credits
- 60 requests/minute per key (sliding window). A 429 includes a
Retry-Afterheader — honor it. - 1 page = 1 credit, from the same pool as the web app (Business: 500/mo, Corporate: 2,000/mo). Check your balance with
GET /api/v1/me. - Credits are reserved when a job is accepted and refunded automatically on failure — you only pay for completed conversions.
- Retries are safe: send an
Idempotency-Keyheader and replays within 24h return the original statement.
Data retention
Original files are deleted from storage as soon as page rendering completes — before extraction even finishes. Extracted transactions follow your account's retention setting (7 days to 1 year, or Never) and can be removed immediately with DELETE /api/v1/statements/{id}. PDF passwords are used once in memory and never stored. ParseBank never trains AI models on your data.
FAQ
What does the bank statement API return?
Structured transactions — date, description, debit, credit, balance, currency — as JSON, CSV, or XLSX. Every row is verified against the statement's printed running balance, and each statement reports a balance_check of passed, failed, or n/a.
Which banks and formats are supported?
Any bank — extraction is vision-based, not template-based, so it works with statements from any institution, in English, French, German, Spanish, or Italian. PDFs, scans, and phone photos are all supported, including password-protected PDFs.
How is the API priced?
API calls use the same page credits as the web app: 1 page = 1 credit, included in Business ($10/mo, 500 pages) and Corporate ($20/mo, 2,000 pages) plans. Credits are only charged for completed conversions — failures refund automatically.
Is uploaded data retained?
Original files are deleted the moment page rendering completes. Extracted data follows your retention setting (7 days to a year, or Never) and can be deleted anytime via the API. Nothing is used to train AI models.