Automation
Utility Tracker
A private tool that watches my inbox for utility bills, reads each provider's format, and charts household cost and usage on one dashboard.
- Year
- 2025–present
- Role
- Solo Developer
- Status
- Private tool

At a glance
- 7Providers
- 4Formats
- MonthlyCollection
Overview
Tracking seven utility bills by hand meant seven websites and a spreadsheet every month. Utility Tracker connects to Gmail, recognizes each bill by sender, parses PDFs and JSON or CSV exports, and stores everything in one PostgreSQL schema. A dashboard shows monthly totals, year-to-date trends, the solar net-metering balance, and tiered water use, and a monthly job keeps it current without manual steps.
Result
No monthly data entry
Bills arrive, get parsed, and land on the dashboard without anyone copying numbers into a spreadsheet.
One household view
Net-metering balance, tiered water use, and year-to-date spending appear on one dashboard.
Format-agnostic parsing
The same dataset whether a bill arrives as a PDF attachment, a JSON export, or a notification email.
Private by default
Self-hosted and single-tenant. No third party gets a copy of household financial data.
Provider coverage
Each provider sends bills differently; the tracker brings them into one schema:
- ElectricBill emails and PDF invoices, including the solar net-metering balance and time-of-use data
- GasPayment confirmation emails plus JSON exports for usage and amount
- WaterPayment confirmations and CSV imports with tiered usage
- InternetNew-charge notification emails
- TrashQuarterly PDF invoice attachments
- Solar productionMonthly kWh exports from the installer's portal
- HOA duesA fixed monthly amount
Pipeline and dashboard
A small Next.js app handles ingestion, normalization, and charts:
- One-time Gmail authorizationThe refresh token is stored in PostgreSQL for ongoing access
- PDF extractionpdf-parse pulls line items from invoice attachments
- Provider-specific parsingEach provider has its own sender match and parser, with errors recorded for follow-up
- Monthly jobCollects the last 30 days of bills automatically; a backfill endpoint can load 12 months on demand
- DashboardMonthly totals, year-to-date trends, the net-metering balance, and tiered water use in Recharts
Technical architecture
A deliberately small stack, because the parsers are the point:
- Frontend
- Next.js 16 App Router with Tailwind
- Database
- PostgreSQL with Prisma 7 (four models: provider, bill, payment, OAuth token)
- Email ingestion
- Gmail API via googleapis with refresh-token rotation
- PDF parsing
- pdf-parse for invoice extraction
- Charts
- Recharts for monthly, year-to-date, and tier views
- Hosting
- Self-hosted on Hetzner via Coolify with a monthly job
Lessons
- Gmail OAuth done rightConsent flow, refresh-token storage in the database, and silent rotation. The refresh token is the long-lived credential, so it lives in Postgres rather than an environment variable.
- Parsing is the productEvery provider has quirks: one encodes the net-metering balance as a separate line, another rolls water use up by tier, a third uses its own timestamp format. The value is in the parsers, not the framework.
- Lean by designNo multi-tenancy, auth library, or UI kit. The app serves one household, and every dependency I did not add is one I will not maintain.
- Cron-driven data productsA scheduled call to a secret-protected endpoint is a simple pattern for periodic pipelines: no queues, workers, or orchestrator.
More work
Let's talk.
Still moving data between inboxes and spreadsheets? I build focused automations that collect, normalize, and surface the information a team needs without adding another manual process.


