Introduction
Mint is a online interface that lets you link all of your bank accounts and credit cards
to it and see all of your financial transactions at once.
I like to keep track my myspending, but right now that means downloading CSV files from about five different
websites, importing them into Excel and classifying all of them manuall.
If I could access the data on Mint and download it, I’d have all of my transactions in one place and I could
categorize them and analyze them programmatically.
Goal
My current goals are:
Get all transaction data from Mint using a Python script
Categorize the transactions into one of my budget categories
Generate a budget analysis of my spending and remaining amount in each budget category
If I can run this script once a week or so I can get a report on my budget status and use that to direct my spending.
Python Mint API
There’s a Python API for Mint that can be found here .
It relies on using a Chrome browswer to scrape data off of the site to gather your financial records.
Installing the API
Run this at the command line:
C:\Users\sfrie> pip install mintapi
Collecting mintapi
Downloading https://files.pythonhosted.org/packages/42/1e/cd1e5803d565d3c379595254059289847da74eb4ddf01e6e144839f92343/mintapi-1.44.tar.gz
Collecting future (from mintapi)
Downloading https://files.pythonhosted.org/packages/45/0b/38b06fd9b92dc2b68d58b75f900e97884c45bedd2ff83203d933cf5851c9/future-0.18.2.tar.gz (829kB)
|████████████████████████████████| 829kB 6.8MB/s
Collecting mock (from mintapi)
Downloading https://files.pythonhosted.org/packages/5c/03/b7e605db4a57c0f6fba744b11ef3ddf4ddebcada35022927a2b5fc623fdf/mock-4.0.3-py3-none-any.whl
Collecting requests (from mintapi)
Downloading https://files.pythonhosted.org/packages/29/c1/24814557f1d22c56d50280771a17307e6bf87b70727d975fd6b2ce6b014a/requests-2.25.1-py2.py3-none-any.whl (61kB)
|████████████████████████████████| 61kB 1.9MB/s
Collecting selenium-requests (from mintapi)
Downloading https://files.pythonhosted.org/packages/78/72/94eadc1667bf4e17e1c5f880fdf8715144b22600114ee68266c8bdde3a09/selenium-requests-1.3.zip
Collecting xmltodict (from mintapi)
Downloading https://files.pythonhosted.org/packages/28/fd/30d5c1d3ac29ce229f6bdc40bbc20b28f716e8b363140c26eff19122d8a5/xmltodict-0.12.0-py2.py3-none-any.whl
Collecting pandas> = 1.0 ( from mintapi)
Downloading https://files.pythonhosted.org/packages/f4/b0/63aa0d048e4c3be3f0d2c3851cde44ce644bac3f527f9239df5ca15947d1/pandas-1.1.5-cp38-cp38-win32.whl (7.9MB)
|████████████████████████████████| 7.9MB 6.4MB/s
Collecting selenium (from mintapi)
Downloading https://files.pythonhosted.org/packages/80/d6/4294f0b4bce4de0abf13e17190289f9d0613b0a44e5dd6a7f5ca98459853/selenium-3.141.0-py2.py3-none-any.whl (904kB)
|████████████████████████████████| 911kB ...
Collecting oathtool (from mintapi)
Downloading https://files.pythonhosted.org/packages/75/cd/9d35865c9a581dd2c39598d07955b494e3cc704f3f2f6d2dd47f8dff78a5/oathtool-2.3.0-py3-none-any.whl
Collecting certifi> = 2017.4.17 ( from requests->mintapi)
Downloading https://files.pythonhosted.org/packages/5e/a0/5f06e1e1d463903cf0c0eebeb751791119ed7a4b3737fdc9a77f1cdfb51f/certifi-2020.12.5-py2.py3-none-any.whl (147kB)
|████████████████████████████████| 153kB 6.4MB/s
Collecting chardet<5,> = 3.0.2 ( from requests->mintapi)
Downloading https://files.pythonhosted.org/packages/19/c7/fa589626997dd07bd87d9269342ccb74b1720384a4d739a1872bd84fbe68/chardet-4.0.0-py2.py3-none-any.whl (178kB)
|████████████████████████████████| 184kB ...
Collecting idna<3,> = 2.5 ( from requests->mintapi)
Downloading https://files.pythonhosted.org/packages/a2/38/928ddce2273eaa564f6f50de919327bf3a00f091b5baba8dfa9460f3a8a8/idna-2.10-py2.py3-none-any.whl (58kB)
|████████████████████████████████| 61kB 4.1MB/s
Collecting urllib3<1.27,> = 1.21.1 ( from requests->mintapi)
Downloading https://files.pythonhosted.org/packages/f5/71/45d36a8df68f3ebb098d6861b2c017f3d094538c0fb98fa61d4dc43e69b9/urllib3-1.26.2-py2.py3-none-any.whl (136kB)
|████████████████████████████████| 143kB 6.4MB/s
Collecting six (from selenium-requests-> mintapi)
Downloading https://files.pythonhosted.org/packages/ee/ff/48bde5c0f013094d729fe4b0316ba2a24774b3ff1c52d924a8a4cb04078a/six-1.15.0-py2.py3-none-any.whl
Collecting tldextract (from selenium-requests-> mintapi)
Downloading https://files.pythonhosted.org/packages/7e/62/b6acd3129c5615b9860e670df07fd55b76175b63e6b7f68282c7cad38e9e/tldextract-3.1.0-py2.py3-none-any.whl (87kB)
|████████████████████████████████| 92kB 5.8MB/s
Collecting numpy> = 1.15.4 ( from pandas>= 1.0->mintapi)
Downloading https://files.pythonhosted.org/packages/b1/a6/e570ac0fdc466491b2c5d54dc3373292de50b5a326f383909a6cfa224fbe/numpy-1.19.4-cp38-cp38-win32.whl (11.0MB)
|████████████████████████████████| 11.0MB 6.4MB/s
Collecting pytz> = 2017.2 ( from pandas>= 1.0->mintapi)
Downloading https://files.pythonhosted.org/packages/12/f8/ff09af6ff61a3efaad5f61ba5facdf17e7722c4393f7d8a66674d2dbd29f/pytz-2020.4-py2.py3-none-any.whl (509kB)
|████████████████████████████████| 512kB ...
Collecting python-dateutil> = 2.7.3 ( from pandas>= 1.0->mintapi)
Downloading https://files.pythonhosted.org/packages/d4/70/d60450c3dd48ef87586924207ae8907090de0b306af2bce5d134d78615cb/python_dateutil-2.8.1-py2.py3-none-any.whl (227kB)
|████████████████████████████████| 235kB 6.8MB/s
Collecting importlib-resources; python_version < "3.9" ( from oathtool->mintapi)
Downloading https://files.pythonhosted.org/packages/8d/94/2f6ceee0c4e63bff0177c07e68d27c937a19f6bc77c4739755b49f5adb04/importlib_resources-3.3.1-py2.py3-none-any.whl
Collecting autocommand (from oathtool-> mintapi)
Downloading https://files.pythonhosted.org/packages/61/55/9fb7c5a63fe0a797054034ce9aeacded2ca078690c63413ebfa06c47ee56/autocommand-2.2.1-py3-none-any.whl
Collecting path (from oathtool-> mintapi)
Downloading https://files.pythonhosted.org/packages/ee/11/9f51c02c14cdd29383bd3a880d472a22629e090fbd1415075c979ff76d94/path-15.0.1-py3-none-any.whl
Collecting filelock> = 3.0.8 ( from tldextract->selenium-requests->mintapi)
Downloading https://files.pythonhosted.org/packages/93/83/71a2ee6158bb9f39a90c0dea1637f81d5eef866e188e1971a1b1ab01a35a/filelock-3.0.12-py3-none-any.whl
Collecting requests-file> = 1.4 ( from tldextract->selenium-requests->mintapi)
Downloading https://files.pythonhosted.org/packages/77/86/cdb5e8eaed90796aa83a6d9f75cfbd37af553c47a291cd47bc410ef9bdb2/requests_file-1.5.1-py2.py3-none-any.whl
Installing collected packages: future, mock, certifi, chardet, idna, urllib3, requests, selenium, six, filelock, requests-file, tldextract, selenium-requests, xmltodict, numpy, pytz, python-dateutil, pandas, importlib-resources, autocommand, path, oathtool, mintapi
Running setup.py install for future ... done
Running setup.py install for selenium-requests ... done
Running setup.py install for mintapi ... done
Successfully installed autocommand-2.2.1 certifi-2020.12.5 chardet-4.0.0 filelock-3.0.12 future-0.18.2 idna-2.10 importlib-resources-3.3.1 mintapi-1.44 mock-4.0.3 numpy-1.19.4 oathtool-2.3.0 pandas-1.1.5 path-15.0.1 python-dateutil-2.8.1 pytz-2020.4 requests-2.25.1 requests-file-1.5.1 selenium-3.141.0 selenium-requests-1.3 six-1.15.0 tldextract-3.1.0 urllib3-1.26.2 xmltodict-0.12.0
That was successful.
The, to make sure it works, I try this:
C:\Users\sfrie> python mintapi/api.py --keyring --headless sfriederichs@gmail.com
python: can't open file 'mintapi/api.py': [Errno 2] No such file or directory
Well, that must be somehwere else. Let’s find it.
The file is located here: C:\Users\sfrie\AppData\Local\Programs\Python\Python38-32\Lib\site-packages
Let’s navigate there and try again.
Here’s another wrinkle:
C:\Users\sfrie\AppData\Local\Programs\Python\Python38-32\Lib\site-packages> python mintapi/api.py --keyring --headless sfriederichs@gmail.com
usage: api.py [-h] [--session-path [SESSION_PATH]] [--accounts] [--budgets] [--budget_hist] [--net-worth]
[--credit-score] [--credit-report] [--extended-accounts] [--transactions] [--extended-transactions]
[--start-date [START_DATE]] [--include-investment] [--skip-duplicates] [--show-pending]
[--filename FILENAME] [--keyring] [--headless] [--use-chromedriver-on-path]
[--chromedriver-download-path CHROMEDRIVER_DOWNLOAD_PATH] [--mfa-method {sms,email,soft-token}]
[--mfa-token MFA_TOKEN] [--imap-account IMAP_ACCOUNT] [--imap-password IMAP_PASSWORD]
[--imap-server IMAP_SERVER] [--imap-folder IMAP_FOLDER] [--imap-test] [--no_wait_for_sync]
[--wait_for_sync_timeout WAIT_FOR_SYNC_TIMEOUT] [--attention]
[email] [password]
api.py: error: --keyring can only be used if the `keyring` library is installed.
Alright, does installing this do it?
C:\Users\sfrie\AppData\Local\Programs\Python\Python38-32\Lib\site-packages> pip install keyring
Collecting keyring
Downloading https://files.pythonhosted.org/packages/e2/23/c15f403d1993a003a711a37318bbe66096c0802b265047919d5c14a4d693/keyring-21.7.0-py3-none-any.whl
Collecting pywin32-ctypes!=0.1.0,!=0.1.1; sys_platform == "win32" ( from keyring)
Downloading https://files.pythonhosted.org/packages/9e/4b/3ab2720f1fa4b4bc924ef1932b842edf10007e4547ea8157b0b9fc78599a/pywin32_ctypes-0.2.0-py2.py3-none-any.whl
Installing collected packages: pywin32-ctypes, keyring
Successfully installed keyring-21.7.0 pywin32-ctypes-0.2.0
Now, here’s what I did to test the script:
C:\Users\sfrie\AppData\Local\Programs\Python\Python38-32\Lib\site-packages> python mintapi/api.py --keyring --headless sfriederichs@gmail.com
Mint password:
DevTools listening on ws://127.0.0.1:59829/devtools/browser/9da9d1fd-bbdc-4dc3-be79-0f664fd73765
[1223/134828.031:INFO:CONSOLE(1)] "analytics performance metrics [object Object]", source: https://mint.intuit.com/handlebars/common_3.0.1170/common/cms/js/csa-init.js (1)
[1223/135927.928:INFO:CONSOLE(1)] "analytics performance metrics [object Object]", source: https://mint.intuit.com/handlebars/common_3.0.1170/common/cms/js/csa-init.js (1)
[1223/135927.934:INFO:CONSOLE(1)] "mtx-track-star.js has been loaded.", source: https://mint.intuit.com/handlebars/common/cms/js/track-star.js (1)
Well something happened anyway. I’ll call it good.
Basic Mint Script
Here’s the suggsted basic script for Mint:
import mintapi
mint = mintapi . Mint (
'your_email@web.com' , # Email used to log in to Mint
'password' , # Your password used to log in to mint
# Optional parameters
mfa_method = 'sms' , # Can be 'sms' (default), 'email', or 'soft-token'.
# if mintapi detects an MFA request, it will trigger the requested method
# and prompt on the command line.
headless = False , # Whether the chromedriver should work without opening a
# visible window (useful for server-side deployments)
mfa_input_callback = None , # A callback accepting a single argument (the prompt)
# which returns the user-inputted 2FA code. By default
# the default Python `input` function is used.
session_path = None , # Directory that the Chrome persistent session will be written/read from.
# To avoid the 2FA code being asked for multiple times, you can either set
# this parameter or log in by hand in Chrome under the same user this runs
# as.
imap_account = None , # account name used to log in to your IMAP server
imap_password = None , # account password used to log in to your IMAP server
imap_server = None , # IMAP server host name
imap_folder = 'INBOX' , # IMAP folder that receives MFA email
wait_for_sync = False , # do not wait for accounts to sync
wait_for_sync_timeout = 300 , # number of seconds to wait for sync
use_chromedriver_on_path = False , # True will use a system provided chromedriver binary that
# is on the PATH (instead of downloading the latest version)
)
# Get basic account information
mint . get_accounts ()
# Get extended account detail at the expense of speed - requires an
# additional API call for each account
mint . get_accounts ( True )
# Get budget information
mint . get_budgets ()
# Get transactions
mint . get_transactions () # as pandas dataframe
mint . get_transactions_csv ( include_investment = False ) # as raw csv data
mint . get_transactions_json ( include_investment = False , skip_duplicates = False )
# Get transactions for a specific account
accounts = mint . get_accounts ( True )
for account in accounts :
mint . get_transactions_csv ( id = account [ "id" ])
mint . get_transactions_json ( id = account [ "id" ])
# Get net worth
mint . get_net_worth ()
# Get credit score
mint . get_credit_score ()
# Get bills
mint . get_bills ()
# Get investments (holdings and transactions)
mint . get_invests_json ()
# Close session and exit cleanly from selenium/chromedriver
mint . close ()
# Initiate an account refresh
mint . initiate_account_refresh ()
So, this is what happens when I run it:
C:\Users\sfrie\Dropbox\Projects\pyMint> python mintTest.py
DevTools listening on ws://127.0.0.1:60684/devtools/browser/4e663c4d-b730-4f39-b211-068829104a38
[32600:37920:1223/140751.559:ERROR:device_event_log_impl.cc(211)] [14:07:51.559] USB: usb_device_handle_win.cc:1020 Failed to read descriptor from node connection: A device attached to the system is not functioning. (0x1F)
[32600:37920:1223/140751.562:ERROR:device_event_log_impl.cc(211)] [14:07:51.561] USB: usb_device_handle_win.cc:1020 Failed to read descriptor from node connection: A device attached to the system is not functioning. (0x1F)
[32600:37920:1223/140751.567:ERROR:device_event_log_impl.cc(211)] [14:07:51.567] USB: usb_device_handle_win.cc:1020 Failed to read descriptor from node connection: A device attached to the system is not functioning. (0x1F)
[32600:37920:1223/140751.571:ERROR:device_event_log_impl.cc(211)] [14:07:51.570] USB: usb_device_handle_win.cc:1020 Failed to read descriptor from node connection: A device attached to the system is not functioning. (0x1F)
Traceback (most recent call last):
File "mintTest.py", line 63, in <module>
mint.get_transactions_csv(id=account["id"])
TypeError: get_transactions_csv() got an unexpected keyword argument 'id'
Hmm, that’s interesting. I wonder if there’s an API reference that I can use…
No, not a written one. I can look at the API code though. I see this:
def get_transactions_csv ( self , include_investment = False , acct = 0 ):
So ‘id’ should probably be ‘acct’.
However, the json version of this still uses ‘id’ so don’t change that.
The get_credit_score function seems to have errors internally. I won’t use it.
Getting Transactions as CSV
Okay, I’m using this script to grab the transactions:
import mintapi
import csv
from io import StringIO
mint = mintapi . Mint (
'username' , # Email used to log in to Mint
'password' , # Your password used to log in to mint
# Optional parameters
mfa_method = 'sms' , # Can be 'sms' (default), 'email', or 'soft-token'.
# if mintapi detects an MFA request, it will trigger the requested method
# and prompt on the command line.
headless = True , # Whether the chromedriver should work without opening a
# visible window (useful for server-side deployments)
mfa_input_callback = None , # A callback accepting a single argument (the prompt)
# which returns the user-inputted 2FA code. By default
# the default Python `input` function is used.
session_path = None , # Directory that the Chrome persistent session will be written/read from.
# To avoid the 2FA code being asked for multiple times, you can either set
# this parameter or log in by hand in Chrome under the same user this runs
# as.
imap_account = None , # account name used to log in to your IMAP server
imap_password = None , # account password used to log in to your IMAP server
imap_server = None , # IMAP server host name
imap_folder = 'INBOX' , # IMAP folder that receives MFA email
wait_for_sync = False , # do not wait for accounts to sync
wait_for_sync_timeout = 300 , # number of seconds to wait for sync
use_chromedriver_on_path = False , # True will use a system provided chromedriver binary that
# is on the PATH (instead of downloading the latest version)
)
# Get transactions
#mint.get_transactions() # as pandas dataframe
csvData = mint . get_transactions_csv ( include_investment = False ) # as raw csv data
csvDataHandle = StringIO ( csvData . decode ( "utf-8" ))
mint . close ()
reader = csv . reader ( csvDataHandle , delimiter = ',' )
for date , desc , amount , transType , classification , account , burn1 , burn2 , burn3 in reader :
print ( str ( date ) + "," + str ( desc ) + "," + str ( transType ) + "," + str ( classification ) + "," + str ( account ) + "," + str ( burn1 ) + "," + str ( burn2 ) + "," + str ( burn3 ))
Resources