Developer reference
ParseMyStatement API & MCP
Convert bank statement PDFs into normalized JSON, CSV, and Excel through one authenticated API. This reference covers the web & session APIs, the machine-to-machine Developer API v1, the statement JSON schema, and the Model Context Protocol (MCP) endpoint for AI agents.
Overview
ParseMyStatement turns PDF bank statements into structured, machine-readable output. Every statement is processed asynchronously: you submit a PDF, poll for status, and fetch the finished result when it is ready. The same normalized output is returned whether you convert through the website, the session APIs, the Developer API, or MCP.
- Three output formats — JSON (single normalized document), CSV, and Excel (.xlsx), each derived from the same parsed data.
- No webhooks required — use the simple upload → poll → result flow instead.
- Per-key visibility — a developer key only ever sees its own uploads and request logs.
- Named keys — create separate keys for production, staging, and scripts, then revoke or rotate them independently.
How it works
Every statement follows the same pipeline, regardless of which surface you use:
- Extraction — Native digital text is read directly from the PDF with layout-aware parsing so columns stay aligned (works across banks and statement layouts).
- OCR (when needed) — Scanned or image-only pages are recognized with OCR, preserving table structure and returning per-page confidence alerts.
- Normalization — An AI model identifies dates, descriptions, credits, debits, balances, references, and account metadata, and writes each transaction into a strict JSON schema.
- Reconciliation & classification — Balance reconciliation corrects swapped debit/credit columns, and classification rules replace generic bank labels (e.g. "ONLINE TRANSACTION") with merchant names inferred from UPI, POS, or reference text.
Processing is fully asynchronous. Acceptable input is a single PDF per upload; results are retained for a limited window, so download or store them soon after completion.
Quickstart
- Create a named key in the developer console. The full secret (
psm_...) is shown exactly once — save it immediately. - Authenticate every request with a Bearer header.
- Upload a PDF, poll for status, then fetch the result.
export PSM_API_KEY="psm_live_..."
# 1. Upload the PDF (async; returns an upload id)
curl -X POST https://parsemystatement.com/api/v1/developer/uploads \
-H "Authorization: Bearer $PSM_API_KEY" \
-F "[email protected]"
# => 202 { "upload": { "id": "upload_...", "status": "queued", "createdAt": "..." } }
# 2. Poll until status is "done" or "failed"
curl https://parsemystatement.com/api/v1/developer/uploads/upload_abc123 \
-H "Authorization: Bearer $PSM_API_KEY"
# 3. Fetch the normalized result
curl https://parsemystatement.com/api/v1/developer/uploads/upload_abc123/result \
-H "Authorization: Bearer $PSM_API_KEY"A single upload usually completes in seconds. Poll every 2–3 seconds; use exponential backoff for long scans.
Web & session APIs
The website is powered by these endpoints. They use your login session and are ideal for authenticated single-file conversions; guests get a rate-limited, inline response.
/api/convertUpload a PDF. Authenticated users get stored documents; guests receive an inline JSON response once processing completes.
Auth: Session cookie (authenticated) or guest (rate-limited)
Multipart form field: file (PDF only).
curl -X POST https://parsemystatement.com/api/convert \
-H "Cookie: <session>" \
-F "[email protected]"Stored documents (authenticated)
GET /api/documents— list documents (24h retention)GET /api/documents/status— latest processing stateGET /api/documents/:id/json— normalized statement JSONGET /api/documents/:id/csv— CSV exportGET /api/documents/:id/xlsx— Excel exportGET /api/documents/:id/input— original PDF bytes
Billing
POST /api/billing/checkout— start a checkout sessionGET /api/billing/subscription— current plan and usagePOST /api/billing/webhooks— payment lifecycle notifications (server-to-server)
Developer API v1
The Developer API is for machine-to-machine access. Keys are identified by a public prefix (psm_...) and authenticated with the full secret. Only a hash of the secret is ever stored, so the full value is shown once at creation and once on rotation.
Manage keys in the developer console, or download the machine-readable spec: /developers/openapi.json.
Authentication
Send the full secret as a Bearer token. Never put keys in client-side code or public repositories.
Authorization: Bearer psm_<your-secret-key>Key lifecycle
- Create — returns the secret once; treat it like a password.
- Rotate — issues a new secret and returns it once. Old key stops working; update your integration promptly.
- Revoke — immediately disables the key. Keep one key per environment so you can revoke without disrupting the rest.
Key management endpoints
/api/v1/developer/api-keysCreate a named key. Response includes rawKey — save it immediately; it is shown once.
Auth: None (console only)
/api/v1/developer/api-keysList keys with aggregated request counts, success/failure, and average latency.
Auth: None (console only)
/api/v1/developer/api-keys/:id/revokeRevoke a key. Revoked keys reject all requests immediately.
Auth: Bearer (must match the key)
/api/v1/developer/api-keys/:id/rotateIssue a new secret. Returns rawKey once; the previous secret is invalidated.
Auth: Bearer (must match the key)
/api/v1/developer/api-keys/:id/statsPer-key request, success, failure, and average latency counts.
Auth: Bearer
/api/v1/developer/logsRecent request logs — route, method, status, latency, error. A key sees only its own logs; an admin sees all keys.
Auth: Bearer (or admin session)
Create a key
curl -X POST https://parsemystatement.com/api/v1/developer/api-keys \
-H "Content-Type: application/json" \
-d '{ "name": "production-upload" }'
# => 201 {
# "key": { "id": "key_...", "name": "production-upload", "prefix": "psm_live_a1b2" },
# "rawKey": "psm_live_a1b2c3d4..."
# }Upload → poll → result
No webhooks. Upload returns an id immediately; poll the status endpoint until it reports done or failed, then fetch the result. The status endpoint returns 404 if the id is unknown to your key, and the result endpoint returns 202 while processing is still in progress.
/api/v1/developer/uploadsUpload a PDF (multipart field file). Returns 202 with an upload id immediately.
Auth: Bearer
/api/v1/developer/uploadsList your uploads (newest first) with status, row and page counts.
Auth: Bearer
/api/v1/developer/uploads/:idCheck status: queued | processing | done | failed.
Auth: Bearer
/api/v1/developer/uploads/:id/resultFetch outputJson and outputCsv when done; 202 while not ready.
Auth: Bearer
Example flow
# 1. Upload
curl -X POST https://parsemystatement.com/api/v1/developer/uploads \
-H "Authorization: Bearer $PSM_API_KEY" \
-F "[email protected]"
# => 202 { "upload": { "id": "upload_abc123", "status": "queued", "createdAt": "..." } }
# 2. Poll status
curl https://parsemystatement.com/api/v1/developer/uploads/upload_abc123 \
-H "Authorization: Bearer $PSM_API_KEY"
# => 200 { "upload": { "id": "upload_abc123", "fileName": "statement.pdf", "status": "done",
# "rowsDetected": 42, "pagesDetected": 2, "completedAt": "..." } }
# 3. Fetch result when status is done
curl https://parsemystatement.com/api/v1/developer/uploads/upload_abc123/result \
-H "Authorization: Bearer $PSM_API_KEY"
# => 200 { "result": { "id": "upload_abc123", "status": "done", "rowsDetected": 42,
# "outputJson": { ... }, "outputCsv": "date,description,..." } }Statement JSON schema
All surfaces return the same normalized shape:
{
"bank_name": null,
"account_holder": "Jane Doe",
"account_number": "0006",
"currency": "INR",
"statement_period": { "from": null, "to": null },
"opening_balance": null,
"closing_balance": null,
"transactions": [
{
"date": "2026-05-16",
"description": "ZOMATO",
"debit": 462.98,
"credit": null,
"balance": 13948.41,
"reference": "UPI20260516000771719",
"classification": "ZOMATO"
}
]
}Top-level fields
bank_namestring | nullDetected bank or issuer name.account_holderstring | nullAccount holder name when present in the statement.account_numberstring | nullAccount number, as printed (may be masked).currencystring | nullISO currency code, e.g. INR.statement_periodobject | null{ "from": string, "to": string } statement coverage dates.opening_balancenumber | nullOpening balance when stated.closing_balancenumber | nullClosing balance when stated.transactionsarrayOrdered transaction list, each item as below.
Transaction fields
datestringTransaction date in YYYY-MM-DD when parseable.descriptionstringMerchant or raw description text.creditnumber | nullMoney in. Debits leave this null.debitnumber | nullMoney out. Credits leave this null.balancenumber | nullRunning account balance after the transaction.referencestring | nullReference, UPI id, or transaction number.classificationstring | nullMerchant classification inferred from UPI/POS text, otherwise the raw label.
CSV columns: Date, Description, Credit, Debit, Balance, Reference, Classification. Debit is a positive number in CSV; empty cells are empty strings.
Model Context Protocol (MCP)
AI agents can connect over streamable HTTP JSON-RPC. Discovery files are published for MCP clients and crawlers.
- MCP endpoint:
https://parsemystatement.com/api/mcp - Server card:
https://parsemystatement.com/.well-known/mcp/server-card.json - AI plugin manifest:
https://parsemystatement.com/.well-known/ai-plugin.json - Agent catalog:
https://parsemystatement.com/.well-known/agents.json
Available tools
upload_bank_statement—api_key,file_name,file_base64get_upload_status—api_key,upload_idget_upload_result—api_key,upload_id
Each tool accepts the full developer API key in api_key. Results are the same normalized JSON and CSV as the REST API.
Initialize (JSON-RPC)
curl -X POST https://parsemystatement.com/api/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {},
"clientInfo": { "name": "my-agent", "version": "1.0.0" }
}
}'Call a tool
curl -X POST https://parsemystatement.com/api/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "upload_bank_statement",
"arguments": {
"api_key": "psm_...",
"file_name": "hsbc.pdf",
"file_base64": "<base64>"
}
}
}'After upload_bank_statement, poll with get_upload_status until status is done, then call get_upload_result.
Errors & limits
400— invalid PDF, missing form field, or malformed body401— missing, invalid, or revoked developer API key404— unknown upload id (or an upload that does not belong to your key)202— accepted (uploads) or still processing (result not ready yet)429— rate limit exceeded; retry after the returned delay500— unexpected server error; retry with backoff503— service temporarily unavailable; try again shortly
Errors return a JSON body with an error message. Plan limits (documents/pages per month) apply to authenticated usage — see pricing on the homepage or your billing page for the exact caps.
Best practices
- Store secrets in a vault or environment variable; never hardcode or commit them.
- Use one key per environment so revocation is surgical.
- Rotate keys on a schedule and whenever a secret may have leaked.
- Poll with backoff instead of tight loops to stay within rate limits.