If you're like me and you use great open-source finance software like ActualBudget you probably want your Gnosis Pay transactions in CSV format for an easy import.
Fear not, me (and ChatGPT) have your back:
- Login to GnosisPay
- Open the console (
Ctrl + Shift + JorCmd + Option + J) - Paste this script. Don't blindly trust me, if you don't know JS then ask ChatGPT to explain what does this script do.
// Gnosis Pay – Export transactions since 12 Oct 2025 to CSV (with signed amounts)
(async () => {
const API_BASE = 'https://api.gnosispay.com';
const START_PATH = '/api/v1/cards/transactions?limit=200&offset=0';
const FILTER_DATE = new Date('2025-10-12T00:00:00Z');
// Helper to find or prompt for a Bearer token
function discoverToken() {
const looksLikeJWT = s => typeof s === 'string' && s.startsWith('eyJ');
for (const store of [localStorage, sessionStorage]) {
for (let i = 0; i < store.length; i++) {
const val = store.getItem(store.key(i));
if (looksLikeJWT(val)) return val;
if (val && val.includes('eyJ')) {
try {
const obj = JSON.parse(val);
for (const v of Object.values(obj)) if (looksLikeJWT(v)) return v;
} catch {}
}
}
}
return null;
}
async function fetchAll(bearer) {
const headers = { accept: 'application/json', authorization: `Bearer ${bearer}` };
let url = API_BASE + START_PATH;
const all = [];
while (url) {
const r = await fetch(url, { headers, credentials: 'include' });
if (!r.ok) throw new Error(`${r.status} ${r.statusText}`);
const j = await r.json();
if (Array.isArray(j.results)) all.push(...j.results);
url = j.next ? (j.next.startsWith('http') ? j.next : API_BASE + j.next) : null;
}
return all;
}
const toCSV = items => {
const lines = ['"Date","Transaction Amount","Merchant Name","Type"'];
for (const item of items) {
const date = item.createdAt
? new Date(item.createdAt).toLocaleDateString('en-GB').replace(/\//g, '-')
: '';
const decimals = item.billingCurrency?.decimals ?? 2;
const raw = Number(item.billingAmount || 0);
let amount = (raw / 10 ** decimals).toFixed(decimals);
// negative for spends, positive for refunds/credits
if (item.kind === 'Payment' || item.transactionType === '00') amount = -amount;
let merchant = (item.merchant?.name ?? 'Unknown').trim().replace(/"/g, '""');
const type = item.kind || 'Payment';
lines.push(`"${date}","${amount}","${merchant}","${type}"`);
}
return lines.join('\r\n');
};
try {
let token = discoverToken();
if (!token) token = prompt('Paste your Bearer token (without "Bearer "):', '');
if (!token) throw new Error('No Bearer token.');
const all = await fetchAll(token);
const filtered = all.filter(
t => new Date(t.createdAt) >= FILTER_DATE && t.status === 'Approved'
);
const csv = toCSV(filtered);
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' });
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = 'transactions.csv';
document.body.appendChild(a);
a.click();
a.remove();
console.log(`✅ Downloaded ${filtered.length} transactions from 12 Oct 2025 onward.`);
} catch (e) {
console.error('Error:', e);
alert(`Export failed: ${e.message}`);
}
})();