“5 Python Libraries That Power My Self-Hosted Billing Monitor”

5 Python Libraries That Power My Self-Hosted Billing Monitor

When I started building BillingWatch, I had one goal: catch billing anomalies before they become customer support nightmares. A refund tsunami at 2 AM. A webhook that quietly stops…


This content originally appeared on DEV Community and was authored by Ray

5 Python Libraries That Power My Self-Hosted Billing Monitor

When I started building BillingWatch, I had one goal: catch billing anomalies before they become customer support nightmares. A refund tsunami at 2 AM. A webhook that quietly stops firing. Subscriptions flipping to "canceled" for no obvious reason.

The commercial tools — Baremetrics, ChartMogul, Datadog — can do this, but they run $50–$400/month and you're trusting a third party with your entire billing data. I wanted something self-hosted, auditable, and free to run.

Here are the 5 Python libraries that made BillingWatch work.

1. stripe — The Foundation

pip install stripe

Stripe's official Python SDK is the backbone of the whole system. BillingWatch uses it for two things: pulling historical event data for backfill and verifying webhook signatures so we know events are actually from Stripe.

The signature verification piece is critical:

import stripe

def handle_webhook(payload: bytes, sig_header: str) -> dict:
    try:
        event = stripe.Webhook.construct_event(
            payload, sig_header, settings.STRIPE_WEBHOOK_SECRET
        )
        return event
    except stripe.error.SignatureVerificationError:
        raise ValueError("Invalid webhook signature")

Without this check, any bad actor could POST fake billing events to your endpoint. The construct_event call verifies the HMAC signature Stripe includes in every webhook request.

The SDK also handles pagination cleanly when backfilling historical charges:

events = stripe.Event.list(type="charge.failed", limit=100)
for event in stripe.auto_paging_iter(events):
    process_event(event)

2. fastapi — The API Layer

pip install fastapi uvicorn

FastAPI handles the webhook receiver, the REST API for the dashboard, and the admin endpoints. I chose it over Flask for three reasons: async support (webhooks can come in fast), automatic OpenAPI docs, and Pydantic validation that makes the event parsing bulletproof.

The webhook receiver is a single async endpoint:

from fastapi import FastAPI, Request, HTTPException
import stripe

app = FastAPI()

@app.post("/webhooks/stripe")
async def stripe_webhook(request: Request):
    payload = await request.body()
    sig_header = request.headers.get("stripe-signature")

    try:
        event = stripe.Webhook.construct_event(
            payload, sig_header, WEBHOOK_SECRET
        )
    except ValueError:
        raise HTTPException(status_code=400, detail="Invalid payload")

    await process_billing_event(event)
    return {"status": "ok"}

The async design matters because Stripe has a 30-second timeout on webhook delivery. If your handler is slow, Stripe retries — and then you're processing duplicates. FastAPI's async model keeps things fast enough that this hasn't been a problem.

3. sqlalchemy + alembic — Persistent Anomaly Storage

pip install sqlalchemy alembic

Every billing event gets persisted. SQLAlchemy handles the ORM layer, Alembic handles schema migrations. The core model is simple:

from sqlalchemy import Column, String, Float, DateTime, JSON
from database import Base

class BillingEvent(Base):
    __tablename__ = "billing_events"

    id = Column(String, primary_key=True)
    event_type = Column(String, index=True)
    amount = Column(Float)
    currency = Column(String)
    customer_id = Column(String, index=True)
    created_at = Column(DateTime)
    raw_data = Column(JSON)
    anomaly_score = Column(Float, default=0.0)
    flagged = Column(Boolean, default=False)

The anomaly_score field is populated by the detection logic — things like "this customer just triggered 5 failed charges in 10 minutes" or "refund amount exceeds original charge." The flagged column drives the dashboard alerts.

Alembic migrations mean schema changes don't break production deployments:

alembic revision --autogenerate -m "add anomaly score column"
alembic upgrade head

4. apscheduler — The Heartbeat

pip install apscheduler

BillingWatch doesn't just react to webhooks — it also runs periodic digest checks. Every hour, it scans for patterns that wouldn't be obvious from individual events: rolling refund rates, sudden drops in new subscriptions, failed charge clusters.

APScheduler makes this dead simple:

from apscheduler.schedulers.asyncio import AsyncIOScheduler

scheduler = AsyncIOScheduler()

@scheduler.scheduled_job("interval", hours=1)
async def hourly_digest():
    anomalies = await check_rolling_metrics()
    if anomalies:
        await send_alert_digest(anomalies)

scheduler.start()

The async scheduler runs inside the same FastAPI process — no Celery, no Redis, no separate worker. For a self-hosted tool running on a $5 VPS, that simplicity matters a lot.

5. httpx — Outbound Alerts

pip install httpx

When something trips an anomaly threshold, BillingWatch needs to tell you. I went with httpx over requests because it's async-native, which means alert delivery doesn't block the webhook handler.

The alert dispatcher is a small utility:

import httpx

async def send_webhook_alert(webhook_url: str, payload: dict):
    async with httpx.AsyncClient(timeout=10.0) as client:
        try:
            resp = await client.post(webhook_url, json=payload)
            resp.raise_for_status()
        except httpx.HTTPError as e:
            logger.error(f"Alert delivery failed: {e}")

This powers Slack notifications, Discord alerts, or any custom webhook endpoint. BillingWatch doesn't care where the alert goes — it just needs a URL.

Putting It Together

The full stack looks like this:

Stripe → Webhook → FastAPI receiver → SQLAlchemy storage
                                    → APScheduler digest checks
                                    → httpx alert dispatch

No containers required for local dev. Just:

git clone https://github.com/rmbell09-lang/billingwatch
cd billingwatch
pip install -r requirements.txt
cp .env.example .env  # add your STRIPE_WEBHOOK_SECRET
uvicorn main:app --reload

Why Self-Host?

The honest reason: I don't want Baremetrics or Datadog seeing every charge, refund, and subscription churn event from my business. That's the full picture of revenue health — and it belongs on infrastructure I control.

BillingWatch runs on a $6/month VPS. The only external dependency is Stripe itself.

If you're running a Stripe-based product and haven't set up anomaly monitoring, it's worth 30 minutes. The next billing anomaly you miss will cost more than that.

GitHub: https://github.com/rmbell09-lang/billingwatch


This content originally appeared on DEV Community and was authored by Ray


Print Share Comment Cite Upload Translate Updates
APA

Ray | Sciencx (2026-03-30T15:30:26+00:00) “5 Python Libraries That Power My Self-Hosted Billing Monitor”. Retrieved from https://www.scien.cx/2026/03/30/5-python-libraries-that-power-my-self-hosted-billing-monitor/

MLA
" » “5 Python Libraries That Power My Self-Hosted Billing Monitor”." Ray | Sciencx - Monday March 30, 2026, https://www.scien.cx/2026/03/30/5-python-libraries-that-power-my-self-hosted-billing-monitor/
HARVARD
Ray | Sciencx Monday March 30, 2026 » “5 Python Libraries That Power My Self-Hosted Billing Monitor”., viewed ,<https://www.scien.cx/2026/03/30/5-python-libraries-that-power-my-self-hosted-billing-monitor/>
VANCOUVER
Ray | Sciencx - » “5 Python Libraries That Power My Self-Hosted Billing Monitor”. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2026/03/30/5-python-libraries-that-power-my-self-hosted-billing-monitor/
CHICAGO
" » “5 Python Libraries That Power My Self-Hosted Billing Monitor”." Ray | Sciencx - Accessed . https://www.scien.cx/2026/03/30/5-python-libraries-that-power-my-self-hosted-billing-monitor/
IEEE
" » “5 Python Libraries That Power My Self-Hosted Billing Monitor”." Ray | Sciencx [Online]. Available: https://www.scien.cx/2026/03/30/5-python-libraries-that-power-my-self-hosted-billing-monitor/. [Accessed: ]
rf:citation
» “5 Python Libraries That Power My Self-Hosted Billing Monitor” | Ray | Sciencx | https://www.scien.cx/2026/03/30/5-python-libraries-that-power-my-self-hosted-billing-monitor/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.