# Bank Statement Converter - camt.053 / MT940 to JSON, CSV, Ex (`martin10291/bank-statement-converter`) Actor

No AI, no software install. Converts camt.053 (ISO 20022, .001.02-.08), camt.052 and MT940 bank statements, single or zipped, to JSON rows, CSV, Excel (xlsx) or MT940 for older accounting tools. Every statement gets a balance check (opening + entries = closing). Counterparty, IBAN, BIC, remittance,

- **URL**: https://apify.com/martin10291/bank-statement-converter.md
- **Developed by:** [Martin Petrov](https://apify.com/martin10291) (community)
- **Categories:** Developer tools, Automation, Business
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.70 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Bank Statement Converter - camt.053 / MT940 to JSON, CSV, Excel

**Problem:** your bank switched account statements from MT940 to camt.053 (ISO 20022 XML) - Commerzbank EBICS from 2026-11-16, many German, Dutch and Polish banks during 2025-2026 - and your accounting import, ERP integration or spreadsheet still expects MT940 or a simple table. Or you get camt/MT940 files from clients and need them as JSON or Excel. Desktop converters cost EUR 39-180 per year and run only on Windows.

**This API:** send the file (link or content), get it back as JSON rows, CSV, Excel (xlsx) or MT940 - in about a second, no AI, no account at a converter site.

- **Inputs:** camt.053 versions .001.02 / .04 / .08 (any .001.NN), camt.052, MT940 (with or without SWIFT envelope), and a .zip with up to 50 files. Raw text or base64.
- **Outputs:** `json` = one row per statement + one row per transaction; `csv` (UTF-8 with BOM, opens in Excel); `xlsx` (sheets Transactions + Statements); `mt940` = camt converted to MT940 for older software.
- **Balance check in every statement:** opening balance + all entries = closing balance, with the difference when it does not match.
- **Parsed fields:** booking and value date, signed amount, currency, credit/debit, counterparty name + IBAN + BIC, remittance text (unstructured + structured reference), end-to-end ID, mandate ID, creditor ID, bank transaction code, references, purpose code, reversal flag. Batch entries are split into one row per transaction. German `:86:` subfields `?20-?33` and Dutch `/TRTP/.../IBAN/.../REMI/` are read.
- **Privacy:** files are processed in memory only and never stored or logged. Error messages quote at most the first 40 bytes.

### Example (API)

```bash
curl -X POST "https://api.apify.com/v2/acts/martin10291~bank-statement-converter/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"fileUrl": "https://example.com/statements/camt053-2026-11-12.xml", "format": "json"}'
```

Send the file itself instead of a link: `{"fileContent": "<base64 of the file>", "format": "xlsx"}`. Run with no input to convert a built-in sample statement.

Transaction row (from the built-in sample):

```json
{"type": "transaction", "statementId": "EXAMPLE-2026-211", "account": "DE02120300000000202051", "bookingDate": "2026-11-02", "valueDate": "2026-11-02",
 "amount": 250.0, "currency": "EUR", "creditDebit": "CRDT", "counterpartyName": "Nordwind Logistik AG", "counterpartyIban": "DE89370400440532013000",
 "counterpartyBic": "COBADEFFXXX", "remittance": "Rechnung 2026-1042", "endToEndId": "INV-2026-1042", "bankTxCode": "PMNT/RCDT/ESCT", "status": "BOOK"}
```

Statement row:

```json
{"type": "statement", "account": "DE02120300000000202051", "currency": "EUR", "openingBalance": 1000.0, "closingBalance": 2560.9,
 "sumCredits": 1730.0, "sumDebits": -169.1, "balanceCheck": {"ok": true, "expectedClosing": 2560.9, "closingBalance": 2560.9, "difference": 0.0},
 "sourceFormat": "camt.053.001.08"}
```

For `csv`, `xlsx` and `mt940` you get one row with `fileName`, `contentBase64` (decode and save) and the statement summaries.

### Use from agents (MCP)

Add the Apify MCP server (`https://mcp.apify.com?tools=martin10291/bank-statement-converter`) to Claude, Cursor or any MCP client and ask: "Convert this camt.053 file to Excel and tell me if the balances add up."

### Daily feeds (monitor)

`onlyNew: true` + `monitorId` (format json): returns only transactions not returned before under that ID, so a daily download that repeats yesterday's entries gives you each booking once. Only one-way hashes of the transaction keys are kept, never the statement.

### Input

| Field | Meaning |
|---|---|
| `fileUrl` | public http(s) link to the file or zip (max 10 MB); private/local addresses refused |
| `fileContent` | the file as text or base64 |
| `fileName` | optional, used for `sourceFile` and the output file name |
| `format` | json (default), csv, xlsx, mt940 |
| `inputFormat` | auto (default), camt053, mt940 |
| `onlyNew`, `monitorId` | only new transactions per feed |

### Honest limits

- No PDF statements (they need OCR); only the bank's electronic camt/MT940 files.
- MT940 output follows the format's limits: SWIFT character set (umlauts become ae/oe/ue/ss), names cut at 54 and purpose at 378 characters, long end-to-end IDs kept in `:86:` as `EREF+`. The German GVC code is derived when the camt has none.
- Polish bank-specific `:86:` layouts are kept as raw text in `remittance`.
- `:64:` / `:65:` (available balances) are not parsed.

### FAQ

**Do you keep my statements?** No. The file is parsed in memory during the call and dropped; nothing is written to disk or to logs.
**Which banks?** Any bank that issues standard camt.053/052 or MT940 (Commerzbank, Deutsche Bank, Sparkassen, Volksbanken, ING, ABN AMRO, Rabobank, PKO BP, mBank and others).

# Actor input Schema

## `fileUrl` (type: `string`):

Public http(s) link to a camt.053, camt.052 or MT940 file, or a .zip with several (max 10 MB, 50 files). For example a pre-signed S3/Drive link. Private and local addresses are refused. Leave empty and fill File content instead, or leave both empty to convert a built-in sample statement.

## `fileContent` (type: `string`):

The statement itself: raw XML / MT940 text, or base64 of the file (zip allowed). Max 10 MB. Processed in memory only, never stored or logged.

## `fileName` (type: `string`):

Optional original file name, used as sourceFile in the rows and as the name of the output file (for example statement-2026-11.xml).

## `format` (type: `string`):

json = one row per statement (balances + balance check) and one row per transaction; csv or xlsx = one row with the file as base64 (contentBase64) plus statement summaries; mt940 = camt converted to MT940 text (also base64).

## `inputFormat` (type: `string`):

auto detects camt (XML) or MT940 by content. Set it only if detection fails.

## `onlyNew` (type: `boolean`):

With format json: return only transactions not returned before under this Monitor ID (useful when a daily feed repeats yesterday's entries). Only one-way hashes of the transaction keys are kept, never the statement.

## `monitorId` (type: `string`):

Name of the feed for only-new mode, for example account-de-main. Each Monitor ID keeps its own memory.

## Actor input object example

```json
{
  "format": "json",
  "inputFormat": "auto",
  "onlyNew": false,
  "monitorId": "default"
}
```

# Actor output Schema

## `results` (type: `string`):

JSON: statement rows (account, currency, opening/closing balance, sums, balanceCheck) and transaction rows (dates, signed amount, counterparty, IBAN, BIC, remittance, end-to-end ID, mandate, bank code). CSV/XLSX/MT940: one file row with contentBase64.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "format": "json"
};

// Run the Actor and wait for it to finish
const run = await client.actor("martin10291/bank-statement-converter").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "format": "json" }

# Run the Actor and wait for it to finish
run = client.actor("martin10291/bank-statement-converter").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "format": "json"
}' |
apify call martin10291/bank-statement-converter --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,martin10291/bank-statement-converter"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/LodwIp7bGAm5oOf3w/builds/vmRab7ATIUDfchcQo/openapi.json
