I tweet every time I spend money

June 15, 2020 ☼ publishingwriting

For the past few months, I’ve been tweeting every time I spend money. You can check it out at @ch_ylukem1.

It started with an Uber ride:

UBER — $23.03

— ch (@ch_ylukem) October 17, 2019

I took December 2019 off to travel Asia and visit family in Europe. The account became a fun place to catalog my travels.

A hotel I stayed at in Tokyo:

HOTEL ON BOOKING COM — 24,024.00 JPY

— ch (@ch_ylukem) December 5, 2019

Getting around Bangkok:

GRABTAXI (THAILAN... — $1.65

— ch (@ch_ylukem) December 7, 2019

My favorite coffee shop in Oslo:

TIM WENDELBOE — $13.42

— ch (@ch_ylukem) December 27, 2019

Rolling my own Mint.com

To make this work, the first thing I had to do was enable email notifications at every bank’s website. Banks slightly bury this functionality, but just look for Alerts” somewhere in the settings and you’ll find it.

chase’s alerts portal

citi’s alerts portal

Citibank and Chase support this flawlessly; they let you configure email-based transactions for everything above $0.01. I had a harder time with my Barclays card2. American Express almost gets there, but only will send you an email if the charge is above $10.00. Good enough, I guess.

Once I started getting these transaction emails in my email, I wrote a Lambda function that scrapes the merchant name and amount from the email’s HTML using cheerio. It’s not the prettiest thing, but for each bank I wrote a function that returns a string.

{|<} Citibank transaction email The email that Citibank sends

const citibank = jq => {
  const merchant = jq("td")
    .filter((i, el) => jq(el).text() === "Merchant")
    .next()
    .text();

  const amount = jq("span")
    .filter((i, el) => jq(el).text() === "Amount:")
    .closest("tr")
    .text()
    .replace("Amount:", "")
    .trim();
  return `${merchant}${amount}`;
};

Code for scraping Citibank transaction emails

After that, I configured Amazon’s SES email hosting at my domain. SES can trigger a Lambda function with each incoming email.

Here’s all of it together:

  1. A Gmail filter forwards each transaction alert email to my AWS SES account
  2. AWS SES saves the email to an S3 bucket and triggers a Lambda function
  3. The Lambda function scrapes the merchant name and amount from the email and sends the tweet

Why do this?

Some time ago, I remember reading online that the easiest way to get one’s own financial data in real time isn’t to try to scrape it from the bank’s website, but to instead use email.

Later, I got the idea to start publishing my transactions from seeing @SbarroChica on twitter. I really liked seeing credit card transactions interspersed with tweets about her day.

This also isn’t the first time I’ve experimented with publishing my own data; I used to operate an API that published my age, what music I’m listening to, and my sleep history.

It’s been fun seeing friends of mine interact with this, in predictable ways and otherwise. Almost immediately, people started reacting to transactions:

they give you meals even if the flight is like 45 min

they also have a terrible airline safety record

— Jarred Sumner (@jarredsumner) December 3, 2019

this is a lot of hichew

— Alex Sexton (@SlexAxton) December 18, 2019

gotta stock up on dat tp

— Hallie Lomax (@hrlomax) March 20, 2020

What’s the worst that can happen?

An unintended consequence has been that people have been using this information to more accurately split the bill; in cases where I’ve told people don’t worry about dinner”, they do in fact worry about it”, and dutifully I get a Venmo with half of the cost of dinner, since the amount is published publicly.

Naturally, this account got a lot more boring once shelter-in-place began. Taxi rides and restaurant transactions got replaced with online orders for tea and Instant Pots. The account even had a month long outage that I barely noticed until a former coworker tweeted about it.

I intend to run this as experiment indefinitely. It might end one day, but like I said when I first posted about this, I’m excited to see how it goes wrong.


  1. The name is an homage to my former employer, Stripe. Charge IDs in their system always start with ch_.

  2. I used to have an Uber Visa Card issued by Barclays. Barclays would send transaction alert emails that didn’t have the name of the merchant in the email. Since I only used this card to buy coffee, I instead wrote a scraper for the Square receipts the coffee shop would send with each coffee.