Naar de inhoud

Bankafschrift-API

Zet CAMT.053- en MT940-bankafschriften om naar CSV, XLSX, MT940 of CAMT.053 via de REST-API. Bij ontbrekende gegevens kan een tweede verzoek nodig zijn.

Deze functie is in bèta: ze werkt en is getest, maar details kunnen nog veranderen.

Op deze pagina

De bankafschrift-API van Klarbon zet de CAMT.053- en MT940-bestanden die banken al aanbieden om naar CSV, XLSX, MT940 of CAMT.053. Hij leest het afschrift en geeft bij een afgeronde omzetting het nieuwe bestand terug met een voorbeeld van rekening, periode, saldi en de eerste transacties. Alleen een geslaagde omzetting wordt berekend. Bestanden worden voor het verzoek verwerkt, zonder documentarchief.

Wat de API doet

  • POST /api/v1/bank/convert met target op bank-csv, xlsx, mt940 of camt-053.
  • Eén bestand kan meerdere afschriftbestanden opleveren. Is een keuze of ontbrekende waarde nodig, dan antwoordt de API met status: "need". Herhaal het verzoek met hetzelfde bestand, het antwoord en resume. Een need-antwoord is gratis; een afgeronde omzetting wordt berekend.
  • Voor MT940-uitvoer is statementNumber in het verzoek nodig.
  • CSV gebruikt een vaste set kolommen. Koppel ze aan je importvelden en controleer de datum- en decimaalinstellingen.

Wat de API niet doet

Hij leest nog geen pdf-afschriften en maakt geen verbinding met banken. Het lezen van CAMT.053 en MT940 is in bèta. Er is geen JSON-uitvoer: het voorbeeld in het antwoord is een samenvatting, geen volledig afschrift.

Snel aan de slag

Elk voorbeeld doet dezelfde aanroep. Voor publicatie draait elk voorbeeld tegen een lokale Klarbon-server en de echte validatie.

Maak op uw accountpagina een sleutel aan en zet die in KLARBON_API_KEY. Zet de sleutel nooit in het adres.

POST /api/v1/bank/convert

curl + jq
curl -sS https://klarbon.app/api/v1/bank/convert \
  -H "Authorization: Bearer $KLARBON_API_KEY" \
  -H "Idempotency-Key: statement-2026-11-04-csv" \
  -F "file=@statement.xml;type=application/xml" \
  -F target=bank-csv \
  -o result.json
jq -e '.ok' result.json > /dev/null || { jq -r '.error + ": " + .message' result.json; exit 1; }
jq -e '.status == "converted"' result.json > /dev/null || { jq -r '"Klarbon needs " + .need' result.json; exit 1; }
jq -r '.files[0].content' result.json | base64 --decode > "$(jq -r '.files[0].filename' result.json)"
TypeScript, Node.js 22.18+
import { readFile, writeFile } from "node:fs/promises"

type KlarbonFile = { content: string; filename: string; mime: string; bytes: number }
type Failure = { ok: false; error: string; message: string }
type Converted = { ok: true; status: "converted"; files: KlarbonFile[]; credits: { spent: number; balance: number } }
type Need = { ok: true; status: "need"; need: string; resume: string }

const form = new FormData()
form.append("file", new Blob([await readFile("statement.xml")], { type: "application/xml" }), "statement.xml")
form.append("target", "bank-csv")

const response = await fetch("https://klarbon.app/api/v1/bank/convert", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.KLARBON_API_KEY}`,
    "Idempotency-Key": "statement-2026-11-04-csv",
  },
  body: form,
})
const result = (await response.json()) as Converted | Need | Failure
if (!result.ok) throw new Error(`${response.status} ${result.error}: ${result.message}`)
if (result.status === "need") throw new Error(`Klarbon needs ${result.need}: send it with resume`)
for (const file of result.files) await writeFile(file.filename, Buffer.from(file.content, "base64"))
console.log(`${result.files.length} file(s) written, ${result.credits.balance} credits left`)
Python 3.9+, requests
import base64
import os

import requests

with open("statement.xml", "rb") as upload:
    response = requests.post(
        "https://klarbon.app/api/v1/bank/convert",
        headers={
            "Authorization": f"Bearer {os.environ['KLARBON_API_KEY']}",
            "Idempotency-Key": "statement-2026-11-04-csv",
        },
        files={"file": ("statement.xml", upload, "application/xml")},
        data={"target": "bank-csv"},
        timeout=120,
    )
result = response.json()
if not result["ok"]:
    raise SystemExit(f"{response.status_code} {result['error']}: {result['message']}")
if result["status"] == "need":
    raise SystemExit(f"Klarbon needs {result['need']}: send it with resume")
for written in result["files"]:
    with open(written["filename"], "wb") as output:
        output.write(base64.b64decode(written["content"]))
print(len(result["files"]), "file(s) written,", result["credits"]["balance"], "credits left")
PHP 8+, curl
<?php
$curl = curl_init('https://klarbon.app/api/v1/bank/convert');
curl_setopt_array($curl, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 120,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('KLARBON_API_KEY'),
        'Idempotency-Key: statement-2026-11-04-csv',
    ],
    CURLOPT_POSTFIELDS => [
        'file' => new CURLFile('statement.xml', 'application/xml', 'statement.xml'),
        'target' => 'bank-csv',
    ],
]);
$body = curl_exec($curl);
if ($body === false) {
    fwrite(STDERR, curl_error($curl) . "\n");
    exit(1);
}
$result = json_decode($body, true);
if (!$result['ok']) {
    $status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
    fwrite(STDERR, "$status {$result['error']}: {$result['message']}\n");
    exit(1);
}
if ($result['status'] === 'need') {
    fwrite(STDERR, "Klarbon needs {$result['need']}: send it with resume\n");
    exit(1);
}
foreach ($result['files'] as $written) {
    file_put_contents($written['filename'], base64_decode($written['content']));
}
echo count($result['files']) . " file(s) written, {$result['credits']['balance']} credits left\n";
C#, .NET 10
using System.Net.Http.Headers;
using System.Text.Json;

using var http = new HttpClient();
var key = Environment.GetEnvironmentVariable("KLARBON_API_KEY");
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", key);

var upload = new ByteArrayContent(await File.ReadAllBytesAsync("statement.xml"));
upload.Headers.ContentType = new MediaTypeHeaderValue("application/xml");

using var request = new HttpRequestMessage(HttpMethod.Post, "https://klarbon.app/api/v1/bank/convert?target=bank-csv") { Content = upload };
request.Headers.Add("Idempotency-Key", "statement-2026-11-04-csv");
using var response = await http.SendAsync(request);
using var json = JsonDocument.Parse(await response.Content.ReadAsStringAsync());
var result = json.RootElement;
if (!result.GetProperty("ok").GetBoolean())
{
    Console.Error.WriteLine($"{(int)response.StatusCode} {result.GetProperty("error")}: {result.GetProperty("message")}");
    return 1;
}
if (result.GetProperty("status").GetString() == "need")
{
    Console.Error.WriteLine($"Klarbon needs {result.GetProperty("need")}: send it with resume");
    return 1;
}
foreach (var written in result.GetProperty("files").EnumerateArray())
{
    await File.WriteAllBytesAsync(written.GetProperty("filename").GetString()!,
        Convert.FromBase64String(written.GetProperty("content").GetString()!));
}
Console.WriteLine($"{result.GetProperty("files").GetArrayLength()} file(s) written, {result.GetProperty("credits").GetProperty("balance")} credits left");
return 0;

Het antwoord

Het antwoord op de curl-aanroep hierboven, vastgelegd bij die run met Klarbon synthetic fixture, CC0-1.0.

Ingekort voor de pagina: files[0].content: base64 cut

{
  "ok": true,
  "status": "converted",
  "format": "camt-053",
  "files": [
    {
      "content": "77u/IkJvb2tpbmcgZGF0ZSI7IlZhbHVlIGRhdGUiOyJBbW91...",
      "filename": "statement.csv",
      "mime": "text/csv;charset=utf-8",
      "bytes": 300
    }
  ],
  "preview": {
    "bank": null,
    "accountHolder": null,
    "iban": "DE89370400440532013000",
    "currency": "EUR",
    "period": {
      "from": "2026-11-02",
      "to": "2026-11-04"
    },
    "openingBalance": "1000.00",
    "closingBalance": "1075.00",
    "transactionCount": 2,
    "firstTransactions": [
      {
        "bookingDate": "2026-11-02",
        "valueDate": "2026-11-02",
        "amount": "100.00",
        "description": "Fictional customer payment",
        "reference": "TEST-1"
      },
      {
        "bookingDate": "2026-11-03",
        "valueDate": "2026-11-03",
        "amount": "-25.00",
        "description": "Fictional office supplies",
        "reference": "TEST-2"
      }
    ],
    "reconciliation": {
      "ok": true,
      "difference": "0.00",
      "checkedBalances": 1
    },
    "balanceSource": {
      "opening": "printed",
      "closing": "printed"
    }
  },
  "warnings": [],
  "credits": {
    "spent": 1,
    "balance": 196,
    "plan": 0,
    "bought": 196
  }
}

Kosten

Vooraf betaalde API-pakketten, per kaart gekocht op uw accountpagina: 1.000 aanroepen voor € 39,00, 5.000 aanroepen voor € 129,00, 25.000 aanroepen voor € 449,00, 100.000 aanroepen voor € 990,00, zonder vervaldatum. Uw eerste sleutel geeft 25 gratis aanroepen. Betaalde aanroepen gebruiken eerst het API-saldo, daarna dezelfde credits als de site. Een mislukte aanroep kost niets.

Aanroep Kosten
POST /open Gratis, redelijk gebruik tot 1.000 per dag per account
POST /check Gratis binnen de daglimiet van uw abonnement (20 per dag in het gratis abonnement)
POST /convert, POST /make 1 aanroep, alleen als het gemaakte bestand geldig is
POST /bank/convert 1 aanroep per begonnen 500 transacties, alleen na omzetting

Limieten

  • Bestanden tot 20 MB, pdf’s tot 50 pagina’s.
  • 60 verzoeken per minuut per sleutel, 120 per account. Een 429 bevat Retry-After.
  • Tot 10 sleutels per account. Idempotency-Key geldt 24 uur, dus een nieuwe poging wordt nooit twee keer berekend.

Fouten

Elke fout is { ok: false, error, message }: een vaste code en één duidelijke zin.

Status Code Wanneer
401 auth Geen sleutel, of een onbekende of ingetrokken sleutel.
402 no_credits API-saldo en credits zijn allebei te laag. Er is niets gedaan; topUpUrl wijst naar de aankooppagina.
413 too_large Meer dan 20 MB of 50 pdf-pagina’s.
415 unsupported Een bestandsformaat dat de route niet aanneemt.
422 invalid Het bestand was niet leesbaar, of het gemaakte bestand is niet geldig. Niet berekend.
429 limit Een snelheidslimiet of de gratis aanroepen van de dag.
503 engine_unavailable De validatie is even niet bereikbaar. Niet berekend.

Privacy

Bestanden worden voor het verzoek verwerkt, zonder documentarchief. De applicatielogs van Klarbon bevatten geen bestandsnaam, geen inhoud en geen sleutel. De volledige sleutel wordt niet bewaard, alleen een HMAC ervan en een kort weergaveprefix.

Verder

De documentatie voor ontwikkelaars is in het Engels.

Veelgestelde vragen

Welke afschriftformaten leest de API?

CAMT.053 en MT940, met andere gestructureerde bankformaten in bèta. Pdf-afschriften worden nog niet ondersteund.

Welke formaten kan de API schrijven?

CSV, XLSX, MT940 en CAMT.053, plus ondersteunde bèta-uitvoer. De parameter target kiest één formaat per aanroep; het antwoord kan meerdere afschriftbestanden bevatten.

Wat als het bestand meerdere afschriften bevat?

De API kan voor elk afschrift een bestand teruggeven. Als een rekeningkeuze of ontbrekend veld nodig is, krijg je status need. Herhaal het verzoek met hetzelfde bestand, het antwoord en resume. Een need-antwoord is gratis; een afgeronde omzetting wordt berekend.

Maakt de API verbinding met mijn bank?

Nee. Je stuurt een bestand dat je bank al heeft geëxporteerd. Klarbon vraagt nooit om bankinloggegevens.

Bronnen

Bijgewerkt op 6 november 2026