Synthetic Monitoring Without a Vendor: Catching Production Failures with Scheduled Scrapers

Stop paying per-check for flows only your team understands. Learn how to build, schedule, and maintain production health scrapers that catch JavaScript failures, auth regressions, and checkout breakage before users do.

Trawl banner: synthetic-monitoring grainy gradient with filet mesh

Your Monitoring Vendor Does Not Know Your App

Every managed monitoring platform promises the same thing: set up a check, get an alert, never miss an outage. What they sell you is a generic HTTP ping or a simplified browser script running from shared infrastructure. What they cannot give you is the intimate knowledge of your own product flows. Only you know which endpoints carry real revenue, which page states mean "broken," and which timeouts are noise versus signal.

The best synthetic monitor is one that knows your app as well as you do. That means you wrote it.

What You'll Learn


What Synthetic Monitoring Is and Why It Matters

Synthetic monitoring runs scripted interactions against your production systems on a schedule, independent of real user traffic. Unlike error logs or APM traces that react to what users hit, synthetic checks run proactively: they visit pages, click buttons, submit forms, and verify expected states before anyone reports a problem.

The business case is straightforward. According to EMA Research data analyzed by BigPanda (2024), unplanned IT downtime now averages $14,056 per minute across all organization sizes. For SaaS products, that number compounds with churn: every minute a checkout flow is broken is a minute you cannot recover. Synthetic checks that catch failures in two minutes instead of twenty are the cheapest insurance you can buy.

Passive vs. synthetic monitoring

Passive monitoring (logs, traces, real-user monitoring) only fires when users hit a problem. Synthetic monitoring fires on a cron, regardless of traffic. That distinction matters most at 3 a.m. on a Tuesday when your traffic is at 2% of peak but a broken deployment is sitting in production, waiting to greet the morning rush.

The synthetic monitoring market is growing at a 15.30% CAGR through 2030 (Mordor Intelligence). Teams are waking up to the gap between "we have APM" and "we know our app is working right now."

The Case for Rolling Your Own

Managed synthetic monitoring platforms price on check frequency, number of locations, and browser steps. A single browser-based transaction check running every five minutes from three regions can consume thousands of test runs per month. That pricing model is fine for a generic login flow check. It becomes expensive when you want fine-grained coverage: fifty key pages, three purchase paths, two checkout variants, and a handful of API sequences.

There is a second, less-discussed reason: expressiveness. Managed platforms abstract browser automation into point-and-click recorders or simplified scripting environments. When your checkout flow involves a multi-step modal, a PostHog feature flag, and a payment iframe, that abstraction breaks down. A real Puppeteer or Playwright script, running on infrastructure you control, can handle all of it.

What you give up

Rolling your own is not free. You trade the vendor's managed runner infrastructure, multi-region nodes, and dashboard UI for full control over the script and significantly lower per-check cost. The honest tradeoff: if your primary need is global latency monitoring from fifty geographic locations, a managed platform is hard to beat. If your need is "does this specific feature work in production right now," a scheduled scraper wins on cost and precision.

Anatomy of a Production Health Scraper

A production health scraper is a headless browser script that exercises a real user path and asserts on the result. It is not an end-to-end test. It does not mock anything. It hits your real production URL, uses real infrastructure, and treats observed state as ground truth.

Core components

  • Navigation target — the production URL or API endpoint to check
  • Interaction sequence — page loads, clicks, form fills, or API calls that constitute the monitored flow
  • Assertion — the expected DOM state, HTTP status code, response shape, or rendered text that confirms "healthy"
  • Alert payload — a structured output (pass/fail + observed value + timestamp) routed to your alerting system
  • Schedule — the cron expression that determines check frequency

A minimal example

const puppeteer = require('puppeteer');

async function checkHomepage() {
  const browser = await puppeteer.launch({ headless: true });
  const page = await browser.newPage();
  await page.goto('https://your-app.com', { waitUntil: 'networkidle2', timeout: 15000 });

  const heading = await page.$eval('h1', el => el.textContent.trim());
  const passed = heading.includes('Expected Heading Text');

  await browser.close();
  return {
    check: 'homepage_heading',
    passed,
    observed: heading,
    ts: new Date().toISOString()
  };
}

checkHomepage().then(result => {
  if (!result.passed) {
    // send to alerting channel
    console.error('FAIL', JSON.stringify(result));
    process.exit(1);
  }
  console.log('PASS', JSON.stringify(result));
});

This script is a complete unit. It loads a page, asserts on visible state, and exits non-zero on failure. A scheduler watching the exit code knows when to fire an alert.

Four Failure Patterns Scrapers Catch That Pings Miss

HTTP ping checks confirm one thing: a server responded with 2xx. They miss every failure that happens inside the application layer. Here are four categories that only a rendered-page scraper catches reliably.

1. Partial renders and JavaScript failures

A page can return 200 while half the React tree fails to mount. An HTTP ping gives you a green light. A headless browser that asserts on a specific DOM element gives you the real answer. This class of failure is especially common after a bad deploy of a single-page application or after a third-party script times out on load.

2. Authentication and session regressions

Your login endpoint returns 200. But a cookie misconfiguration, a JWT signing key rotation, or a session store eviction silently rejects every authenticated request downstream. A scraper that logs in and then navigates to a gated page catches this. A ping on the login endpoint does not.

3. Form and checkout flow breakage

Add-to-cart, checkout initiation, and payment confirmation are the highest-value flows in any commerce product. A deploy that changes a form field name, breaks a CSRF token rotation, or alters a redirect chain can silently kill checkout while the server continues returning 200s. A scraper that walks the full purchase path is the only automated check that catches this class of regression before users do.

4. Third-party degradation

You cannot ping your way to knowing whether your embedded chat widget, analytics snippet, or payment iframe is actually loading. A headless browser that waits for widget initialization and checks for visible render state tells you what the user actually sees. When a third-party CDN degrades, your server stays healthy but your users experience a broken page.

Want to run production health scrapers on a cron without managing infrastructure? Try Trawl free and have your first scheduled check running in under two minutes.

Building Your First Scheduled Scraper Monitor

The architecture has three pieces: the script, the scheduler, and the alert sink. You can assemble this from open-source components in an afternoon.

Step 1: Define your critical paths

List the three to five flows in your product that, if broken, would cause user-visible failure within minutes. Homepage load, login, primary feature action, checkout, and outbound API are a good starting list. Do not try to cover everything at first. Start narrow, high-signal.

Step 2: Write assertions against production state

Each script should assert on observed state, not just on HTTP status. Prefer visible DOM text or specific element existence over CSS class checks. Classes change in refactors. User-visible text is more stable and more meaningful as a health signal.

// Prefer this: checks user-visible state
const cartCount = await page.$eval('[data-testid="cart-count"]', el => el.textContent);
if (parseInt(cartCount) !== 1) throw new Error(`Expected 1 item in cart, got: ${cartCount}`);

// Not this: checks HTTP status only
const res = await page.goto('/cart');
if (res.status() !== 200) throw new Error('cart 404');

Step 3: Schedule at meaningful intervals

Run high-priority checks (checkout, login) every five minutes. Run content and feature checks every thirty minutes. There is no value in sub-minute polling for browser-based checks since each check takes ten to thirty seconds to complete. Align check frequency with your acceptable time-to-detect: if a broken checkout for fifteen minutes is acceptable, a five-minute interval gives you two confirmation cycles before alerting.

Step 4: Route failures to the right sink

A dead webhook is a useless alert. Route production failures to the channel where your on-call rotation actually looks: PagerDuty, Slack, or a push notification service. Avoid email for P1 paths. The alert payload should include the check name, the observed value, the expected value, and a direct link to the production URL to accelerate diagnosis.

Step 5: Treat script maintenance as a feature

Production health scripts drift with your product. When you ship a new checkout step, update the scraper. When you rename a heading, update the assertion. Tie script updates to your deploy process: any PR that changes a monitored flow should include a corresponding script update. This is the discipline that separates a thriving synthetic monitoring setup from a pile of stale scripts that never fire.

Alerting, Routing, and Runbooks

An alert is only as useful as the action it produces. Production health scrapers fire frequently enough that false positives will erode trust unless you build in confirmation and routing logic from the start.

Confirmation windows

Before paging anyone, run the failing check twice with a one-minute delay. A transient network hiccup, a brief CDN cold start, or a deployment rollout in progress will resolve in the retry window. Page on two consecutive failures, not one.

Runbook-linked alerts

Every alert should include a runbook URL or an inline diagnostic checklist. "Checkout flow failed: observed empty cart after add-to-cart click" is useful. "ALERT: check_checkout" is not. The time you invest in well-formed alert messages pays off in faster recovery and fewer escalation chains.

Structured output for observability pipelines

If you run more than ten checks, pipe results into an observability system rather than raw webhook calls. Emit structured JSON (check name, pass/fail, duration, observed value, environment) and ingest into your existing logs or metrics pipeline. This gives you trending, historical baselines, and anomaly detection on top of point-in-time alerts.

For teams using MCP-enabled AI agents for observability, consider the cross-stack potential: grounding Claude with live web data via MCP allows an AI agent to query check results and synthesize diagnostics alongside production health data.

When to Keep Your Vendor Tool

Rolling your own is not the answer for every team. Managed synthetic monitoring platforms make sense when you need multi-region latency monitoring from globally distributed nodes, when your team lacks bandwidth to maintain scripts, or when your checks are simple enough that the platform's UI is genuinely faster than writing Puppeteer code.

The two approaches are also compatible. A managed platform can handle global ping checks and basic HTTP health while your custom scrapers handle the application-layer flows that require real browser execution and business logic. Use the right tool for each job rather than betting everything on one approach.

If you want to understand competitor monitoring practices, job posting data for competitor intelligence offers a complementary signal: teams hiring for SRE roles with synthetic monitoring keywords often indicate where competitors are investing in observability.

Tools and Resources

These are the components of a self-hosted synthetic monitoring stack:

  • Trawl — scheduled scraper orchestration; run headless browser checks on a cron without managing runner infrastructure. Write your Puppeteer script, configure the schedule, and Trawl handles execution, retries, and result delivery.
  • Puppeteer — headless Chrome automation library from Google; the industry-standard scripting layer for browser-based checks.
  • Playwright — Microsoft's cross-browser automation framework; preferred for teams that need Firefox or WebKit coverage alongside Chromium.
  • node-cron / cron (npm) — lightweight Node.js cron scheduler for self-hosted execution without external dependencies.
  • Ntfy / PagerDuty / Slack Webhooks — alert sinks; choose based on your on-call rotation and escalation needs.

Key Takeaways

  1. Synthetic monitoring runs scripted production checks on a schedule, independent of real user traffic. It catches failures before users do.
  2. HTTP pings return 200 even when your app is broken at the application layer. Browser-based checks catch JavaScript errors, auth regressions, checkout failures, and widget degradation that pings miss entirely.
  3. Rolling your own synthetic monitor is cheaper and more expressive than managed platforms when your flows require real browser execution and business logic.
  4. Start with three to five critical paths: homepage render, login, and your primary revenue flow. Add coverage incrementally.
  5. Confirm failures with a retry before alerting. Two consecutive failures are a signal; one is often noise.
  6. Treat scraper maintenance as a feature: update scripts when monitored flows change, tied to your deploy process.
  7. Self-hosted and managed synthetic monitoring are compatible. Use managed platforms for global latency; use custom scrapers for application-layer correctness.

If you want to stop paying per-check for flows only you understand, Trawl gives you the scheduling layer without the browser infrastructure overhead.

FAQ

What is synthetic monitoring in simple terms?

Synthetic monitoring runs automated scripts against your production systems on a fixed schedule to verify they are working correctly. Unlike passive monitoring that only detects issues when real users encounter them, synthetic checks run whether or not anyone is using your app, catching failures proactively before they affect users.

How is synthetic monitoring different from uptime monitoring?

Uptime monitoring confirms a server responds with a 2xx HTTP status code. Synthetic monitoring verifies the actual application behavior: that a page renders correctly, a login flow works, or a checkout completes successfully. A server can be "up" (returning 200) while the application is broken. Synthetic monitoring catches those cases; uptime monitoring does not.

What scripting tools work best for synthetic monitoring?

Puppeteer and Playwright are the two industry standards. Puppeteer controls Chromium via the Chrome DevTools Protocol and is well-suited for most browser-based checks. Playwright supports Chromium, Firefox, and WebKit, making it preferable when cross-browser coverage matters. Both produce real browser behavior that reflects what users actually experience.

How often should I run synthetic checks?

Run high-priority checks (login, checkout, primary revenue flows) every five minutes. Run secondary checks (feature pages, content blocks) every fifteen to thirty minutes. Sub-minute intervals rarely make sense for browser-based checks since each execution takes ten to thirty seconds and the marginal detection gain is minimal at that cadence.

Can I use synthetic monitoring for authentication-gated pages?

Yes. Your script can perform a full login sequence, persist the resulting session state, and then navigate to gated pages as part of the check. This is one of the key advantages of custom scrapers over managed platforms: you control the authentication flow completely and can handle multi-step auth, MFA flows, and session management without restriction.

What should my synthetic checks assert on?

Assert on user-visible state rather than implementation details. Prefer checking for specific visible text, the presence of a named element (using data-testid attributes when available), or an expected API response shape. Avoid asserting on CSS classes or element counts that change in routine refactors but do not affect user experience.

How do I avoid alert fatigue from synthetic monitoring?

Use a confirmation window: only alert after two consecutive failures separated by at least one minute. This eliminates false positives from transient network issues and brief deployment rollouts. Ensure every alert includes enough diagnostic context (check name, observed value, expected value, production URL) that the on-call engineer can start debugging immediately without additional investigation.

Do I still need a managed monitoring platform if I roll my own?

The two approaches serve different purposes and work well together. Managed platforms are best for global latency monitoring from distributed nodes. Custom scrapers are best for application-layer correctness checks that require real business logic. Teams often run both: pings and global checks via a managed platform, application flow verification via custom scheduled scrapers.

How do I handle script maintenance as my app evolves?

Tie script updates to your deploy process. Any pull request that changes a monitored user flow should include a corresponding update to the synthetic check. This discipline prevents script drift, which is the most common reason synthetic monitoring setups fail over time. Treat each script as living documentation of the correct behavior of its monitored flow.

What is the difference between synthetic monitoring and end-to-end testing?

End-to-end tests run against a staging environment with mocked or seeded data and are designed to be deterministic and repeatable. Synthetic monitoring runs against production with real data and is designed to reflect the actual state of the live system. E2E tests catch issues before deploy; synthetic monitors catch issues that only appear in production, including third-party integrations, live data edge cases, and infrastructure configurations not replicated in staging.

Disclaimer: Trawl provides scraping infrastructure. Users are responsible for ensuring their use complies with applicable laws, robots.txt directives, and target website terms of service. This article is for educational purposes only.

Written by Pierre | September 2026