Abstract minimal data flow visualization
Andrejkostal.sk logo

Stop manually entering transactions into Actual Budget and YNAB

Nov 2, 2025

Stop manually entering transactions into Actual Budget and YNAB - Andrej Kostal

If you use a personal finance app like Actual Budget or YNAB, you’ve probably experienced the tedium of manual transaction entry. Maybe you’ve wished your bank had an API integration. Maybe you’ve looked into third-party services like Plaid or Salt Edge, only to find your regional European bank isn’t supported.

Here’s the thing: you already have a data feed from your bank. It arrives in your inbox several times a day. Those transaction notification emails contain everything you need: amounts, dates, merchants, transaction IDs. The question isn’t whether the data exists. It’s whether you can extract it reliably and automatically.

I’ve been running an email-based transaction import system for over a year now. It’s not perfect, but it handles 95% of my transactions automatically. No bank API. No third-party services. Just email parsing, some regular expressions, and a scheduled task.

What excites me about this approach isn’t the technical cleverness, it’s the reliability. Once it’s set up, it just works. Emails arrive, transactions import, budget stays current. The maintenance burden is minimal. And unlike third-party integrations that can break when APIs change or services shut down, email parsing depends only on your inbox, something that’s not going anywhere.

Here’s how this approach works, when it makes sense, and what you need to think about if you want to build something similar.

Is Email Parsing Right for You?

Before diving into how it works, here’s a quick decision tree:

START: Does your bank have API integration with your budget app?

  ├─ YES → Use native integration

  └─ NO → Does a service like Plaid support your bank?

      ├─ YES → Comfortable with third-party access?
      │   ├─ YES → Use Plaid/Salt Edge
      │   └─ NO → Continue below

      └─ NO → Does your bank send email notifications for all transactions?

          ├─ NO → Stick with manual entry or CSV exports

          └─ YES → Comfortable with basic scripting and regex?

              ├─ YES → Email parsing is a good fit ✓
              └─ NO → Manual entry might be easier

Why I Decided on Email Parsing

Actual Budget supports bank connections through GoCardless API, and I tried it. The problem wasn’t availability, it was reliability. GoCardless would sync only a few times per day, and sometimes transactions simply didn’t come through at all. When you’re trying to maintain an accurate budget, missing transactions or 12-hour delays defeat the purpose.

Email notifications, on the other hand, arrive within minutes of each transaction and are about 99% reliable. Banks have strong incentives to deliver these notifications, they’re using battle-tested email infrastructure. That reliability difference made email parsing the obvious choice for me, despite the extra setup work.

Why Email Notifications Are Actually a Good Data Source

Most banks send transaction notifications by email. These aren’t marketing emails or monthly statements. They’re real-time alerts sent within minutes of each transaction. They’re structured, consistent, and contain all the essential data:

  • Transaction amount and currency
  • Date and time
  • Merchant or payee name
  • Transaction ID (usually)
  • Account identifier

The structure is predictable because these emails are automated. The same template gets used for every transaction. That predictability is exactly what makes them parsable.

The security story is interesting too. You’re not giving a third party access to your bank account. You’re not storing bank credentials anywhere. You’re reading emails that are already being sent to you. If someone compromises your email, they can already see these transactions. The automation doesn’t create a new security surface, it just reads what’s already there.

Key Security Insight: Email parsing doesn’t create a new security surface. If your email is compromised, attackers can already see your transaction notifications. This approach simply automates reading what’s already there, no bank credentials stored, no third-party access granted.

This isn’t a theoretical exercise. I’ve processed thousands of transactions this way. The email notifications are more reliable than you’d expect. Banks don’t change email templates often. When they do, the parser breaks loudly and obviously, you notice immediately that transactions stopped importing.

The Architecture Behind Email-Based Budget Automation

The system has four distinct pieces, and understanding each one helps you see where the complexity lives.

Email Client - This connects to your email provider via IMAP (the standard protocol for accessing email on a server) and fetches transaction notifications. It looks for unread emails from your bank’s sender address, typically within a specific date range. The key capability here is filtering: you want transaction notifications, not marketing emails or account statements. Most banks use consistent sender addresses for transaction alerts, which makes filtering straightforward.

Parser - This is where the intelligence lives. The parser examines each email and extracts structured data: amount, currency, date, merchant name, transaction ID. It uses pattern matching (regular expressions, usually) to find these fields in the email text. Each bank’s email format is different, so you need a dedicated parser for each bank. This is the part that breaks when banks change their email templates.

I learned this the hard way. My first parser was rigid—it looked for exact text matches and broke immediately when my bank changed “Amount:” to “Transaction amount:”. Rebuilding with flexible patterns took 20 minutes but saved me from monthly maintenance headaches.

Budget API Client - Once you have structured transaction data, you need to send it to your budget app. Both Actual Budget and YNAB provide APIs for importing transactions. The client handles authentication, formats the transaction data according to the API specification, and submits it. The API typically returns confirmation or error details.

Duplicate Detection - Banks might send the same transaction notification multiple times. Your script might process the same email twice. Duplicate detection prevents importing the same transaction repeatedly. The most reliable approach: extract a unique transaction ID from the email and send it along with the transaction data. Many budget APIs can use this ID to reject duplicates.

Here’s how these components work together:

[Email Inbox]
    → Fetch unread emails from bank

[Parser]
    → Extract: amount, date, merchant, transaction ID

[Budget API Client]
    → Send to Actual Budget/YNAB
    → Include transaction ID for duplicate detection

[Mark Email as Read]
    → Prevents reprocessing

The flow is linear and stateless. Each run fetches unread emails, processes them, imports transactions, and marks emails as read. If the script crashes halfway through, the worst case is some transactions get imported twice—which duplicate detection handles. There’s no complex state to corrupt or recover.

What You Can Actually Extract from Bank Emails

Not all transaction emails contain the same information. What you can extract depends on what your bank includes. Here’s what’s typically available and how it maps to budget app fields:

Transaction Amount and Currency - Always present and usually easy to extract. Most emails format it clearly: “Amount: 45.67 EUR” or “$123.45”. Watch for negative vs. positive amounts.

Date and Time - Usually present, but formatting varies wildly. You’ll need to handle your bank’s specific format and convert it to whatever your budget API expects. Time is often included but rarely useful, date is what matters.

Merchant or Payee Name - Banks include whatever the payment terminal transmitted, which might be abbreviated or include location codes. “STARBUCKS #8234 SEATTLE WA” becomes your payee name. The raw extraction just passes through whatever the email contains.

Transaction ID - Your duplicate detection mechanism. Most banks include a unique identifier labeled “Transaction ID”, “Reference”, or “Authorization Code”. Extract this and send it with the transaction. Budget APIs can use it to reject duplicates.

Account Identifier - For multiple accounts with the same bank, the email might specify which account was used. You need this to route the transaction to the correct account in your budget app.

Here’s what actual extraction looks like:

FieldExample from EmailExtracted ValueAPI Field
Amount”Amount: 45.67 EUR”45.67, EURamount, currency
Date”2025-11-02 14:35”2025-11-02date
Merchant”STARBUCKS #8234 SEATTLE WA”STARBUCKS #8234 SEATTLE WApayee_name
Transaction ID”ID: 221025/131110-2”221025/131110-2imported_id
Account”Card ending **5633”5633account routing

The extraction is pattern matching, looking for text like “Amount: 45.67 EUR” and capturing the values. Regular expressions work well because bank emails are structured and consistent.

Security and Access Patterns

Email-based automation requires IMAP access to your inbox. How you handle this matters.

App-Specific Passwords and OAuth - Don’t use your main email password for automation. As of May 2025, Gmail requires OAuth 2.0 for IMAP access, and app-specific passwords are being phased out. For Gmail, you’ll need to implement OAuth 2.0 authentication, which requires going through the OAuth consent screen once to obtain a refresh token for unattended access. Other email providers may still support app-specific passwords, limited-scope credentials that can be revoked independently. If it’s compromised or you stop using the system, revoke it without affecting your main email access.

TLS Encryption - IMAP connections should use TLS encryption. This is standard for any IMAP client library. It ensures email credentials and content aren’t transmitted in cleartext.

Read-Only Access - The automation only needs to read emails and mark them as read. Verify your code isn’t doing anything beyond fetching and marking.

Email Aliases for Account Mapping - Use email aliases to route different accounts to different inboxes. If your email is [email protected], many providers let you use [email protected] and [email protected], though not all email providers or banks support this. Your parser can use the recipient address to determine which budget account receives the transaction.

Run Environment - Common choices: a personal server, Raspberry Pi, cloud VM, or local machine on schedule. Running it under your own user account on a machine you control keeps the data local and private.

Making It Extensible: The Parser Interface Pattern

You’ll probably want to support multiple banks eventually. Maybe you have accounts at two banks. Maybe you want to share your system with a friend who uses a different bank. Extensibility means new banks don’t require rewriting the entire system.

The parser interface pattern solves this. Each bank gets its own parser class that implements the same interface. The interface defines whether a parser can handle a given email (usually by checking the sender address) and how to extract transaction data into a standardized structure. The main automation loops through available parsers until it finds one that can handle the current email, then uses it to extract the data. This keeps bank-specific logic contained. When a bank changes its email format, you update one parser without touching the rest of the system.

Duplicate Detection: Transaction IDs Are Your Friend

The same transaction might appear multiple times. Your script might run twice, or banks might send duplicate notifications. Duplicate detection prevents your budget from showing the same expense twice.

The reliable approach: transaction IDs. When your parser extracts a transaction, include the bank’s unique identifier. When you send it to your budget API with this ID, the API can store it and recognize duplicates. Running the import multiple times produces the same result as running it once.

If your script crashes after importing but before marking emails as read, the next run fetches the same emails again. The API rejects them as duplicates based on transaction IDs. No manual cleanup required.

Without transaction IDs, you’d match on amount + date + merchant, which is fragile. Two coffee purchases on the same day would conflict. Transaction IDs eliminate this ambiguity.

Why Transaction IDs Matter: Without unique IDs, you’d need to match on amount + date + merchant. Two $5 coffee purchases on the same day would conflict. Transaction IDs eliminate this fragility, each transaction is uniquely identifiable regardless of timing or amount.

Process Automation: Running on a Schedule

The email parsing doesn’t need to run constantly. Banks send notifications within minutes of each transaction, but you don’t need instant import. Hourly or daily is usually fine.

The automation runs in batches: connect to email, fetch unread emails from bank senders, parse each one, import to budget API, mark as read, disconnect. The key principle is that the script should be stateless—each run is independent, relying only on what’s in your inbox and the budget API’s state. This means you can run it manually, change the schedule, or skip executions without breaking anything.

Batch limits matter for the first run. If you have 500 unread transaction emails, processing all of them might hit API rate limits or take too long. Setting a limit (max 100 emails per run) prevents this. The next run will process the next batch.

When This Approach Makes Sense

Email-based transaction import isn’t for everyone. Here’s when it works well:

Your bank doesn’t have API access - Regional European banks, smaller credit unions, and banks in developing markets often lack APIs. Third-party aggregators like Plaid don’t support them. Email parsing might be your only automation option.

You’re comfortable with light scripting - You’ll write or adapt a parser, set up scheduled tasks, and debug when something breaks. If you’re comfortable with regular expressions and running scripts, this is approachable. Otherwise, manual entry or bank CSV exports might be easier.

You have consistent email notifications - Some banks only send emails above certain amounts. If your bank emails every transaction, email parsing can be comprehensive. If emails are sporadic, you’ll miss transactions.

You want to avoid third-party bank access - Services like Plaid work through two primary methods: direct API connections with banks (now about 75% of connections), or for banks without APIs, credential-based screen scraping where you provide your bank login and Plaid accesses your account on your behalf. Email parsing reads data you already have. It’s a privacy-preserving alternative.

You manage multiple accounts - Email parsing can consolidate multiple banks. Each bank gets its own parser. All transactions flow into the same budget app. Better than logging into multiple bank websites to export CSVs.

When It Doesn’t Make Sense

Your bank has native integration - If your bank is supported by your budget app or by Plaid/Salt Edge, use that. Native integrations are more reliable, better maintained, and don’t break from email template changes.

You prefer manual control - Manual entry forces awareness. If that’s important to your budgeting, automation might reduce the mindfulness that makes it effective.

Email notifications are inconsistent - If your bank only sends notifications for certain transaction types or has unreliable delivery, email parsing will miss transactions.

Comparing Your Options

Here’s how the three main approaches stack up:

AspectEmail ParsingThird-Party API (Plaid)Manual Entry
Setup EffortMedium (parser development)Low (if bank supported)None
Bank CoverageAny bank with email notificationsLimited to supported banksUniversal
PrivacyHigh (no third-party access)Medium (third-party login)High
MaintenanceLow (15-30 min when templates change)NoneContinuous (daily task)
ReliabilityHigh (breaks loudly when it fails)HighHigh (manual oversight)
CostFree (DIY)Often subscription-basedTime cost
Best ForUnsupported banks, privacy-consciousMainstream banks, convenienceMindful budgeting

The Maintenance Reality

Banks change email templates occasionally—maybe once a year. When it happens, your parser breaks and transactions stop importing. You notice immediately.

Fixing it takes 15-30 minutes: examine the new format, update your parsing logic, test. The failure is obvious, not silent corruption. The parser works or it doesn’t.

This maintenance burden is the trade-off. Native API integrations don’t break from email template changes, but they require banks to maintain APIs. For banks without APIs, occasional parser updates are the price of automation.

Building This with AI Assistance

I built this entire integration using Claude Code, an AI coding assistant. It handled the TypeScript implementation, IMAP client setup, regex patterns for parsing, and Actual Budget API integration. The whole system took a few hours to get working, most of that time spent testing different email formats and debugging edge cases.

That said, this isn’t a no-code solution. You need programming basics to understand what the code is doing, modify parsers when banks change email formats, and debug when things break. If you’re comfortable reading TypeScript or JavaScript and can work with command-line tools, AI assistants can handle most of the heavy lifting. If programming fundamentals aren’t in your toolkit, this approach will be frustrating.

Practical Considerations for Implementation

If you’re considering building this, you’ll need: IMAP access to your email (with app-specific password or OAuth), sample transaction emails from your bank to develop parsing rules, API credentials for your budget app, and a plan for error handling. Test manually with a small batch before automating.

Closing Thoughts

I’ve been running this system since early 2024, processing thousands of transactions across multiple banks and currencies. Total maintenance time: maybe two hours spread across the year, mostly parser updates when banks changed email formats.

This approach is a workaround for missing bank APIs, but it’s a surprisingly robust one. It won’t replace native integrations where they exist. But for banks without APIs, for people who want privacy-preserving automation, for anyone who’s tired of manual transaction entry, email parsing is a viable path. The data is already there. The notifications are already structured. You’re just extracting what’s already being sent to you.

If you’ve built something similar, or if you’re stuck manually entering transactions because your bank lacks API support, I’d genuinely like to hear about it. What’s your approach? What broke? What surprised you? Reach out at [email protected].