Downloading Balance History from Mint.com

I recently learned that Mint is shutting down, which is a bit of a bummer because I've been using them for 10 years as a way to keep an eye on my finances. The replacement service through Credit Karma looks feature incomplete and it will only preserve three years of history, which is a bit of a non-starter for me. I will most likely be looking for something else, but in the meantime, I've been looking for a way to preserve all my data, so I can take it with me.
The two types of data that are important to me are the transaction history and the balance history. The former is fairly straightforward to do through the web interface (though it has to be done in multiple parts since mint caps exports to 10k transactions), but the balance history is tricker. Under Trends > Net Worth
, I can set the filters to a single account and export the history to CSV, but the data will only contain monthly balances. However, I noticed that Mint internally has daily balance history, because when I manually set the date range to any range of 44 days or less, the daily balances show up. This is impractical to do for every account manually, so I went to work writing a script to automate this.
Version 1: Bash Scripts
For my first attempt, I used the developer console to find the rest API calls and copied it as a curl command:

I took this command, piped it into jq
to convert the data to CSV, and wrapped it in a bash loop over the data range. The end result looked something like this:
#!/bin/bash -e
ID=...
COOKIE=...
OUTPUT=data.csv
echo "Date,Amount" > "$OUTPUT"
DATE="2007-02-01" # Adjust based on your data to make it run faster
NOW="$(date +%Y-%m-%d)"
while [[ $DATE < $NOW ]]; do
curl 'https://mint.intuit.com/pfm/v1/trends' \
-H 'authority: mint.intuit.com' \
-H 'accept: application/json, text/plain, */*' \
-H 'accept-language: en-US,en;q=0.9' \
-H 'authorization: Intuit_APIKey intuit_apikey=prdakyresYC6zv9z3rARKl4hMGycOWmIb4n8w52r,intuit_apikey_version=1.0' \
-H 'content-type: application/json' \
-H "cookie: $COOKIE" \
-H 'intuit_tid: 27b096e4-5d76-4e55-958a-d01430a39a97' \
-H 'origin: https://mint.intuit.com' \
-H 'referer: https://mint.intuit.com/trends' \
-H 'sec-ch-ua: "Microsoft Edge";v="119", "Chromium";v="119", "Not?A_Brand";v="24"' \
-H 'sec-ch-ua-mobile: ?0' \
-H 'sec-ch-ua-platform: "Windows"' \
-H 'sec-fetch-dest: empty' \
-H 'sec-fetch-mode: cors' \
-H 'sec-fetch-site: same-origin' \
-H 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 Edg/119.0.0.0' \
--compressed \
--data @- <<EOF | jq -r '.Trend[]? | [.name, .amount] | @csv' >> "$OUTPUT"
{
"reportView": {
"type": "NET_WORTH"
},
"searchFilters": [
{
"matchAll": true,
"filters": [
{
"type": "AccountIdFilter",
"accountId": "$ID"
}
]
},
{
"matchAll": false,
"filters": []
}
],
"dateFilter": {
"type": "CUSTOM",
"startDate": "$DATE",
"endDate": "$(date +%Y-%m-%d -d "$DATE + 43 days")"
},
"offset": 0,
"limit": 1000
}
EOF
DATE=$(date +%Y-%m-%d -d "$DATE + 44 days")
done
Bash code to download Mint.com balance history
This generally worked, but it was a bit annoying to need to update my cookie whenever my Mint web session was logged out. It was also a bit slow, which could run into issues if Mint auto logs me out due to inactivity. So, I started looking at some other options.
Version 2: JavaScript
In the developer console, I noticed I could also copy the REST API network request as a JavaScript fetch()
statement. I got an idea that perhaps I could write a little script that could be executed in the JavaScript console. The nice thing about that is that I don't need to manually update the cookie, because the browser automatically includes that when the request is sent. I can also do more advanced data processing and send queries in parallel. The end result was:
await (async function(console) {
let begin = new Date();
// Fetch list of accounts
let accounts = await fetch("https://mint.intuit.com/pfm/v1/accounts?offset=0&limit=1000", {
"headers": {
"authorization": "Intuit_APIKey intuit_apikey=prdakyresYC6zv9z3rARKl4hMGycOWmIb4n8w52r,intuit_apikey_version=1.0",
"content-type": "application/json",
"intuit_tid": "0dc9c908-ef3d-4d97-93eb-a676a74b0cb2",
},
"body": null,
"method": "GET",
"mode": "cors",
"credentials": "include"
}).then(
response => response.json()
).then(
result => result.Account ?? []
);
console.log("Accounts:", accounts);
// data[date][account] = amount
let data = {};
for (const [index, account] of accounts.entries()) {
const isDebtAccount = (account.type == "CreditAccount" || account.type == "LoanAccount");
let ids = [account.id];
if (isDebtAccount) {
// Querying net worth doesn't seem to work for debt accounts, unless
// there is at least one assert account that covers the same date range.
// To work around this, add the bank accounts to the query, so that a
// mix of ASSET and DEBT entries are returned. (The ASSET entries are
// filtered out below).
ids.push(...accounts.filter(({type}) => (type == 'BankAccount')).map(({id}) => id));
}
console.log(`Fetching account ${index+1}/${accounts.length}: ${account.name}`)
let queries = [];
for (let start = new Date('2007-01-01'); start < new Date(); start.setUTCDate(start.getUTCDate()+44)) {
// Query data for date range in parallel
let end = new Date(start-1); end.setUTCDate(end.getUTCDate()+44);
queries.push(fetch("https://mint.intuit.com/pfm/v1/trends", {
"headers": {
"authorization": "Intuit_APIKey intuit_apikey=prdakyresYC6zv9z3rARKl4hMGycOWmIb4n8w52r,intuit_apikey_version=1.0",
"content-type": "application/json",
"intuit_tid": "0dc9c908-ef3d-4d97-93eb-a676a74b0cb2",
},
"body": JSON.stringify({
"reportView": {
"type": "NET_WORTH"
},
"searchFilters": [
{
"matchAll": true,
"filters": ids.map(id => ({ "type": "AccountIdFilter", "accountId": id })),
},
],
"dateFilter": {
"type": "CUSTOM",
"startDate": start.toISOString().substring(0,10),
"endDate": end.toISOString().substring(0,10),
},
"offset": 0,
"limit": 1000,
}),
"method": "POST",
"mode": "cors",
"credentials": "include",
}).then(
response => response.json()
).then(result => {
let trends = result.Trend ?? [];
//console.log("Trends:", trends)
for (const {name: date, type, amount} of trends) {
if (isDebtAccount && type == 'DEBT') {
(data[date] ??= {})[account.id] = -amount;
} else if (!isDebtAccount && type == 'ASSET') {
(data[date] ??= {})[account.id] = amount;
}
}
}));
// Wait for some requests to complete so that we don't get trottled
if (queries.length >= 10) {
await Promise.all(queries);
queries = [];
}
}
// Wait for remaining requests to complete
await Promise.all(queries);
// Wiggle mouse to keep from getting logged out
document.body.dispatchEvent(new MouseEvent("mousedown"))
}
// Build csv content
console.log("Data:", data);
let csv_lines = [[`"Date"`, ...accounts.map(({name}) => `"${name}"`)].join(",")];
for (const date of Object.keys(data).sort((a, b) => (new Date(a) - new Date(b)))) {
csv_lines.push([`"${date}"`, ...accounts.map(({id}) => `"${data[date][id] ?? ""}"`)].join(","));
}
// Save csv file
const blob = new Blob([csv_lines.join("\r\n")], {type: 'text/csv'});
let a = document.createElement('a');
a.download = "mint_bal_history.csv"
a.href = window.URL.createObjectURL(blob)
a.dataset.downloadurl = ['text/csv', a.download, a.href].join(':')
a.dispatchEvent(new MouseEvent("click"))
console.log(`Finished in ${(new Date() - begin)/60000} minutes`)
})(console)
JavaScript code to download Mint.com balance History
The script prints progress as it goes. For 10 years of data and 88 accounts, it takes about 12 minutes to run. When it finishes, it downloads a CSV file that looks something like:
Date | Membership Share | Money Mkt Advantage | Free Checking | ... |
---|---|---|---|---|
2/19/2013 | 8007.6 | 5479.63 | ... | |
2/20/2013 | 8007.6 | 5479.63 | ... | |
2/21/2013 | 8007.6 | 479.63 | ... | |
... | ... | ... | ... | .. |
11/6/2016 | 5216.34 | 0 | 3503.31 | .. |
11/7/2016 | 5216.34 | 0 | 1567.27 | .. |
... | ... | ... | ... | .. |
(The blank entries for Membership Share
are because that account did not exist until later, so there is no data).
How to Run this for Yourselves
For those of you interested in running this yourselves, I would urge caution regarding running code from strangers on the Internet, especially if you do not know how to read JavaScript for yourselves. Self XSS is a common tactic that scammers will use to take over your account. I have also only tested this on my Mint account and that of one other person, so no guarantees that this will work for you. If you understand the risks, here are the steps:
- Open an incognito tab in a Chrome or Chromium based browser (I havwe tested on Microsoft Edge).
- Login to your Mint account.
- From any page on Mint, open up the developer console by right clicking and selecting "Inspect" (or
Ctrl + Shift + I
). - Find the "Console" tab.
- Paste the JavaScript code from the previous section into the console and press
Enter
. - Look for messages such as
Fetching account 1/88: Membership Share
in the console to show up every 10-20 seconds, which show the progress of the script.- If you need to abort the script, just reload the page.
- At the end, a message like
Finished in 12.31635 minutes
will be shown andmint_bal_history.csv
will show up in your Downloads.
Update 2023-11-16: I noticed today that Monarch has built a browser plugin to download all the Mint data, including the daily balance history. That is probably a more robust option than what I posted here.