If you've ever tried to call a European bank's open banking endpoint directly — say, to read your own account balances programmatically — you probably hit a wall that looks something like this:
HTTP 401 Unauthorized
{"error": "invalid_client", "error_description": "mutual TLS certificate required"}
That's the eIDAS certificate gate. Under PSD2, a bank's production API is protected by mutual TLS using a qualified certificate (a QWAC), and request signing often needs a second one (QSeal). For a regulated bank or a well-funded fintech, that's a tax you pay. For an indie developer, a student project, or a small business automating its own accounting, it's a deal-breaker: the certs cost roughly EUR 2,000–10,000 per year, take weeks of paperwork, and require a registered legal entity.
The good news: there's a well-defined, fully compliant path to read EU bank data without ever touching an eIDAS certificate yourself. This guide explains how it actually works under the hood, when to use it, and includes real, runnable code.
Disclosure up front: I'm John, the founder of 's API as the worked example. The shape (create requisition -> redirect -> list -> fetch) is identical across providers; only the field names change.
Step 1 — Create a requisition (pick a bank)
CODE# Replace YOUR_API_KEY with the key from your provider's dashboard
curl -X POST https://api.open-banking.io/v1/requisitions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"redirect": "https://yourapp.example.com/callback",
"institution_id": "DANMARKS_DANSKE_BANK",
"reference": "user-42-first-link"
}'
CODE{
"id": "req_8f3a...",
"link": "https://api.open-banking.io/redirect/req_8f3a...",
"accounts": [],
"status": "CR"
}
Send the user's browser to
link. They authenticate at Danske Bank (SCA). On success, the bank redirects back to yourredirectURL.
Step 2 — List the linked accounts
CODEcurl https://api.open-banking.io/v1/requisitions/req_8f3a... \
-H "Authorization: Bearer YOUR_API_KEY"
CODE{
"id": "req_8f3a...",
"status": "LN",
"accounts": ["acc_a1b2...", "acc_c3d4..."]
}
status: "LN"means linked. Now grab data.
Step 3 — Fetch balances and transactions
CODE# Balances
curl https://api.open-banking.io/v1/accounts/acc_a1b2.../balances \
-H "Authorization: Bearer YOUR_API_KEY"
# Transactions (last 30 days)
curl "https://api.open-banking.io/v1/accounts/acc_a1b2.../transactions?date_from=2026-06-21&date_to=2026-07-21" \
-H "Authorization: Bearer YOUR_API_KEY"
A tiny Python client
CODEimport os, requests
API = "https://api.open-banking.io/v1"
KEY = os.environ["OBI_API_KEY"]
H = {"Authorization": f"Bearer {KEY}"}
def create_requisition(institution_id, redirect_url):
r = requests.post(f"{API}/requisitions", headers=H, json={
"redirect": redirect_url,
"institution_id": institution_id,
})
r.raise_for_status()
return r.json()
def list_accounts(requisition_id):
r = requests.get(f"{API}/requisitions/{requisition_id}", headers=H)
return r.json()["accounts"]
def get_transactions(account_id, date_from, date_to):
r = requests.get(
f"{API}/accounts/{account_id}/transactions",
headers=H, params={"date_from": date_from, "date_to": date_to},
)
return r.json()["transactions"]
# Usage
req = create_requisition("DANMARKS_DANSKE_BANK", "https://yourapp.example.com/cb")
print("Send user to:", req["link"])
# ... after the user authenticates at their bank ...
for acct in list_accounts(req["id"]):
txns = get_transactions(acct, "2026-06-21", "2026-07-21")
print(acct, len(txns), "transactions")
Check each provider's live docs for the exact field names — they vary. The flow above is universal across PSD2 AIS.
Five gotchas that cost me real time
Consent expiry is per-country, not universal. Germany defaults to 90 days; the Nordics often allow 180. Store the expiry per consent and notify users at T-7, T-3, and T-0. A silent expiry is the #1 reason "my sync stopped working" tickets.
Bank coverage has real gaps. "Supports 10,000 banks" hides the fact that your specific regional Sparkasse or niche neobank may be missing or sandbox-only. Test your actual target banks before committing.
Transaction booking vs pending. Most APIs expose bothbookedandpendingtransactions. Dedupe bytransactionId(orentryReference); if a bank doesn't supply one, hash(date, amount, counterparty, description)and treat collisions carefully.
Rate limits and polling. Refreshing transactions every minute will get you throttled — and PSD2 limits how often you can pull anyway. Once or twice a day per account is usually plenty; some banks also support webhooks for new transactions.
GDPR / data residency. If your users are EU-based and you're shipping a commercial product, confirm the aggregator processes data in the EU and offers end-to-end encryption. "Compliant with PSD2" is not the same as "we can't read your data."
When should you actually get the certificate?
The aggregator path isn't always right. Go direct with your own eIDAS certificate when:
- You're a regulated AIS/PISP and volume justifies it (rough rule of thumb: once you're paying the aggregator more than ~EUR 2k/year, the math flips).
- You're an enterprise treasury or bank-adjacent product where no third party may sit in the data path (regulatory or policy reasons).
- You need Payment Initiation (PIS) at high reliability and the aggregator's PIS coverage is thin for your target banks.
For everyone else — hobby projects, SMB accounting automation, personal finance dashboards, self-hosted budgeting tools (Actual Budget, Firefly III, Beancount integrations) — the certificate-free aggregator path is almost always the pragmatic choice. You get to ship this week instead of next quarter.
TL;DR
- You don't need an eIDAS certificate to read EU bank data. An AIS aggregator holds it for you; you get an API key.
- The flow is universal: create requisition -> SCA redirect -> list accounts -> fetch transactions -> renew consent before it expires.
- Pick a provider on three axes: bank coverage for your banks, EU data residency + E2E encryption, and a free tier that fits your stage.
- Go direct (buy the cert) only when volume, regulation, or a no-intermediary requirement demands it.
If you want to try the flow above end-to-end, you can grab a free API key at open-banking.io — and yes, that's my project, so apply appropriate skepticism and compare it against Tink, TrueLayer, GoCardless (formerly Nordigen), and Enable Banking before you commit. The right answer depends on your banks and your budget, not on who wrote this article.
Questions or war stories from your own bank-data integrations? Drop them in the comments — I read every one.
↗ Original-Artikel auf dev.to lesenVollständiger Original-BerichtAusführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
SOCIAL SHARE CARD GENERATOR