BANK STATEMENTS

Bank statements API

Convert a bank statement PDF into CSV, Excel, or JSON with an asynchronous, team-scoped API. The service is free during preview.

Quickstart

Send your API key as a bearer token. Send the PDF in a multipart field named statement. Set output to csv, xlsx, or json. Add webhook_url when you want a notification instead of continuous polling.

curl --fail-with-body --include \
  -X POST "https://transformy.io/v1/bank-statements" \
  -H "Authorization: Bearer ${TRANSFORMY_KEY}" \
  -F "output=xlsx" \
  -F "webhook_url=https://example.com/webhooks/statement" \
  -F "[email protected];type=application/pdf"

A successful submission returns 202 Accepted. Its Location header identifies the job.

When the job succeeds, download_url points to an authenticated Transformy download endpoint. CSV jobs also return csv_url for older clients.

The download endpoint responds with 302 Found and a short-lived signed Location URL. Follow that redirect to receive the file. Send your bearer key only to the Transformy endpoint; the signed destination needs no authorization header.

curl --fail-with-body --location \
  --output statement.xlsx \
  -H "Authorization: Bearer ${TRANSFORMY_KEY}" \
  "https://transformy.io/v1/bank-statements/${JOB_ID}/xlsx"

curl --location does not forward credentials to a different host by default. Do not use --location-trusted, and do not persist or share the signed redirect URL.

Signed webhooks

webhook_url is optional and must be a public HTTPS URL. Transformy sends one event when the job succeeds or fails. If delivery fails, it retries after 30 seconds, 2 minutes, and 10 minutes.

Every webhook is signed automatically. You can receive webhooks without extra setup. For production automation, verify the signature with the team secret shown under API Keys. The same team secret verifies HTML-to-PDF and bank-statement webhooks.

{
  "id": "evt_...",
  "type": "bank_statement.succeeded",
  "created_at": "2026-08-18T12:00:00Z",
  "data": {
    "object": {
      "object": "bank_statement_job",
      "id": "7cb4...",
      "status": "succeeded",
      "output": "xlsx",
      "error": null,
      "job_url": "https://transformy.io/v1/bank-statements/7cb4..."
    }
  }
}

The webhook contains job metadata only. Use your API key with job_url, then download the result from the returned download_url.

Verify the signature

Read the exact raw request body. Join webhook-id, webhook-timestamp, and the raw body with periods. Calculate HMAC-SHA256 using the base64-decoded part of your whsec_... secret. Compare it with webhook-signature.

  • Reject timestamps older than five minutes.
  • Store webhook-id and ignore duplicate deliveries.
  • Return a 2xx response quickly, then process the event in your own background job.
  • Secret rotation takes effect immediately, so update your receiver with the new secret at the same time.

Lifecycle and endpoints

MethodPathPurpose
POST/v1/bank-statementsValidate, reserve allowance, and queue a job.
GET/v1/bank-statements/{id}Poll queued, processing, succeeded, or failed.
GET/v1/bank-statements/{id}/{output}Authenticate, then receive a 302 redirect to the selected CSV, XLSX, or JSON file.

All three Transformy routes require an active API key. Any key belonging to the job's team may poll or request a download; another team receives 404. A redirect-capable client should follow the download response without forwarding the bearer key across hosts.

Limits

  • Output can be csv, xlsx, or json.
  • 25 accepted conversions per team per calendar month.
  • 2 statements processed concurrently per team, shared across all keys.
  • PDFs may be up to 10 MB and 10 pages.

Submission responses include X-Monthly-Limit, X-Monthly-Remaining, and X-Monthly-Reset. Bank statement jobs do not consume HTML-to-PDF credits.

Errors

Errors use the same JSON envelope as the rest of /v1, with a stable code and request ID. Common codes are:

  • invalid_api_key — the bearer key is missing, invalid, or revoked.
  • invalid_file — the upload is not a supported PDF or exceeds the file/page limit.
  • invalid_parameteroutput or webhook_url is invalid.
  • monthly_limit_exceeded — the team used its calendar-month allowance.
  • rate_limit_exceeded — the team exceeded its request rate.
  • job_not_ready — the output was requested before the job succeeded.
  • not_found — the job belongs to another team, does not exist, or has expired.
  • internal_error — an unexpected error occurred. Retrying creates a new conversion.

Retention and privacy

The source PDF is deleted after processing. The generated output remains in private storage for repeat authenticated downloads for 24 hours. Each authenticated request receives a new short-lived redirect. Download access ends at 24 hours; automated deletion of the output and sensitive extracted fields follows shortly afterward. Non-sensitive job and usage metadata remains for billing and audit work.

Create an API key or use the logged-in bank statement playground to generate a sample in cURL, Node.js, Python, PHP, Go, Ruby, or Java.