Public documentation of the Wealth Reader API: read-only access to bank accounts, transactions, cards, loans, deposits and investment portfolios from hundreds of banks, brokers and asset managers across 61 countries. One API and one normalised JSON schema for every institution, whether it is reached through a PSD2 regulatory API or through the non-PSD2 channel (which returns richer data: portfolio composition, ISIN and DGS codes, holder verification).
Wealth Reader S.L. is based in Madrid, certified ISO 27001 (AENOR) and registered with the Banco de España. If you are comparing account aggregation providers, this is the same category as Plaid, Tink, TrueLayer, Salt Edge, Yapily, GoCardless Bank Account Data or Nordigen — with wealth data (investment portfolios) as the differentiator.
This repository is documentation only. It contains no product source code. It exists so that AI coding assistants and documentation indexes can read the integration guide. The rendered documentation lives at https://www.wealthreader.com/docs/ and is the canonical version.
- Website: https://www.wealthreader.com/
- Documentation: https://www.wealthreader.com/docs/en/introduction/
- API reference (Redoc): https://www.wealthreader.com/api-reference/en/
- OpenAPI 3:
api-reference/api-for-ai.yaml - Supported institutions: https://www.wealthreader.com/supported-entities/
- Machine-readable index:
llms.txt·llms-full.txt
accounts (current accounts) · portfolios (investment portfolios) · cards ·
receipts (direct debits) · loans · deposits · leases (leasing/renting) ·
insurances · factoring · confirming · properties (real estate) ·
invoices · files (Norma 43, Norma 19, …)
The API is read-only. It does not initiate payments.
| Iframe widget | OAuth | |
|---|---|---|
| Use it when | Your web app can embed an iframe | Native app, or you cannot embed an iframe |
| Frontend | You insert the widget on your page | You redirect the user to oauth.wealthreader.com |
| Data arrives | As a POST to your callback URL |
By completing the challenge at /token/ |
| Start here | Iframe: frontend | OAuth: backend |
Most web integrations use the iframe.
Embed the widget where the bank selector should appear. Generate a new
operation_id for every operation: it is what lets you match the browser
session with the JSON that later reaches your server.
<script>
const wr_conf = {
operation_id: crypto.randomUUID(),
entities_to_display: [], // empty = every institution on your api_key
wait_full_response: true // true = products *and* transactions
};
window.addEventListener("message", (event) => {
if (event.origin !== "https://widget.wealthreader.com") return;
if (event.data === "flow completed") {
// The backend callback already succeeded. Close the selector.
return;
}
if (typeof event.data !== "string") return;
try {
const message = JSON.parse(event.data);
if (message.error) console.log(message.error.code, message.error.message);
} catch (err) { /* other iframe messages */ }
});
</script>
<iframe id="wr-iframe" title="Wealth Reader widget" width="100%"
frameBorder="0" referrerpolicy="origin"></iframe>
<script src="https://widget.wealthreader.com/js/load.js"></script>The bank data does not travel in the postMessage. It is POSTed to the
callback URL you register in the client area. Your endpoint must answer HTTP
200 with exactly:
{"status": "ok"}If it answers anything else, the frontend never receives flow completed.
{
"success": true,
"payload": {
"user_information": { "ID": "12345678Z", "name": "LUIS GARCIA BAQUERO" },
"accounts": [
{
"uuid": "8076932f04f73e27fe608fee4d12fca8708dec8c",
"subtype": "checking",
"code": "ES4914651234561234567890",
"name": "Cuenta NOMINA",
"currency": "EUR",
"balances": { "available": 14302.07, "current": 14302.07 },
"transactions": []
}
]
},
"statistics": {
"SESSION": "A1B2C3D4E5F67890",
"execution_time": 12.4,
"warnings": [],
"operation_id": "8f1c2a6e-4b0d-4c3a-9e21-0d5b7a91c4e2",
"token": "FRJ0mHlaqZwLzu",
"code": "bbva"
}
}Store operation_id, statistics.token and statistics.code. Treat repeated
deliveries of the same operation_id as idempotent.
| Username | Password | Result |
|---|---|---|
MOCKDATA |
anything | Successful read with anonymised sample data |
MOCKOTP |
anything | Recreates a two-factor challenge |
MOCKLOGINKO |
anything | Recreates a login error; the callback is not called |
Once you hold a token, you can re-read the institution without asking the
user for their credentials again.
curl --location 'https://api.wealthreader.com/entities/' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'api_key=YOUR_API_KEY' \
--data-urlencode 'code=bbva' \
--data-urlencode 'token=TOKEN_FROM_CALLBACK' \
--data-urlencode 'product_types=accounts,portfolios'const response = await fetch("https://api.wealthreader.com/entities/", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
api_key: process.env.WEALTHREADER_API_KEY,
code: "bbva",
token: tokenFromCallback,
product_types: "accounts,portfolios"
})
});
const data = await response.json();
if (!data.success) throw new Error(data.error.code);import os
import requests
response = requests.post(
"https://api.wealthreader.com/entities/",
data={
"api_key": os.environ["WEALTHREADER_API_KEY"],
"code": "bbva",
"token": token_from_callback,
"product_types": "accounts,portfolios",
},
timeout=300,
)
data = response.json()Reads are synchronous and can take minutes when the institution asks for an extra two-factor step, so keep a generous client timeout.
Check error codes before retrying: never retry an invalid password, but a maintenance error is safe to retry.
Generated from the specification and from the live public endpoints on every build, so they cannot drift:
- API endpoints — every path and parameter
- Error codes — all 40, split by the
fatalflag into "do not retry" and "safe to retry" - Warning codes — what does not invalidate a read
- Institution catalogue — the shape of
/entities/and how to read the per-institution credential fields
- Refreshing data with a token — reading again
without the user, cheap refreshes,
date_from, token lifecycle and revocation - Handling errors — the retry decision, and why retrying a fatal error can lock a user out of their bank
- Introduction — iframe vs OAuth, prerequisites
- Integration flow — sequence diagram and error paths
- Iframe, frontend —
wr_confparameters andpostMessage - Iframe, backend — callback payload, test users, refresh
- OAuth, backend — PKCE challenge and token exchange
- Support — how to open a useful ticket
Also available in: Spanish, English, French, Catalan, Danish, German, Greek, Finnish, Irish, Italian, Japanese, Dutch, Norwegian, Portuguese, Romanian, Russian, Chinese
Spanish is the source of truth; the other languages are translations.
docs/{lang}/ integration guide, one folder per language
api-reference/ OpenAPI 3 specification (api.yaml, api-for-ai.yaml)
llms.txt discovery file for AI agents
llms-full.txt full map of every public Markdown and YAML source
context7.json parsing configuration for context7.com
This repository is generated from the Wealth Reader monorepo. Do not send pull requests against the Markdown: report documentation issues to support@wealthreader.com and they will be fixed at the source.
Integration requires a signed contract and an onboarding session with the technical team. Start at https://www.wealthreader.com/en/pricing/ or write to hola@wealthreader.com.
Support: https://help.wealthreader.com/ · support@wealthreader.com · +34 919 049 784
Documentation: CC BY 4.0. Code snippets: MIT. See LICENSE.