Developers

API reference

One endpoint. Send a statement, name a format, get the rows back.

Not open yet — this page is the contract it will ship against. Ask for early access

Authentication

Every request goes over HTTPS and carries your API key as a bearer token. Keys are issued per account and can be rolled at any time.

A key reads your statements. Keep it on your server — never in a browser, a mobile app or a repository.

Base URL

https://thestatementconverter.com/api/v1

Header

Authorization: Bearer sk_live_xxxxxxxxxxxx

Convert a statement

POST /api/v1/convert

Send the statement as multipart/form-data, one file per request. Accepted types are pdf, jpg and jpeg, up to 25 MB — a photographed or scanned page is read the same way as a digital PDF.

Request

curl -X POST https://thestatementconverter.com/api/v1/convert \
  -H "Authorization: Bearer sk_live_xxxx" \
  -F "[email protected]" \
  -F "format=json"
Field Type Description
file required file The statement. pdf, jpg or jpeg, up to 25 MB.
format required string One of json, xlsx, csv, tally_xml.
password string For a locked PDF. Used once to open the file, never stored.

The response

json returns the transactions in the body. The other three formats return a file object with a download URL valid for 24 hours; everything else in the response is identical.

balance_check is the part that matters. The rows are added up and compared with the closing balance printed on the statement.

AI models make mistakes. Read balance_check.matched on every response and treat false as a file for a human to open. Never post a sheet into books or a filing on the strength of a conversion nothing has checked — clause 6 of the terms puts that on you.

200 — format: json

{
  "id": "cnv_7Kq2mB4xR9",
  "status": "completed",
  "format": "json",
  "pages": 4,
  "account": {
    "bank": "HDFC Bank",
    "number_masked": "XXXXXX4821",
    "period": { "from": "2026-04-01", "to": "2026-04-30" }
  },
  "balance_check": {
    "closing_balance_stated": 184320.55,
    "closing_balance_computed": 184320.55,
    "matched": true
  },
  "transactions": [
    {
      "date": "2026-04-02",
      "narration": "UPI/DR/409821/RAMESH K/payment",
      "reference": "409821",
      "debit": 2500.00,
      "credit": null,
      "balance": 181820.55
    }
  ]
}

200 — format: xlsx, csv, tally_xml

  "format": "xlsx",
  "file": {
    "url": "https://thestatementconverter.com/api/v1/files/cnv_7Kq2mB4xR9.xlsx",
    "expires_at": "2026-04-30T18:00:00Z",
    "bytes": 18422
  }

Errors

Errors carry the HTTP status and a stable code. Match on the code, never on the message — messages get reworded, codes do not.

400 — error body

{
  "error": {
    "code": "unsupported_file_type",
    "message": "Only pdf, jpg and jpeg are accepted.",
    "field": "file"
  }
}
HTTP Code What happened
400 unsupported_file_type Not a PDF, JPG or JPEG.
400 unsupported_format format missing or not one of the four.
401 invalid_api_key Key missing, malformed, rolled or revoked.
402 allowance_exhausted The month's pages are used up. Nothing converted, nothing charged.
413 file_too_large Over 25 MB.
422 password_required The PDF is locked and no working password was sent.
422 unreadable_statement Opened, but no transactions could be read — usually a poor scan.
429 rate_limited Too many requests. Retry after Retry-After.
503 service_unavailable Not open yet. This is what it returns today.

How pages count

  • One page of the PDF as your bank produced it is one page, whether it carries five transactions or five hundred.
  • A JPG or JPEG is one page.
  • A failed request counts nothing. Pages count on a completed conversion only.
  • API and web share one monthly allowance — a page is a page however it reaches us.

Response header

X-Pages-Used: 4
X-Pages-Remaining: 496

Live example

Pick a file and a format to see the request your code should make, the response it gets back, and what those rows look like once they land in a sheet.

The file is read for its name only. Nothing is uploaded and nothing is sent — not the statement, not the key. The endpoint is not open yet, so the output below is a worked example on a sample statement.

Request

curl -X POST https://thestatementconverter.com/api/v1/convert \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "[email protected]" \
  -F "format=json"

Response 503 service_unavailable

{
  "error": {
    "code": "service_unavailable",
    "message": "The API is not open yet."
  }
}

What lands in your sheet json

Date Narration Debit Credit Balance
2026-04-02 UPI/DR/409821/RAMESH K/payment 2,500.00 1,81,820.55
2026-04-05 NEFT CR/SBIN0001234/ACME TEXTILES 45,000.00 2,26,820.55
2026-04-11 ACH DR/HDFC LIFE INSURANCE/premium 12,400.00 2,14,420.55
2026-04-18 IMPS/P2A/410992/VENDOR PAYOUT 30,100.00 1,84,320.55

Closing balance on the statement — ₹1,84,320.55 — matches the rows added up. That is balance_check.matched: true.

Today every request answers 503. Build one above to see what it returns when the converter is live.