Iurii RoguliaIurii Rogulia
AboutServicesPricingProjectsStackReviewsPhrasesBlog
Contact
Iurii ships.

Iurii Rogulia, IT partner for business & fractional CTO. Professionally building software since 2001.

Think of a number
PricingQuality checklistPrivacy PolicyCookie Policy

Business

TMI Iurii Rogulia
VAT ID: FI29845875
DUNS: 368664211
Lappeenranta, Finland 🇫🇮

[email protected]
Back to projects

Iurii Monitors: Site Pulse — Open-Source Website & Domain Health Monitor

December 11, 2024

Open-source Python tool that discovers URLs from sitemap.xml, validates each in desktop and mobile modes with parallel checks, monitors domain registrations via WHOIS, and delivers instant Telegram alerts.

Source code

Stack

Python

Libraries

requestspython-whoispytzpython-dotenv

Services

Telegram

Topics

MonitoringAutomation

Key Results

  • Catches outages and expiring domains before customers or renewals do
  • Self-hostable and free — MIT-licensed, no monitoring SaaS subscription
  • Warns 90 days before a domain expires, so no site goes dark on a missed renewal
  • Checks every page in the sitemap, on both desktop and mobile, unattended via cron
  • Sends an instant Telegram alert the moment something breaks
Site Pulse — Open-Source Website & Domain Health Monitor

The Problem

Any website with a non-trivial number of pages faces the same monitoring gap: you can ping a homepage, but you can't manually check every URL that sitemap.xml knows about — especially across both desktop and mobile. Add domain registrations across multiple TLDs to the mix and the surface area grows fast. Missed renewals, silent nameserver changes, and pages that return 200 with an empty body are all failure modes that standard uptime monitors miss entirely — and the paid ones that do catch them charge a monthly subscription per site.

The Solution

Site Pulse is an open-source tool I built to close that gap: it watches your whole site and your domains, runs unattended on a cron schedule, and pings your Telegram the second something looks wrong. It is MIT-licensed and self-hostable, so there's no per-site subscription and nothing leaves your own server.

It has three jobs. First, it reads your sitemap.xml directly, so monitoring always reflects the site as it actually is today — add a page, and it's covered automatically. Second, it checks every one of those pages in both desktop and mobile modes, retrying on failure and flagging pages that return "OK" but come back suspiciously empty — the kind of silent breakage a basic uptime ping sails right past. Third, it watches your domain registrations across any number of extensions, warning you a full 90 days before one expires and flagging if the nameservers ever quietly change.

When it finds a problem — a down page, a slow response, an expiring domain — it sends an instant Telegram alert, with per-type controls so you only hear about what you care about. A companion log analyser then groups the results and surfaces your slowest, least stable, and heaviest pages.

Results

MetricValue
Codebase~757 lines Python
Parallel workers10 (ThreadPoolExecutor)
Device modesDesktop + Mobile per URL
Domain monitoringAny number of TLDs via WHOIS
Expiry warning90 days before expiration
SchedulingCron-based, fully automated

The system runs unattended via cron, dynamically adapting to sitemap changes and delivering Telegram alerts within seconds of detecting an issue — whether it's a down page, a slow response, or an expiring domain.

Under the Hood

For the technically curious, here is how the core pieces are built.

Concurrent Page Health Checks

Parallel URL validation using ThreadPoolExecutor with session reuse and conditional Telegram notifications:

# website_monitor.py
def check_pages() -> None:
    """Check all websites from the list"""
    timestamp = datetime.now().strftime("%d.%m.%Y %H:%M:%S")
    messages_to_send = []
 
    with requests.Session() as session:
        def check_with_session(url):
            return check_single_page(url, session)
 
        with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
            future_to_url = {
                executor.submit(check_with_session, url): url
                for url in URLS_TO_CHECK
            }
 
            for future in concurrent.futures.as_completed(future_to_url):
                message, is_success, is_warning, is_error = future.result()
 
                if (is_success and NOTIFY_SUCCESS) or \
                   (is_warning and NOTIFY_WARNING) or \
                   (is_error and NOTIFY_ERROR):
                    messages_to_send.append(message)
 
    if messages_to_send:
        message = f"📅 {timestamp}\n\n" + "\n\n".join(messages_to_send)
        send_telegram_message(message)

Result: 10 parallel workers with session reuse, configurable notification granularity, automatic message aggregation.

Desktop & Mobile Validation with Retry

Each URL is checked in both device modes with automatic retry on failure and response size validation:

# website_monitor.py
def check_single_page(url: str, session: requests.Session) -> tuple:
    def try_request(headers, device_type):
        start_time = time.time()
        response = session.get(
            url,
            timeout=(CONNECT_TIMEOUT, READ_TIMEOUT),
            headers=headers
        )
        content_length = len(response.content)
        elapsed_time = time.time() - start_time
 
        if response.status_code == 200 and content_length >= MIN_RESPONSE_SIZE:
            return response, content_length, elapsed_time, None
        else:
            if response.status_code != 200:
                raise Exception(f"Response code: {response.status_code}")
            else:
                raise Exception(f"Response size too small: {content_length} bytes")
 
    # Desktop check with retry
    try:
        try:
            response, content_length, elapsed_time, _ = try_request(
                DESKTOP_HEADERS, "Desktop"
            )
        except Exception:
            time.sleep(5)
            response, content_length, elapsed_time, _ = try_request(
                DESKTOP_HEADERS, "Desktop"
            )

Result: Catches both HTTP errors and suspiciously small responses (e.g. empty pages returning 200), with a 5-second retry delay.

Domain Expiration & Nameserver Monitoring

WHOIS-based domain check that validates expiration dates and verifies nameservers match expected configuration:

# website_monitor.py
def check_domain(domain: str) -> tuple[str, bool]:
    """Check domain registration expiry and nameservers"""
    w = whois.whois(domain)
 
    if isinstance(w.expiration_date, list):
        expiry_date = min(w.expiration_date)
    else:
        expiry_date = w.expiration_date
 
    days_until_expiry = (expiry_date - datetime.now()).days
 
    domain_ns = [ns.lower() for ns in w.name_servers]
    our_ns = [ns.lower() for ns in OUR_NAMESERVERS]
    using_our_ns = any(ns in domain_ns for ns in our_ns)
 
    messages = []
    has_warning = False
 
    if days_until_expiry <= EXPIRY_WARNING_DAYS:
        messages.append(f"⚠️ Domain will expire in {days_until_expiry} days")
        has_warning = True
    if not using_our_ns:
        messages.append(f"⚠️ Domain is using external nameservers")
        has_warning = True

Result: Handles WHOIS quirks (some registrars return lists instead of single dates), monitors both expiration and DNS configuration drift.

Iurii RoguliaAvailable

Need something similar?

I build custom solutions — from APIs to full products. Let's talk about your project.

View all projects

Related projects

Pikkuna — AI-Powered Localization Pipeline
Pikkuna — AI-Powered Localization Pipeline
July 30, 2026
Pikkuna — AI-Powered Localization Pipeline

A production localization pipeline built entirely on the OpenAI API: one English source of truth, SEO-aware translation prompts, cross-model verification with

Stack

Python

Libraries

pytestOpenAI SDKpython-dotenvpycountry

Services

OpenAIAnthropic

Topics

AILLMPrompt Engineeringi18nLocalizationSEOAutomationTestingArchitectureCTO
Perpetual Futures Grid Trading System
Perpetual Futures Grid Trading System
October 1, 2025
Perpetual Futures Grid Trading System

Production algorithmic trading system for perpetual futures — multi-account, event-driven architecture with dynamic progressive grid and 10-level risk

Stack

Python

Libraries

pybitpandaspytestPyYAMLpython-dotenv

Services

BybitTelegram

Topics

Algorithmic TradingGrid TradingFuturesRisk ManagementCrypto

Related posts

Bybit Grid Trading Bot in Python: Architecture and Risk
April 19, 2026· 14 min
Bybit Grid Trading Bot in Python: Architecture and Risk

Bybit grid trading bot in Python: event-driven architecture, ATR-based grid steps, 10-level risk management, atomic state persistence, and pytest test suite

Stack

Python

Libraries

pybitpytestpandasPyYAMLpytzrequestspython-dotenv

Services

BybitTelegram

Topics

Algorithmic TradingGrid TradingFuturesRisk Management
Publishing One Package to Five Registries with GitHub Actions
March 27, 2026· 10 min
Publishing One Package to Five Registries with GitHub Actions

How to publish one dataset to npm, PyPI, Go Module, RubyGems, and Packagist automatically with GitHub Actions — architecture, versioning, and per-ecosystem

Stack

TypeScriptPythonPHPGoRuby

Services

GitHub ActionsnpmPyPIPackagistRubyGems

Topics

Open SourceTax/VATAutomation