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:

  1. Extraction — Native digital text is read directly from the PDF with layout-aware parsing so columns stay aligned (works across banks and statement layouts).
  2. OCR (when needed) — Scanned or image-only pages are recognized with OCR, preserving table structure and returning per-page confidence alerts.
  3. Normalization — An AI model identifies dates, descriptions, credits, debits, balances, references, and account metadata, and writes each transaction into a strict JSON schema.
  4. 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

  1. Create a named key in the developer console. The full secret (psm_...) is shown exactly once — save it immediately.
  2. Authenticate every request with a Bearer header.
  3. 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.

POST/api/convert

Upload 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 state
  • GET /api/documents/:id/json — normalized statement JSON
  • GET /api/documents/:id/csv — CSV export
  • GET /api/documents/:id/xlsx — Excel export
  • GET /api/documents/:id/input — original PDF bytes

Billing

  • POST /api/billing/checkout — start a checkout session
  • GET /api/billing/subscription — current plan and usage
  • POST /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

POST/api/v1/developer/api-keys

Create a named key. Response includes rawKey — save it immediately; it is shown once.

Auth: None (console only)

GET/api/v1/developer/api-keys

List keys with aggregated request counts, success/failure, and average latency.

Auth: None (console only)

POST/api/v1/developer/api-keys/:id/revoke

Revoke a key. Revoked keys reject all requests immediately.

Auth: Bearer (must match the key)

POST/api/v1/developer/api-keys/:id/rotate

Issue a new secret. Returns rawKey once; the previous secret is invalidated.

Auth: Bearer (must match the key)

GET/api/v1/developer/api-keys/:id/stats

Per-key request, success, failure, and average latency counts.

Auth: Bearer

GET/api/v1/developer/logs

Recent 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.

POST/api/v1/developer/uploads

Upload a PDF (multipart field file). Returns 202 with an upload id immediately.

Auth: Bearer

GET/api/v1/developer/uploads

List your uploads (newest first) with status, row and page counts.

Auth: Bearer

GET/api/v1/developer/uploads/:id

Check status: queued | processing | done | failed.

Auth: Bearer

GET/api/v1/developer/uploads/:id/result

Fetch 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_name string | null

    Detected bank or issuer name.
  • account_holder string | null

    Account holder name when present in the statement.
  • account_number string | null

    Account number, as printed (may be masked).
  • currency string | null

    ISO currency code, e.g. INR.
  • statement_period object | null

    { "from": string, "to": string } statement coverage dates.
  • opening_balance number | null

    Opening balance when stated.
  • closing_balance number | null

    Closing balance when stated.
  • transactions array

    Ordered transaction list, each item as below.

Transaction fields

  • date string

    Transaction date in YYYY-MM-DD when parseable.
  • description string

    Merchant or raw description text.
  • credit number | null

    Money in. Debits leave this null.
  • debit number | null

    Money out. Credits leave this null.
  • balance number | null

    Running account balance after the transaction.
  • reference string | null

    Reference, UPI id, or transaction number.
  • classification string | null

    Merchant 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_statementapi_key, file_name, file_base64
  • get_upload_statusapi_key, upload_id
  • get_upload_resultapi_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 body
  • 401 — missing, invalid, or revoked developer API key
  • 404 — 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 delay
  • 500 — unexpected server error; retry with backoff
  • 503 — 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.