
Parses CSV, OFX/QFX and PDF bank statements locally; auto-detects formats and banks, includes built-in UK bank profiles, customizable CSV-to-transaction mapping, and optional mapping persistence.
Kotlin Multiplatform library for parsing CSV, OFX/QFX, and PDF bank statements on-device.
| Bank | CSV | |
|---|---|---|
| Monzo | ✅ | ✅ |
| Starling | ✅ | ✅ |
| NatWest | ✅ | ✅ |
| HSBC | ✅ | ✅ (current account and credit card) |
| American Express | — | ✅ |
| Barclays | ✅ | — |
| Lloyds | ✅ | — |
| Santander | ✅ | — |
CSV detection matches on column headers (see CsvBankProfiles);
PDF detection matches on first-page text - see Supported PDF Banks below
for exactly what each one looks for.
On logos: this table intentionally doesn't embed bank logos. They're trademarked (and usually copyrighted) brand assets, and this project has no affiliation with any bank listed here - hosting their marks alongside "supported" could read as an implied partnership that doesn't exist. If you'd like logos here, the safe route is sourcing them yourself from each bank's own press/media kit under that bank's brand guidelines.
Add the repository to your settings.gradle.kts:
dependencyResolutionManagement {
repositories {
maven {
url = uri("https://maven.pkg.github.com/sporadiclemon/statement-parser")
credentials {
username = "your-github-username"
password = "your-github-token"
}
}
}
}Then add the dependency:
dependencies {
implementation("io.github.sporadiclemon:statement-parser:0.1.0")
}That one coordinate is all you need, on any target (JVM, Android, or iOS) - if you browse the
repository directly you'll also see statement-parser-jvm, statement-parser-android,
statement-parser-iosarm64, and similar. Those aren't separate releases to choose between; a
JVM jar, an Android AAR, and a Kotlin/Native klib are different binary formats with no single
file that could be all of them at once, so Gradle Module Metadata publishes each as its own
artifact and silently resolves statement-parser to whichever one matches your target. This is
how every Kotlin Multiplatform library is published, not something specific to this one.
If you let a user confirm a ColumnMapping for a CSV export from an unrecognised bank and
want to remember it, add the separate statement-parser-datastore module:
dependencies {
implementation("io.github.sporadiclemon:statement-parser:0.1.0")
implementation("io.github.sporadiclemon:statement-parser-datastore:0.1.0")
}This is a separate artifact - not a transitive dependency of the core module - so an app that
never needs to remember a custom mapping does not pull in AndroidX DataStore, okio, or
coroutines just to use StatementParser.
val store = ColumnMappingStore(dataStore) // your app's DataStore<Preferences>
store.save("MyBank", mapping)
val remembered: ColumnMapping? = store.get("MyBank").first()val parser = StatementParser()
val content = file.readText()
val format = parser.detectFormat(file.name, content)
val result = parser.parse(content, format).getOrThrow()
result.transactions.forEach {
println("${it.date}: ${it.description} (${it.amount})")
}val parser = StatementParser()
// Detect by filename
val format = parser.detectFormat("statement.pdf", "")
// format == StatementFormat.PDF
// Parse a PDF statement
val bytes = file.readBytes()
val result = parser.parsePdf(bytes) // auto-detects bank
val result = parser.parsePdf(bytes, hintProfile = PdfBankProfiles.MONZO) // skip auto-detection
result.getOrThrow().transactions.forEach {
println("${it.date}: ${it.description} (${it.amount})")
}| Bank | Detected by (first page text) |
|---|---|
| NatWest | "NatWest", "National Westminster" |
| Monzo | "Monzo" |
| HSBC | "HSBC" (current account), "Visa Card statement" (credit card) |
| Starling | "www.starlingbank.com", "Starling Bank Limited" |
| American Express | "American Express" |
Starling is matched on its page furniture rather than a bare "Starling", because the word turns up inside payee names on other banks' statements.
PdfBox-Android requires one-time initialisation. In your Application class:
override fun onCreate() {
super.onCreate()
PDFBoxResourceLoader.init(applicationContext)
}| Platform | PDF Support |
|---|---|
| Android | ✓ (PdfBox-Android) |
| iOS | ✓ (PDFKit, iOS 11+) |
| JVM | ✓ (Apache PDFBox) |
Apache License 2.0
Kotlin Multiplatform library for parsing CSV, OFX/QFX, and PDF bank statements on-device.
| Bank | CSV | |
|---|---|---|
| Monzo | ✅ | ✅ |
| Starling | ✅ | ✅ |
| NatWest | ✅ | ✅ |
| HSBC | ✅ | ✅ (current account and credit card) |
| American Express | — | ✅ |
| Barclays | ✅ | — |
| Lloyds | ✅ | — |
| Santander | ✅ | — |
CSV detection matches on column headers (see CsvBankProfiles);
PDF detection matches on first-page text - see Supported PDF Banks below
for exactly what each one looks for.
On logos: this table intentionally doesn't embed bank logos. They're trademarked (and usually copyrighted) brand assets, and this project has no affiliation with any bank listed here - hosting their marks alongside "supported" could read as an implied partnership that doesn't exist. If you'd like logos here, the safe route is sourcing them yourself from each bank's own press/media kit under that bank's brand guidelines.
Add the repository to your settings.gradle.kts:
dependencyResolutionManagement {
repositories {
maven {
url = uri("https://maven.pkg.github.com/sporadiclemon/statement-parser")
credentials {
username = "your-github-username"
password = "your-github-token"
}
}
}
}Then add the dependency:
dependencies {
implementation("io.github.sporadiclemon:statement-parser:0.1.0")
}That one coordinate is all you need, on any target (JVM, Android, or iOS) - if you browse the
repository directly you'll also see statement-parser-jvm, statement-parser-android,
statement-parser-iosarm64, and similar. Those aren't separate releases to choose between; a
JVM jar, an Android AAR, and a Kotlin/Native klib are different binary formats with no single
file that could be all of them at once, so Gradle Module Metadata publishes each as its own
artifact and silently resolves statement-parser to whichever one matches your target. This is
how every Kotlin Multiplatform library is published, not something specific to this one.
If you let a user confirm a ColumnMapping for a CSV export from an unrecognised bank and
want to remember it, add the separate statement-parser-datastore module:
dependencies {
implementation("io.github.sporadiclemon:statement-parser:0.1.0")
implementation("io.github.sporadiclemon:statement-parser-datastore:0.1.0")
}This is a separate artifact - not a transitive dependency of the core module - so an app that
never needs to remember a custom mapping does not pull in AndroidX DataStore, okio, or
coroutines just to use StatementParser.
val store = ColumnMappingStore(dataStore) // your app's DataStore<Preferences>
store.save("MyBank", mapping)
val remembered: ColumnMapping? = store.get("MyBank").first()val parser = StatementParser()
val content = file.readText()
val format = parser.detectFormat(file.name, content)
val result = parser.parse(content, format).getOrThrow()
result.transactions.forEach {
println("${it.date}: ${it.description} (${it.amount})")
}val parser = StatementParser()
// Detect by filename
val format = parser.detectFormat("statement.pdf", "")
// format == StatementFormat.PDF
// Parse a PDF statement
val bytes = file.readBytes()
val result = parser.parsePdf(bytes) // auto-detects bank
val result = parser.parsePdf(bytes, hintProfile = PdfBankProfiles.MONZO) // skip auto-detection
result.getOrThrow().transactions.forEach {
println("${it.date}: ${it.description} (${it.amount})")
}| Bank | Detected by (first page text) |
|---|---|
| NatWest | "NatWest", "National Westminster" |
| Monzo | "Monzo" |
| HSBC | "HSBC" (current account), "Visa Card statement" (credit card) |
| Starling | "www.starlingbank.com", "Starling Bank Limited" |
| American Express | "American Express" |
Starling is matched on its page furniture rather than a bare "Starling", because the word turns up inside payee names on other banks' statements.
PdfBox-Android requires one-time initialisation. In your Application class:
override fun onCreate() {
super.onCreate()
PDFBoxResourceLoader.init(applicationContext)
}| Platform | PDF Support |
|---|---|
| Android | ✓ (PdfBox-Android) |
| iOS | ✓ (PDFKit, iOS 11+) |
| JVM | ✓ (Apache PDFBox) |
Apache License 2.0