adev.build
Adem replies within the hourStart a project

Utility bills collected and reconciled automatically

A production platform that collects utility bills from 13 providers through portal scraping, emailed PDFs and uploads. It extracts and reconciles the amounts, writes them into the finance team's monthly workbook, and generates owner earnings reports with an approval step.

Client
Short-term rental & property management company, 735+ owner-managed units
My role
Lead engineer. Architecture, scrapers, extraction, dashboard, deployment.
When
2026
How a bill gets from a portal to an owner report
  1. CollectProvider portals, emailed PDFs and uploads
  2. ExtractAccount, period, amount and usage read from each bill
  3. ReconcileMatched to the right unit; mismatches flagged
  4. ReportWritten back to finance sheets and owner reports
  • One plugin per utility provider
  • Ops dashboard for every run
  • Scheduled worker with retries

In numbers

units covered
735+
utility providers, one plugin each
13
lines of Python + TypeScript
~33k
dashboard · API gateway · worker
3-tier

The problem

Every month the finance team collected electricity, water and internet bills for more than 735 units. They came from over a dozen providers, each with its own portal, PDF format and quirks. Staff logged in to each portal, downloaded bills, read off the amounts, typed them into a large Excel workbook and then built owner statements by hand. It took days, mistakes slipped through, and nobody could tell which bills were still missing.

What I built

A provider plugin system. Each utility provider is a plugin with its own scraper and extractor, so adding a provider never touches the others. Bills arrive three ways:

  • browser automation of provider portals
  • an email inbox the system reads and classifies, including attachments inside nested zips
  • manual or SFTP uploads

Reliable scraping at scale.

  • Playwright runs through rotating residential proxies, with one sticky session per provider so portals don’t flag the logins.
  • A circuit breaker pauses a failing provider instead of hammering it.
  • Every task has a timeout, and an hourly cleanup job recovers runs that got stuck.

Extraction and reconciliation. The worker reads amounts from the bill PDFs and checks them against the amounts shown on the provider websites. Each bill is matched to the right unit by building and account. Anything that doesn’t reconcile is flagged for review instead of being booked silently.

Excel write-back. The finance team kept their existing monthly workbook. The system fills it in directly, so their process didn’t have to change.

Owner reports. Monthly owner earnings reports are generated and checked against a real historical report, then go through an approval step before they are sent.

An operations dashboard (Next.js). About 30 screens:

  • scrape runs and their status
  • bills and reconciliation
  • the email inbox
  • an Excel viewer
  • owner reports
  • proxy health
  • user management

Engineering decisions worth noting

  • Separate gateway and worker. The dashboard API stays fast and safe to expose. The heavy scraping and extraction runs on Celery workers that can be scaled or restarted without downtime.
  • Write into the workbook the team already uses. Adoption was immediate because the finance team kept working the way they already did.
  • Reconcile instead of trusting extraction. Extraction is never 100% accurate. Checking two sources against each other catches the errors that matter before they reach an owner.

What this proves for your project

If your team spends days a month copying numbers out of portals, PDFs or emails into spreadsheets, that work can be automated reliably, with an audit trail and checks a human approves. The same approach works for invoices, statements, purchase orders and any other recurring documents.

Built with

Python, FastAPI, Celery, Redis, PostgreSQL, Alembic, Playwright, pdfplumber, openpyxl, Next.js 16, React 19, TanStack Query, Tailwind 4, Docker Compose, Railway, Vercel.

More selected work

Want something like this?

You'll get a reply within 1 business hour, from me, with questions or a first plan and price.