Official server-to-server SDKs for open-banking.io in .NET, Node, Python, Rust, Go, Java, Ruby, and PHP.
open-banking.io is zero-knowledge: the service stores and returns only ciphertext it cannot read. Each SDK does the two things an integrator needs — authenticate with your API key and decrypt your data locally with your private key — and hands you clean, typed models.
New here? Create an account and export your credentials bundle at open-banking.io, then pick your language below.
Each package has its own README with install instructions, a runnable example, and language-specific notes (money types, async, dependencies).
| Language | Package | Version | Guide |
|---|---|---|---|
| .NET | OpenBankingIO.Client (NuGet) |
dotnet/ |
|
| Node / TypeScript | @open-banking-io/client (npm) |
node/ |
|
| Python | open-banking-io (PyPI) |
python/ |
|
| Rust | open-banking-io (crates.io) |
rust/ |
|
| Go | github.com/open-banking-io/clients/go |
go/ |
|
| Java | io.open-banking:open-banking-io-client (Maven Central) |
java/ |
|
| Ruby | open-banking-io (RubyGems) |
ruby/ |
|
| PHP | open-banking-io/client (Packagist) |
php/ |
Prefer the terminal? There's also a command-line client (openbanking).
- Get your credentials. In the open-banking.io app, export your
credentials bundle (
credentials.json) — it contains yourapiBaseUrl, an API key, and your encryption private key (PKCS#8). - Point an SDK at the bundle. Every request sends
X-Api-Key; every response is decrypted in-process with your private key. The service never sees your plaintext data.
// Node / TypeScript — the same shape exists in all eight languages (see each guide above).
import { OpenBankingClient } from "@open-banking-io/client";
const client = OpenBankingClient.fromCredentials("credentials.json");
for (const a of await client.getAccounts()) {
const booked = a.balances.find((b) => b.type === "ITBD");
console.log(`${a.iban}: ${booked?.amount} ${a.currency}`);
}The same example in .NET, Python, Rust, Go, Java, Ruby, and PHP
// .NET
using var client = OpenBankingClient.FromCredentials("credentials.json");
foreach (var a in await client.GetAccountsAsync())
Console.WriteLine($"{a.Iban}: {a.Balances.First(b => b.Type == "ITBD").Amount} {a.Currency}");# Python
client = OpenBankingClient.from_credentials("credentials.json")
for a in client.get_accounts():
booked = next(b for b in a.balances if b.type == "ITBD")
print(a.iban, booked.amount, a.currency)// Rust
let client = OpenBankingClient::from_credentials("credentials.json")?;
for a in client.get_accounts()? {
let booked = a.balances.iter().find(|b| b.type_ == "ITBD");
println!("{:?} {:?} {}", a.iban, booked.map(|b| &b.amount), a.currency);
}// Go
client, _ := openbanking.FromCredentials("credentials.json", nil)
accounts, _ := client.GetAccounts()
for _, a := range accounts {
for _, b := range a.Balances {
if b.Type == "ITBD" { fmt.Println(a.Iban, b.Amount, a.Currency) }
}
}// Java
var client = OpenBankingClient.fromCredentials("credentials.json");
for (Account a : client.getAccounts())
a.balances().stream().filter(b -> b.type().equals("ITBD")).findFirst()
.ifPresent(b -> System.out.println(a.iban() + " " + b.amount() + " " + a.currency()));# Ruby
client = OpenBankingIO::Client.from_credentials("credentials.json")
client.get_accounts.each do |a|
booked = a.balances.find { |b| b.type == "ITBD" }
puts "#{a.iban} #{booked&.amount} #{a.currency}"
end// PHP
$client = OpenBankingIO\Client::fromCredentials("credentials.json");
foreach ($client->getAccounts() as $a) {
foreach ($a->balances as $b) {
if ($b->type === "ITBD") echo "{$a->iban} {$b->amount} {$a->currency}\n";
}
}All eight expose the same surface — getAccounts, getTransactions(accountId, …), getConnections,
sync(accountId), syncAll(). sync decrypts the account's session uid locally and posts it, so the
service can refresh from the bank without ever holding it in plaintext.
- Your bank credentials are never sent to open-banking.io.
- Your account data (IBAN, balances, transactions) is stored encrypted by the service.
- Only your local private key can decrypt it — the service holds ciphertext it cannot read.
Each sensitive value is an envelope sealed with ephemeral ECDH (P-256) → HKDF-SHA256 →
AES-256-GCM; only your private key can open it, and all eight SDKs are verified against the same
fixtures/ so they decrypt identically. The full wire format, trust boundaries, and assumptions are
in THREAT_MODEL.md.
# regenerate the shared test fixtures (keypair + encrypted sample responses)
node tools/generate-fixtures.mjs
# run each SDK's tests (crypto round-trip + a mock-server integration suite)
dotnet test dotnet/
cd node && npm install && npm test
cd python && pip install -e .[dev] && pytest -q
cd rust && cargo test
cd go && go test ./...
cd java && mvn -B verify
cd ruby && bundle install && bundle exec rspec
cd php && composer install && vendor/bin/phpunitCI builds and tests all eight on every push. Releases are per-package — each publishes from its own
<dir>/vX.Y.Z tag (e.g. node/v0.2.0), so tagging one package never republishes the others. Cut a
release from Actions → Release (pick a package + version); see RELEASING.md.
Zero-knowledge security model and trust boundaries: THREAT_MODEL.md. Report
vulnerabilities privately per SECURITY.md. Contributions welcome — see
CONTRIBUTING.md, CODE_OF_CONDUCT.md, and
SUPPORT.md.
MIT licensed.