Skip to main content

OpenStatus vs Better Stack vs Instatus: Status Page and Uptime Monitoring (2026)

·PkgPulse Team

TL;DR

OpenStatus is the open-source status page and monitoring platform — synthetic monitoring, status pages, incident management, API checks, self-hostable, built with Next.js. Better Stack (formerly Better Uptime) is the all-in-one observability platform — uptime monitoring, status pages, incident management, on-call scheduling, log management, built for modern DevOps teams. Instatus is the fast, beautiful status page — quick setup, incident communication, integrations, focused purely on status communication. In 2026: OpenStatus for open-source status monitoring, Better Stack for full observability with uptime, Instatus for the fastest beautiful status page setup.

Key Takeaways

  • OpenStatus: Open-source, synthetic monitoring, status pages, self-hostable
  • Better Stack: All-in-one — uptime + logs + on-call + status pages
  • Instatus: Fastest setup — beautiful status pages in minutes
  • OpenStatus is free and open-source (Next.js, Cloudflare Workers)
  • Better Stack combines uptime monitoring with log management and incident response
  • Instatus has the most polished status page design out of the box

OpenStatus

OpenStatus — open-source monitoring:

Setup monitoring

// OpenStatus API — create a monitor:
const response = await fetch("https://api.openstatus.dev/v1/monitor", {
  method: "POST",
  headers: {
    "x-openstatus-key": process.env.OPENSTATUS_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "PkgPulse API",
    url: "https://api.pkgpulse.com/health",
    periodicity: "5m",  // Check every 5 minutes
    regions: ["iad", "sfo", "ams", "hkg"],  // Multi-region
    method: "GET",
    headers: [{ key: "Accept", value: "application/json" }],
    assertions: [
      { type: "status", compare: "eq", target: 200 },
      { type: "latency", compare: "lt", target: 2000 },  // < 2s
    ],
  }),
})

const monitor = await response.json()
console.log(`Monitor created: ${monitor.id}`)

Status page configuration

// Create/update status page:
const statusPage = await fetch("https://api.openstatus.dev/v1/page", {
  method: "POST",
  headers: {
    "x-openstatus-key": process.env.OPENSTATUS_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    title: "PkgPulse Status",
    slug: "pkgpulse",
    description: "Current status of PkgPulse services",
    monitors: [1, 2, 3],  // Monitor IDs to display
    customDomain: "status.pkgpulse.com",
    icon: "https://pkgpulse.com/favicon.ico",
  }),
})

// Report an incident:
await fetch("https://api.openstatus.dev/v1/incident", {
  method: "POST",
  headers: {
    "x-openstatus-key": process.env.OPENSTATUS_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    title: "API Degraded Performance",
    status: "investigating",  // investigating | identified | monitoring | resolved
    monitors: [1],
    message: "We're investigating increased latency on the API.",
  }),
})

Synthetic monitoring checks

// Advanced monitor with assertions:
await fetch("https://api.openstatus.dev/v1/monitor", {
  method: "POST",
  headers: {
    "x-openstatus-key": process.env.OPENSTATUS_API_KEY!,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "PkgPulse Search API",
    url: "https://api.pkgpulse.com/search?q=react",
    periodicity: "10m",
    regions: ["iad", "ams"],
    method: "GET",
    assertions: [
      { type: "status", compare: "eq", target: 200 },
      { type: "latency", compare: "lt", target: 1000 },
      { type: "header", key: "content-type", compare: "contains", target: "application/json" },
    ],
    notifications: [
      { type: "email", target: "ops@pkgpulse.com" },
      { type: "slack", target: "https://hooks.slack.com/..." },
    ],
    degradedAfter: 3,   // Mark degraded after 3 failures
    timeout: 10000,      // 10s timeout
  }),
})

// Query monitor results:
const results = await fetch(
  "https://api.openstatus.dev/v1/monitor/1/result?limit=50",
  { headers: { "x-openstatus-key": process.env.OPENSTATUS_API_KEY! } }
)

const data = await results.json()
data.forEach((result: any) => {
  console.log(`${result.region}: ${result.latency}ms — ${result.statusCode}`)
})

Better Stack

Better Stack — all-in-one observability:

Uptime monitoring

// Better Stack Uptime API — create a monitor:
const response = await fetch("https://uptime.betterstack.com/api/v2/monitors", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BETTERSTACK_API_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    monitor_type: "status",
    url: "https://api.pkgpulse.com/health",
    pronounceable_name: "PkgPulse API",
    check_frequency: 180,  // Every 3 minutes (seconds)
    regions: ["us", "eu", "as", "au"],
    request_timeout: 15,
    confirmation_period: 3,  // Confirm down for 3 checks
    http_method: "get",
    expected_status_codes: [200],
    request_headers: [
      { name: "Accept", value: "application/json" },
    ],
    // Alert policies:
    policy_id: "policy-123",
    follow_redirects: true,
    verify_ssl: true,
    ssl_expiration: 30,  // Alert 30 days before SSL expiry
  }),
})

const monitor = await response.json()
console.log(`Monitor: ${monitor.data.id}${monitor.data.attributes.url}`)

Incident management

// Create an incident:
const incident = await fetch("https://uptime.betterstack.com/api/v2/incidents", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BETTERSTACK_API_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    requester_email: "ops@pkgpulse.com",
    name: "Database Connection Issues",
    summary: "PostgreSQL connections timing out, causing API errors",
    description: "Investigating connection pool exhaustion on the primary database",
    call: true,     // Trigger phone call to on-call
    sms: true,      // Send SMS
    email: true,    // Send email
    push: true,     // Push notification
  }),
})

// Update incident timeline:
await fetch(`https://uptime.betterstack.com/api/v2/incidents/${incident.data.id}/timeline`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BETTERSTACK_API_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    content: "Identified root cause: connection pool limit too low. Scaling up.",
  }),
})

// Resolve incident:
await fetch(`https://uptime.betterstack.com/api/v2/incidents/${incident.data.id}/resolve`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BETTERSTACK_API_TOKEN}`,
    "Content-Type": "application/json",
  },
})

Status page and on-call

// Create status page:
await fetch("https://uptime.betterstack.com/api/v2/status-pages", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BETTERSTACK_API_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    company_name: "PkgPulse",
    company_url: "https://pkgpulse.com",
    subdomain: "pkgpulse",  // pkgpulse.betteruptime.com
    custom_domain: "status.pkgpulse.com",
    timezone: "America/New_York",
    subscribable: true,  // Allow email subscriptions
    hide_from_search_engines: false,
  }),
})

// Set up on-call schedule:
await fetch("https://uptime.betterstack.com/api/v2/on-call-calendars", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BETTERSTACK_API_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "PkgPulse Engineering On-Call",
    default_calendar_attributes: {
      on_call_users: [
        { id: "user-1", weekday_from: "monday", weekday_to: "friday" },
        { id: "user-2", weekday_from: "saturday", weekday_to: "sunday" },
      ],
    },
  }),
})

// Heartbeat monitor (for cron jobs):
// POST https://uptime.betterstack.com/api/v1/heartbeat/{heartbeat_id}
// If no ping received within the expected period → alert
await fetch(`https://uptime.betterstack.com/api/v1/heartbeat/${HEARTBEAT_ID}`)

Log management

// Send logs to Better Stack (Logtail):
import Logtail from "@logtail/node"

const logtail = new Logtail(process.env.LOGTAIL_TOKEN!)

// Structured logging:
logtail.info("Package data synced", {
  packages_count: 150,
  duration_ms: 2340,
  source: "npm-registry",
})

logtail.error("API request failed", {
  url: "/api/packages/react",
  status: 500,
  error: "Database connection timeout",
  request_id: "req-abc-123",
})

// Flush logs (important before process exit):
await logtail.flush()

// Express middleware for request logging:
import { LogtailTransport } from "@logtail/winston"
import winston from "winston"

const logger = winston.createLogger({
  transports: [new LogtailTransport(logtail)],
})

app.use((req, res, next) => {
  const start = Date.now()
  res.on("finish", () => {
    logger.info("HTTP Request", {
      method: req.method,
      path: req.path,
      status: res.statusCode,
      duration_ms: Date.now() - start,
    })
  })
  next()
})

Instatus

Instatus — beautiful status pages:

Status page setup

// Instatus API — create a status page:
const response = await fetch("https://api.instatus.com/v1/pages", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.INSTATUS_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "PkgPulse",
    subdomain: "pkgpulse",  // pkgpulse.instatus.com
    status: "UP",
    logoUrl: "https://pkgpulse.com/logo.png",
    faviconUrl: "https://pkgpulse.com/favicon.ico",
    websiteUrl: "https://pkgpulse.com",
    language: "en",
    useLargeHeader: true,
    subscribable: true,
  }),
})

const page = await response.json()
const pageId = page.id

Components and incidents

// Add components (services to monitor):
const component = await fetch(`https://api.instatus.com/v1/${pageId}/components`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.INSTATUS_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "API",
    description: "PkgPulse REST API",
    status: "OPERATIONAL",  // OPERATIONAL | DEGRADEDPERFORMANCE | PARTIALOUTAGE | MAJOROUTAGE | UNDERMAINTENANCE
    order: 1,
    showUptime: true,
    grouped: false,
  }),
})

// Add more components:
const components = [
  { name: "Website", description: "Main website", order: 2 },
  { name: "Search", description: "Package search engine", order: 3 },
  { name: "CDN", description: "Static asset delivery", order: 4 },
]

for (const comp of components) {
  await fetch(`https://api.instatus.com/v1/${pageId}/components`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.INSTATUS_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ ...comp, status: "OPERATIONAL", showUptime: true }),
  })
}

// Create an incident:
const incident = await fetch(`https://api.instatus.com/v1/${pageId}/incidents`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.INSTATUS_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "Search Index Rebuild Delay",
    message: "Search results may be stale while the index rebuilds.",
    components: [component.id],
    started: new Date().toISOString(),
    status: "INVESTIGATING",  // INVESTIGATING | IDENTIFIED | MONITORING | RESOLVED
    notify: true,  // Notify subscribers
    statuses: [
      {
        id: component.id,
        status: "DEGRADEDPERFORMANCE",
      },
    ],
  }),
})

Incident updates and integrations

// Update incident with progress:
await fetch(`https://api.instatus.com/v1/${pageId}/incidents/${incident.id}/incident-updates`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.INSTATUS_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    message: "Root cause identified — index shard allocation issue. Rebalancing in progress.",
    status: "IDENTIFIED",
    started: new Date().toISOString(),
    notify: true,
    statuses: [
      { id: component.id, status: "DEGRADEDPERFORMANCE" },
    ],
  }),
})

// Resolve incident:
await fetch(`https://api.instatus.com/v1/${pageId}/incidents/${incident.id}/incident-updates`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.INSTATUS_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    message: "Search index rebuild complete. All services operational.",
    status: "RESOLVED",
    started: new Date().toISOString(),
    notify: true,
    statuses: [
      { id: component.id, status: "OPERATIONAL" },
    ],
  }),
})

// Schedule maintenance:
await fetch(`https://api.instatus.com/v1/${pageId}/incidents`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.INSTATUS_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "Scheduled Database Maintenance",
    message: "We'll be performing routine database maintenance. Brief downtime expected.",
    status: "NOTSTARTEDYET",
    notify: true,
    started: "2026-03-15T02:00:00Z",
    components: [component.id],
    statuses: [{ id: component.id, status: "UNDERMAINTENANCE" }],
  }),
})

// Webhook integration (receive Instatus events):
// POST /api/webhooks/instatus
app.post("/api/webhooks/instatus", (req, res) => {
  const { incident, page } = req.body

  if (incident.status === "RESOLVED") {
    // Post to Slack, update internal dashboards, etc.
    notifySlack(`✅ ${incident.name} resolved on ${page.name}`)
  }

  res.sendStatus(200)
})

Feature Comparison

FeatureOpenStatusBetter StackInstatus
Open-source
Self-hosted
Status pages
Uptime monitoring✅ (synthetic)✅ (multi-protocol)❌ (3rd party)
Incident management
On-call scheduling
Log management✅ (Logtail)
Heartbeat monitors
Multi-region checks
Custom domains
Subscriber notifications
Scheduled maintenance
API access
IntegrationsSlack, Discord, email100+ (Slack, PagerDuty, etc.)Slack, Discord, email
Free tierOpen-source (unlimited)10 monitors1 status page
PricingFree / Cloud plansFrom $29/monthFrom $20/month

When to Use Each

Use OpenStatus if:

  • Want an open-source, self-hostable monitoring solution
  • Need synthetic monitoring with multi-region checks
  • Prefer owning your monitoring infrastructure
  • Building on Cloudflare Workers / Next.js ecosystem

Use Better Stack if:

  • Need all-in-one observability (uptime + logs + on-call + status page)
  • Want on-call scheduling and escalation policies
  • Need log management alongside uptime monitoring
  • Building a DevOps workflow with incident response

Use Instatus if:

  • Need the fastest, most beautiful status page setup
  • Want a focused status communication tool (not full monitoring)
  • Need subscriber notifications and maintenance windows
  • Prefer simplicity over feature breadth

Methodology

Feature comparison based on OpenStatus, Better Stack, and Instatus platforms and pricing as of March 2026.

Compare DevOps tooling and monitoring libraries on PkgPulse →

Comments

Stay Updated

Get the latest package insights, npm trends, and tooling tips delivered to your inbox.