Learn how I built a personal budget tracker with Python that helped me save money and understand my spending habits. Step-by-step tutorial with complete code.
🕒 9 min readHey there! 👋 Remember when we talked about what FinTech is and how developers can build financial tools? Well, today I’m sharing something really personal - the exact budget tracker I built that finally helped me understand where my money was going.
I used to be terrible with money. I’d get to the end of the month and wonder, “Where did it all go?” Sound familiar?
That’s why I decided to build my own solution. No complicated spreadsheets, no expensive apps - just clean Python code that does exactly what I need.
And the best part? It actually worked! I went from constantly worrying about money to having a clear financial picture. Let me show you how to build your own.
Before we dive into code, let me share why I think building your own is worth it:
Most budgeting apps have a million features you’ll never use. With your own tracker, you build only what matters to you.
You’ll learn about:
Your financial data stays on your computer. No third parties, no subscriptions, no ads.
We’ll create a web-based budget tracker that:
Here’s a peek at what you’ll have by the end:
# This is the kind of clean interface we'll build
Budget Tracker Dashboard:
- Monthly Income: $4,500
- Total Expenses: $3,200
- Money Saved: $1,300
- Top Spending Category: Dining Out ($420)
Step 1: Setting Up Our Project First, let’s create our project structure. I like to keep things organized:
budget_tracker/
├── app.py
├── templates/
│ └── index.html
├── static/
│ └── style.css
└── data/
└── transactions.json
Now, install the packages we’ll need:
pip install flask pandas matplotlib Flask for our web interface, Pandas for data handling, and Matplotlib for charts. Simple and powerful!
Step 2: Building Our Core Budget Class Let’s start with the brain of our application. I’ll show you the exact class I use:
import json
import pandas as pd
from datetime import datetime
import os
class BudgetTracker:
def __init__(self, data_file="data/transactions.json"):
self.data_file = data_file
self.transactions = self.load_data()
def load_data(self):
"""Load existing transactions or create new file"""
if os.path.exists(self.data_file):
with open(self.data_file, 'r') as f:
return json.load(f)
else:
# Create directory if it doesn't exist
os.makedirs(os.path.dirname(self.data_file), exist_ok=True)
return []
def save_data(self):
"""Save transactions to file"""
with open(self.data_file, 'w') as f:
json.dump(self.transactions, f, indent=2)
def add_transaction(self, amount, category, description, transaction_type="expense"):
"""Add a new transaction - income or expense"""
transaction = {
'id': len(self.transactions) + 1,
'amount': float(amount),
'category': category,
'description': description,
'type': transaction_type, # 'income' or 'expense'
'date': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
self.transactions.append(transaction)
self.save_data()
return transaction
def get_balance(self):
"""Calculate current balance"""
income = sum(t['amount'] for t in self.transactions if t['type'] == 'income')
expenses = sum(t['amount'] for t in self.transactions if t['type'] == 'expense')
return income - expenses
def get_spending_by_category(self):
"""See where your money is going"""
categories = {}
for transaction in self.transactions:
if transaction['type'] == 'expense':
category = transaction['category']
amount = transaction['amount']
categories[category] = categories.get(category, 0) + amount
return categories
def get_monthly_summary(self):
"""Get this month's financial summary"""
current_month = datetime.now().strftime("%Y-%m")
monthly_income = sum(
t['amount'] for t in self.transactions
if t['type'] == 'income' and t['date'].startswith(current_month)
)
monthly_expenses = sum(
t['amount'] for t in self.transactions
if t['type'] == 'expense' and t['date'].startswith(current_month)
)
return {
'income': monthly_income,
'expenses': monthly_expenses,
'savings': monthly_income - monthly_expenses
}
This class does all the heavy lifting! It handles saving, loading, calculations - everything our app needs.
Step 3: Creating Our Web Interface with Flask Now let’s make it pretty and accessible through a web browser:
from flask import Flask, render_template, request, jsonify
import matplotlib.pyplot as plt
import io
import base64
app = Flask(__name__)
budget = BudgetTracker()
@app.route('/')
def index():
"""Main dashboard page"""
balance = budget.get_balance()
monthly_summary = budget.get_monthly_summary()
spending_by_category = budget.get_spending_by_category()
# Create spending chart
chart_url = create_spending_chart(spending_by_category)
return render_template('index.html',
balance=balance,
monthly_summary=monthly_summary,
spending_by_category=spending_by_category,
chart_url=chart_url)
@app.route('/add_transaction', methods=['POST'])
def add_transaction():
"""Handle new transactions"""
try:
amount = request.form['amount']
category = request.form['category']
description = request.form['description']
transaction_type = request.form['type']
budget.add_transaction(amount, category, description, transaction_type)
return jsonify({'success': True, 'message': 'Transaction added!'})
except Exception as e:
return jsonify({'success': False, 'message': str(e)})
@app.route('/transactions')
def get_transactions():
"""Get all transactions"""
return jsonify(budget.transactions)
def create_spending_chart(spending_data):
"""Create a beautiful pie chart of spending"""
if not spending_data:
return None
# Create the plot
plt.figure(figsize=(8, 6))
categories = list(spending_data.keys())
amounts = list(spending_data.values())
plt.pie(amounts, labels=categories, autopct='%1.1f%%', startangle=90)
plt.axis('equal') # Equal aspect ratio ensures pie is circular
plt.title('Spending by Category')
# Convert plot to base64 for HTML embedding
img = io.BytesIO()
plt.savefig(img, format='png', bbox_inches='tight')
img.seek(0)
plot_url = base64.b64encode(img.getvalue()).decode()
plt.close()
return f"data:image/png;base64,{plot_url}"
if __name__ == '__main__':
app.run(debug=True)
Step 4: Making It Look Beautiful (HTML & CSS)
Create templates/index.html:
html
<!DOCTYPE html>
<html>
<head>
<title>My Personal Budget Tracker</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f5f5f5;
}
.dashboard {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-bottom: 30px;
}
.card {
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.balance-positive {
color: #10b981;
font-size: 2em;
font-weight: bold;
}
.balance-negative {
color: #ef4444;
font-size: 2em;
font-weight: bold;
}
.transaction-form {
background: white;
padding: 20px;
border-radius: 10px;
margin-bottom: 20px;
}
.form-group {
margin-bottom: 15px;
}
input, select, button {
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
width: 100%;
box-sizing: border-box;
}
button {
background: #3b82f6;
color: white;
border: none;
cursor: pointer;
}
button:hover {
background: #2563eb;
}
.transaction-list {
background: white;
padding: 20px;
border-radius: 10px;
}
.transaction-item {
padding: 10px;
border-bottom: 1px solid #eee;
display: flex;
justify-content: space-between;
}
.transaction-income {
color: #10b981;
}
.transaction-expense {
color: #ef4444;
}
</style>
</head>
<body>
<h1>💰 My Personal Budget Tracker</h1>
<div class="dashboard">
<div class="card">
<h2>Current Balance</h2>
<div class="{{ 'balance-positive' if balance >= 0 else 'balance-negative' }}">
${{ "%.2f"|format(balance) }}
</div>
</div>
<div class="card">
<h2>This Month</h2>
<p>Income: ${{ "%.2f"|format(monthly_summary.income) }}</p>
<p>Expenses: ${{ "%.2f"|format(monthly_summary.expenses) }}</p>
<p>Savings: ${{ "%.2f"|format(monthly_summary.savings) }}</p>
</div>
</div>
{% if chart_url %}
<div class="card">
<h2>Spending Breakdown</h2>
<img src="{{ chart_url }}" alt="Spending by Category">
</div>
{% endif %}
<div class="transaction-form">
<h2>Add New Transaction</h2>
<form id="transactionForm">
<div class="form-group">
<label>Type:</label>
<select name="type" required>
<option value="income">Income</option>
<option value="expense">Expense</option>
</select>
</div>
<div class="form-group">
<label>Amount:</label>
<input type="number" name="amount" step="0.01" required>
</div>
<div class="form-group">
<label>Category:</label>
<select name="category" required>
<option value="Salary">Salary</option>
<option value="Freelance">Freelance</option>
<option value="Food">Food & Dining</option>
<option value="Transport">Transportation</option>
<option value="Entertainment">Entertainment</option>
<option value="Shopping">Shopping</option>
<option value="Bills">Bills & Utilities</option>
<option value="Other">Other</option>
</select>
</div>
<div class="form-group">
<label>Description:</label>
<input type="text" name="description" required>
</div>
<button type="submit">Add Transaction</button>
</form>
</div>
<div class="transaction-list">
<h2>Recent Transactions</h2>
<div id="transactions">
<!-- Transactions will be loaded here by JavaScript -->
</div>
</div>
<script>
// Load transactions when page loads
loadTransactions();
// Handle form submission
document.getElementById('transactionForm').addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
fetch('/add_transaction', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('Transaction added successfully!');
this.reset();
location.reload(); // Refresh to show new data
} else {
alert('Error: ' + data.message);
}
});
});
function loadTransactions() {
fetch('/transactions')
.then(response => response.json())
.then(transactions => {
const container = document.getElementById('transactions');
container.innerHTML = '';
// Show latest transactions first
transactions.reverse().forEach(transaction => {
const div = document.createElement('div');
div.className = 'transaction-item';
div.innerHTML = `
<div>
<strong>${transaction.description}</strong>
<br>
<small>${transaction.category} • ${transaction.date}</small>
</div>
<div class="transaction-${transaction.type}">
${transaction.type === 'income' ? '+' : '-'}$${transaction.amount.toFixed(2)}
</div>
`;
container.appendChild(div);
});
});
}
</script>
</body>
</html>
Step 5: Running Your Budget Tracker Now for the exciting part - let’s see it in action!
python app.py Then open your browser to http://localhost:5000 and you’ll see your very own budget tracker!
How This Changed My Financial Life I’ve been using my custom budget tracker for 6 months now, and here’s what happened:
📈 I Saved 30% More Money By seeing exactly where my money was going, I cut unnecessary spending without feeling deprived.
🎯 Financial Goals Became Real I could actually track progress toward saving for a vacation, new laptop, or emergency fund.
😌 Less Money Stress No more guessing games about whether I could afford things. The numbers don’t lie!
Customizing Your Tracker The beauty of building your own is that you can customize it! Here are some features I added later:
Monthly Budget Limits
def set_category_budget(self, category, monthly_budget):
"""Set monthly spending limits for categories"""
self.budgets[category] = monthly_budget
def check_budget_alerts(self):
"""Check if any categories are over budget"""
alerts = []
spending = self.get_spending_by_category()
for category, budget in self.budgets.items():
if spending.get(category, 0) > budget:
alerts.append(f"Over budget in {category}!")
return alerts
Recurring Transactions
def add_recurring_transaction(self, amount, category, description, frequency):
"""Add bills that repeat monthly"""
self.recurring_transactions.append({
'amount': amount,
'category': category,
'description': description,
'frequency': frequency # 'monthly', 'weekly', etc.
})
Common Questions I Had When Building This ❓ “Is this secure?” Since it runs locally on your computer, your data never leaves your machine. Much more secure than cloud-based apps!
❓ “What if I’m not good at Python?” That’s the point! Building this will make you better. Start simple and add features as you learn.
❓ “Can I access it from my phone?” You can! Run the app on a home server or use services like PythonAnywhere to host it online.
What’s Next in Our FinTech Series? In our next FinTech post, we’ll explore “Web Scraping Financial Data Ethically” where we’ll:
Learn to gather financial data from public sources
Build a stock market monitor
Create personalized financial dashboards
Understand the legal and ethical considerations
Your Financial Mission This week, try one of these:
Build the basic budget tracker and add your first transactions
Customize one feature to match your spending habits
Share in the comments: What financial insight surprised you when you started tracking?
Remember, the goal isn’t perfection - it’s awareness. Every transaction you track is a step toward financial control.
Wrapping Up Building this budget tracker was one of the most satisfying projects I’ve ever done. It combined my passion for programming with solving a real-life problem that was stressing me out.
The code we built today is just the beginning. You can add features like:
Email reports
Investment tracking
Goal progress tracking
Mobile app version
Data export to CSV
What financial feature would be most helpful for you right now?
Stuck on any part of the tutorial? Have ideas for improving the budget tracker? Share your experience in the comments - let’s build better financial tools together!
Comments & Discussion
Join the conversation using your GitHub account. Comments are powered by Utterances.