Skip to content

Browser checks

A browser check is a synthetic monitor that runs a Playwright script in a real Chromium browser on a schedule. Use it for the things HTTP checks can’t tell you:

  • Does the login flow work end-to-end with real JavaScript?
  • Does the checkout button actually charge a card?
  • Does the multi-step signup form submit correctly?
  • Did the latest deploy break the homepage’s hero CTA?
  • You have a critical user flow with multiple steps and JavaScript-driven UI.
  • You’ve ever shipped a deploy that broke a button without breaking any HTTP endpoint.
  • You want to monitor third-party JavaScript (analytics, payment widgets, fonts) that an HTTP check can’t observe.
  • For “is my API up?” — use HTTP checks. Browser checks cost ~30× more compute and are slower.
  • For “is my page fast for real users?” — use RUM. Browser checks measure synthetic latency from one IP, not your users’ experience.
  • For low-priority pages. Reserve browser checks for the 5–10 most important user flows.
  1. You write a Playwright script — JavaScript that drives the browser using the page API and step() helpers.
  2. SiteQwality runs your script in headless Chromium on Lambda on the schedule (5m, 10m, 15m, 30m, 1h).
  3. Each step’s pass/fail, duration, and a screenshot are captured.
  4. On every run, a HAR file (full network log) and a video are uploaded to S3.
  5. Web vitals (LCP, FCP, CLS, TTFB) are extracted from the page and reported.
  6. Console errors and unhandled exceptions are captured.
  7. Status flips to failed when steps fail, with the same pending_failure debounce as other checks (transient failures don’t immediately page).

The script runs inside a sandboxed async function with page (Playwright Page) and step (your wrapper for tracked steps) injected:

await step('Open homepage', async () => {
await page.goto('https://example.com');
});
await step('Click sign in', async () => {
await page.click('text=Sign in');
});
await step('Fill credentials', async () => {
await page.fill('input[name="email"]', 'demo@example.com');
await page.fill('input[name="password"]', 'demo-password');
await page.click('button[type="submit"]');
});
await step('Confirm dashboard loaded', async () => {
await page.waitForSelector('h1:has-text("Dashboard")');
});

Anything you can do with Playwright works inside a step.

ArtifactWhere
Step resultsPass/fail, duration, error message per step.
ScreenshotsAuto-captured after each step (pass or fail).
HAR fileFull network capture, downloadable from the run page.
Video1280×720 MP4 of the entire run.
Web vitalsLCP, FCP, CLS, TTFB extracted via PerformanceObserver.
Console errorsAnything logged at console.error or thrown.
Trace IDsOutgoing requests get a traceparent header injected, allowing distributed-tracing correlation.

Available frequencies: 5m, 10m, 15m, 30m, 1h. Browser checks are billed per run; the dashboard shows your current monthly count under Settings → Billing → Usage.

  • Script size: anything under 50KB. The script is stored in Postgres alongside the check config.
  • Timeout: 1300 seconds per run. Includes browser startup; the user script gets timeout - 30s.
  • Regions: same list as HTTP checks (aws-us-east-1, aws-us-east-2, aws-us-west-1, aws-eu-west-2).