A source is data, never code — this is the whole of it.
ing-es.json
{
"id": "ing-es",
"name": "ING España",
"service": "ing",
"trust": "first-party",
"credit": {
"author": {
"name": "David Marín",
"url": "https://davefx.com"
},
"capture": {
"name": "David Marín",
"url": "https://davefx.com"
}
},
"domain": "ingdirect.es",
"country": "ES",
"version": "2026-09-10",
"minVersion": "0.9.11.4",
"categories": [
"banking"
],
"gaps": [
"inversión"
],
"throttle": {
"minMs": 300,
"jitterMs": 500
},
"retry": {
"status": [
401
],
"max": 4,
"delayMs": 700,
"bodyMatch": "Authorization Required"
},
"match": [
"https://ing.ingdirect.es/*"
],
"openUrl": "https://ing.ingdirect.es/app/login",
"currency": "EUR",
"auth": {
"mode": "bearer",
"tokenMatch": "^Bearer eyJ[A-Za-z0-9._-]+$",
"replayHeaders": [
"authorization",
"x-ing-extendedsessioncontext",
"baggage"
],
"tokenFromStorage": {
"key": "TokenManager.profiles",
"field": "0.accessToken",
"scheme": "Bearer",
"header": "authorization"
},
"pageOnly": true,
"readyUrl": [
"https://ing.ingdirect.es/pfm/#producto/",
"https://ing.ingdirect.es/pfm/#overall-position"
]
},
"api": {
"host": "https://api.ing.ingdirect.es"
},
"streams": [
{
"id": "movimientos",
"name": "Movimientos",
"schema": "transaction@1",
"keepRaw": true,
"api": {
"groups": {
"path": "/position-keeping",
"itemsPath": "products",
"fields": {
"id": "uuid",
"name": "commercialName",
"type": "type",
"iban": "identifiers[type=PRODUCT_NUMBER].value",
"currency": "denominationCurrency"
},
"mask": [
"iban"
]
},
"list": {
"path": "/v2/products/{group.id}/transactions",
"paging": "offset",
"offsetParam": "offset",
"offsetStep": 100,
"itemsPath": "transactions",
"window": "89d",
"maxAgeDays": 89,
"range": {
"from": "fromDate",
"to": "toDate",
"format": "date"
},
"params": {
"limit": 100,
"filterEru": "false"
},
"idOverride": {
"when": {
"field": "status",
"present": true
},
"template": "card|{transactionDate}|{amount}|{transactionId.productId}"
}
}
},
"fields": {
"internalId": "transactionLocalUUID",
"date": "transactionDate",
"amount": "amount",
"description": "description",
"type": "transactionCode",
"account": "{group.iban}",
"balanceAfter": "balance"
},
"normalize": {
"counterparty": {
"from": "description",
"flags": "i",
"re": [
"(?:enviad[oa] a|recibid[oa] de|a favor de|domiciliaci[oó]n de)\\s+(.+)$",
"^N[oó]mina recibida\\s+(.+)$",
"^Recibo\\s+(.+?)\\s*(?:·.*)?$"
]
}
}
},
{
"id": "informe",
"name": "Extracto por cuenta",
"schema": "invoice@1",
"api": {
"groups": {
"path": "/position-keeping",
"itemsPath": "products",
"fields": {
"id": "uuid",
"name": "commercialName",
"type": "type",
"iban": "identifiers[type=PRODUCT_NUMBER].value"
},
"mask": [
"iban"
]
},
"list": {
"paging": "synthetic",
"synthetic": {
"each": "group-months"
},
"maxAgeDays": 89
}
},
"fields": {
"internalId": "{group.id}-{period}",
"date": "{date}",
"number": "{period}",
"account": "{group.iban}",
"description": "Extracto {group.name} {period}"
},
"formats": [
{
"id": "pdf",
"name": "PDF",
"api": {
"pdf": {
"path": "/v2/products/{group.id}/transactions/report?fromDate={fromDate}&toDate={toDate}&limit=1000&filterEru=false&reportType=pdf",
"method": "GET",
"ext": "pdf"
}
}
},
{
"id": "excel",
"name": "Excel",
"api": {
"pdf": {
"path": "/v2/products/{group.id}/transactions/report?fromDate={fromDate}&toDate={toDate}&limit=1000&filterEru=false&reportType=xls",
"method": "GET",
"ext": "xls"
}
}
}
]
},
{
"id": "extracto-mensual",
"name": "Extracto integrado mensual",
"schema": "invoice@1",
"api": {
"host": "https://ing.ingdirect.es",
"list": {
"paging": "synthetic",
"synthetic": {
"each": "months"
},
"maxAgeDays": 89
},
"pdf": {
"path": "/genoma_api/rest/products/statement?year={year}&month={month}",
"method": "GET",
"ext": "pdf"
}
},
"fields": {
"internalId": "{period}",
"date": "{date}",
"number": "{period}",
"description": "Extracto integrado {period}"
}
}
],
"changelog": [
{
"version": "2026-09-10",
"changes": {
"en": "Fixes ING syncs that failed with an authorisation error, most often on Firefox left open for a long time. Habeas now reads your current access token straight from the page on each request, instead of a saved one that ING may have already rotated.",
"es": "Corrige las sincronizaciones de ING que fallaban con un error de autorización, sobre todo en Firefox si llevaba mucho tiempo abierto. Habeas ahora lee tu token de acceso actual directamente de la página en cada petición, en vez de uno guardado que ING puede haber rotado ya."
}
},
{
"version": "2026-08-26.1",
"changes": {
"en": "Trust level raised to first-party: written and verified against the real service by the project, not contributed unreviewed. The label describes provenance, not permission — the same-domain guard, the consent screen and every other rule apply identically whatever it says.",
"es": "Confianza elevada a first-party: escrita y verificada contra el servicio real por el proyecto, no una aportación sin revisar. La etiqueta describe procedencia, no permisos — la guardia de mismo dominio, la pantalla de consentimiento y las demás reglas se aplican igual diga lo que diga."
}
},
{
"version": "2026-08-26",
"changes": {
"en": "Records authorship: the session was recorded and the definition written by the maintainer. No change to extraction.",
"es": "Registra la autoría: la sesión la grabó y la definición la escribió el mantenedor. No cambia la extracción."
}
},
{
"version": "2026-08-09",
"changes": {
"en": "The guide now says plainly that ING serves only about 90 days of history, so nobody arrives expecting their whole statement archive. Also fixes the month that straddles that limit: its statements now download.",
"es": "La guía dice ahora claramente que ING solo sirve unos 90 días de histórico, para que nadie llegue esperando todo su archivo de extractos. Corrige además el mes que queda a caballo de ese límite: sus extractos ya se descargan."
}
},
{
"version": "2026-08-08.2",
"changes": {
"en": "Corrects the history window by one day, so Habeas no longer asks ING for a day it will refuse. Needs Habeas 0.9.11.4 or newer.",
"es": "Corrige la ventana de histórico en un día, para que Habeas no pida a ING un día que va a rechazar. Requiere Habeas 0.9.11.4 o posterior."
}
},
{
"version": "2026-08-08.1",
"changes": {
"en": "No change to what this source extracts. This version gave it its guide page on habeas.dev.",
"es": "Sin cambios en lo que extrae esta fuente. Esta versión le dio su página de guía en habeas.dev."
}
},
{
"version": "2026-08-08",
"changes": {
"en": "Corrects the coverage note: card movements were already being extracted, so only investments remain uncovered.",
"es": "Corrige la nota de cobertura: los movimientos de tarjeta ya se extraían, así que solo quedan fuera las inversiones."
}
},
{
"version": "2026-07-31.1",
"changes": {
"en": "Fixes syncs that stopped partway with an authorisation error. ING sometimes rejects a valid request and accepts the same one moments later; Habeas now retries, as ING's own app does. Needs Habeas 0.9.7 or newer.",
"es": "Corrige las sincronizaciones que se cortaban a medias con un error de autorización. ING rechaza a veces una petición válida y acepta esa misma poco después; Habeas ahora reintenta, como hace la propia app de ING. Requiere Habeas 0.9.7 o posterior."
}
},
{
"version": "2026-07-23.3",
"changes": {
"en": "Fixes duplicated card movements. A purchase stays pending until the monthly settlement, and ING re-listed it under a new identifier on every sync, so it arrived again each time. Two genuinely separate charges on the same day for the same amount are still kept apart. Needs Habeas 0.7.0.25 or newer.",
"es": "Corrige los movimientos de tarjeta duplicados. Una compra queda pendiente hasta la liquidación mensual, e ING la volvía a listar con un identificador nuevo en cada sincronización, así que llegaba otra vez. Dos cargos realmente distintos del mismo día y el mismo importe se siguen tratando por separado. Requiere Habeas 0.7.0.25 o posterior."
}
},
{
"version": "2026-07-16",
"changes": {
"en": "Map the movement's running balance to record.balanceAfter (promoted to a canonical field for the Cuéntamo contract). Requires extension >= 0.3.0.24.",
"es": "Mapea el saldo tras el movimiento a record.balanceAfter (promovido a campo canónico para el contrato del consumidor). Requiere extensión >= 0.3.0.24."
}
},
{
"version": "2026-07-14",
"changes": {
"en": "Initial: movements (transactions), per-account monthly statements (PDF/Excel), and the integrated monthly statement (PDF). Grouped by account, 90-day window.",
"es": "Inicial: movimientos (transactions), extractos mensuales por cuenta (PDF/Excel) y el extracto mensual integrado (PDF). Agrupada por cuenta, ventana de 90 días."
}
}
],
"content": {
"en": {
"slug": "ing-transactions",
"docs": "transactions and statements",
"h1": "Download all your ING transactions and statements at once",
"intro": "ING lets you view your transactions and download monthly statements, but one account and one month at a time. Pulling a whole year, or several accounts together, is repetitive manual work.",
"note": "ING serves a limited history — around 90 days — and enforces it strictly on current accounts, so Habeas brings that window rather than years back: the sooner you start, the more you keep. If you hold several accounts, it lets you choose which ones the export covers and remembers that choice between syncs.",
"title": "How to download your ING transactions and statements"
},
"es": {
"slug": "movimientos-ing",
"docs": "movimientos y extractos",
"h1": "Descarga todos tus movimientos y extractos de ING de una vez",
"intro": "ING te deja ver tus movimientos y descargar extractos mensuales, pero cuenta por cuenta y mes a mes. Reunir un año entero, o varias cuentas a la vez, es un trabajo manual repetitivo.",
"note": "ING sirve un histórico limitado —unos 90 días— y en las cuentas corrientes lo aplica de forma estricta, así que Habeas trae ese periodo, no años atrás: cuanto antes empieces, más conservas. Si tienes varias cuentas, te deja elegir cuáles entran en la exportación y recuerda esa selección entre sincronizaciones.",
"title": "Cómo descargar tus movimientos y extractos de ING"
}
},
"brand": "ING"
}