Keep your Ghostfolio cash accounts up to date with your real EU/UK bank balances, via open-banking.io.
Ghostfolio tracks investments beautifully, but bank/cash accounts are manual — you type a balance and update it by hand. This is a small community bridge that reads your account balances from open-banking.io and writes them to Ghostfolio through its public API. It runs alongside Ghostfolio and needs no changes to Ghostfolio core — it only uses the documented REST API, exactly as a user with a Security Token would.
Why a separate tool? The Ghostfolio maintainer suggested this approach in discussion #7308: a standalone script that fetches balances and updates them via the Ghostfolio API, published as a community project. This is that script.
open-banking.io ──(getAccounts, decrypted locally)──► this bridge ──(POST /api/v1/account-balance)──► Ghostfolio
- Balances are decrypted locally with your exported private key — open-banking.io
is zero-knowledge and never sees your plaintext data. See the
@open-banking-io/clientSDK. - Nothing is written to Ghostfolio unless you map an account explicitly (or pass
--create). Use--dry-runfirst to see exactly what would happen.
git clone https://github.com/open-banking-io/ghostfolio-openbanking-sync
cd ghostfolio-openbanking-sync
npm install
npm run build
node dist/cli.js --help # or: npm run start -- --helpRequires Node ≥ 20. (An npm release is planned so you can npx it directly.)
The examples below use the
ghostfolio-openbanking-synccommand. That name is available afternpm link(ornpm install -g .); otherwise just substitutenode dist/cli.jsfor it.
-
open-banking.io — in the app, connect your bank(s) and export your
credentials.json(API key + encryption key). Keep it private. -
Ghostfolio — Settings → copy your Security Token.
-
Create a
config.json(seeconfig.example.json):{ "ghostfolio": { "host": "https://your-ghostfolio-host", "securityToken": "your-security-token" }, "openBanking": { "credentials": "./credentials.json" }, "accounts": [] } -
Discover your accounts and build the mapping:
ghostfolio-openbanking-sync --config config.json --list
This prints your open-banking.io accounts and your Ghostfolio accounts with their ids. Add one mapping per account to
"accounts":{ "openBankingId": "ob-account-id", "ghostfolioAccountId": "ghostfolio-account-uuid" }You can match by
ibaninstead ofopenBankingIdif you prefer. To create the Ghostfolio accounts automatically for every unmapped bank account:ghostfolio-openbanking-sync --config config.json --create # prints mapping lines to paste back -
Sync:
ghostfolio-openbanking-sync --config config.json --dry-run # preview ghostfolio-openbanking-sync --config config.json # write balances
# every day at 06:00 — refresh at the bank, then push balances to Ghostfolio
0 6 * * * cd /opt/gf-sync && ghostfolio-openbanking-sync --config config.json --refresh >> sync.log 2>&1--refresh asks open-banking.io to pull fresh data from the bank before reading
(best-effort; a failed refresh falls back to the last cached balance).
Prefer containers? Build the image and run it on a schedule:
# 1. put config.json + credentials.json in ./data
mkdir -p data && cp config.example.json data/config.json # then edit it
# 2. run it on a loop (syncs every 24h; --refresh pulls fresh bank data first)
docker compose -f docker-compose.example.yml up -dOr one-shot (for host cron / a Kubernetes CronJob):
docker build -t ghostfolio-openbanking-sync .
docker run --rm -v "$PWD/data:/data:ro" ghostfolio-openbanking-sync --config /data/config.json --refreshSee docker-compose.example.yml.
| Flag | Effect |
|---|---|
--config <path> |
Config file (default ./config.json) |
--list |
List open-banking.io + Ghostfolio accounts and exit |
--create |
Create a Ghostfolio account per unmapped bank account, print mappings |
--refresh |
Trigger an online bank refresh before reading balances |
--dry-run |
Show what would be synced without writing |
--help |
Help |
- Balance type. open-banking.io returns several ISO 20022 balance snapshots
per account. By default the bridge prefers booked (
CLBD/ITBD) over available so Ghostfolio reflects settled money. Override per account with"balanceType": "ITAV", or globally with"defaultBalanceType". - Precision. Amounts are decimal strings end-to-end and only converted to a
number at the Ghostfolio API boundary (its DTO requires a number). Account
balances are far below
2^53, so there is no float drift in practice. - Scope. This syncs balances today, which is what Ghostfolio's cash accounts model. Transaction-level import is out of scope here.
import { GhostfolioClient, syncBalances, loadConfig } from "ghostfolio-openbanking-sync";
import { OpenBankingClient } from "@open-banking-io/client";
const config = await loadConfig("config.json");
const bank = OpenBankingClient.fromCredentials(config.openBanking.credentials);
const gf = new GhostfolioClient(config.ghostfolio);
await gf.authenticate();
const outcomes = await syncBalances(gf, await bank.getAccounts(), config, { dryRun: true });I'm John, maintainer of open-banking.io. I built this bridge so Ghostfolio users can auto-sync EU/UK bank balances. open-banking.io is a paid service (pricing starts around €3/mo and drops as the user base grows); this tool is free and MIT-licensed, and it talks to Ghostfolio only through its public API. Ghostfolio is an independent project and is not affiliated with open-banking.io. Issues and PRs welcome.