PDFCraft
Extraction guide

Extract a bank statement PDF to JSON

Everything below is the response the live API returned for a bank statement, generated when this page was built. Not a description of what it would return — the output itself, including what it scored low on.

Who this is for

Lending, bookkeeping and personal-finance products that ask a customer to upload statements because the bank has no API worth using.

The job, in the words people search for: “get transactions out of a bank statement PDF”.

What makes this shape awkward

A statement is one logical table the renderer broke across every page, repeating its header each time. Extractors that work page by page return one table per page, each with the header row sitting in the data.

The measured result

Pages read1
Time35 ms (35 ms/page)
Labelled fields found unprompted4
Tables1 (38 rows)
Inputbank-statement.pdf (31 KB — synthetic, generated from a spec in the repo; real documents of this type cannot be published)

Fields, with a schema

Ask for the fields you want by the label printed on the page. Each answer carries the text exactly as printed (raw), the value coerced to the type you asked for, a confidence, and a bounding box you can draw on the page to check it.

curl -X POST https://api.pdfcraft.dev/v1/extract \
  -H "Authorization: Bearer $PDFCRAFT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "file": "<base64 of your bank statement>",
    "schema": {
      "account_number": "string",
      "opening_balance": "currency",
      "closing_balance": "currency"
    }
  }'

What came back

Fieldrawvalueconfidence
account_number•••• •••• 4417"•••• •••• 4417"60%
opening_balanceGBP 18,402.1118402.11 GBP70%
closing_balanceGBP 22,178.4422178.44 GBP70%

Every label it found without being asked

Send no schema and you get all of these, keyed by the label as printed. Useful for discovering what a new supplier’s layout actually contains before you write a schema against it.

Account Number, Statement Period, Opening Balance, Closing Balance

Tables

Date · Description · Debit · Credit · Balance · 38 rows × 5 columns · page 1

Table confidence 100%. Header confidence 100% — the first row was promoted out of the data.

DateDescriptionDebitCreditBalance
2026-02-01Card payment887.00—18,629.00
2026-02-02Direct debit1,722.00—18,828.00
2026-02-03Faster payment in1,719.00—18,683.00

First 3 of 38 rows.

With options.rows_as_objects, the same row keyed by its header:

{
  "Date": "2026-02-01",
  "Description": "Card payment",
  "Debit": "887.00",
  "Credit": "",
  "Balance": "18,629.00"
}

What this does not do

Try it on your own bank statement

The playground takes a file and shows the same JSON, with every bounding box drawn over the page. No key and no signup for the first few; a free key gives you 100 pages a month.

Related