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.