# Status Page Monitor - StatusLedger (`leons0016/status-page-monitor-ledger`) Actor

Status page monitor for vendor pages built on Statuspage, Instatus and status.io (not every vendor page): every incident with all its updates in a ledger that survives between runs, plus an optional monthly uptime and SLA-credit estimate. Plain HTTP, robots.txt respected.

- **URL**: https://apify.com/leons0016/status-page-monitor-ledger.md
- **Developed by:** [Leon s](https://apify.com/leons0016) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $12.00 / 1,000 status page reads

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Status Page Monitor - StatusLedger

**Status page monitor and incident ledger for the vendors you depend on.** StatusLedger reads the public status pages of your SaaS and cloud vendors (Atlassian Statuspage, Instatus and status.io), keeps every incident with its **full timeline of updates** in a ledger that survives between runs, separates **components** from **incidents**, and can build a **monthly uptime and SLA-credit report** per vendor. It uses plain HTTP only, no browser and no proxy, and respects each site's robots.txt.

Actor page: `https://apify.com/leons0016/status-page-monitor-ledger`

### Read this first: narrow coverage

- **Only three kinds of status page are read:** Atlassian Statuspage (and compatible pages), Instatus and status.io. Vendors on other status products, on their own custom pages or on pages without public JSON or feeds are reported as `unsupported` or `failed` with the reason, and are not charged.
- **History is shallow.** Public endpoints list about the latest 50 incidents (Statuspage JSON), about 25 (feeds) or only active incidents (status.io). The ledger grows from the first run onward; it cannot back-fill older history than the vendor still shows.
- **Robots.txt is honoured.** Several large vendors disallow `/api/` for automated clients, so for them only the latest feed entries are read.
- **Uptime and credit figures are estimates** from what the vendor published on its own page, not a contract measurement and not legal advice.
- **No login, no CAPTCHA solving, no browser, no proxy.**

Statuspage is a trademark of Atlassian. Instatus, status.io and other product and vendor names belong to their owners. This Actor is an independent product of LeonS. It is not affiliated with, endorsed by or sponsored by Apify Technologies, Atlassian, the vendors whose status pages you list or any third party whose name is mentioned. All product and company names are trademarks of their respective owners and are used only to identify the data source or the software the Actor works with.

It is built for IT operations, SRE, vendor management and procurement teams who need to answer "what happened at vendor X last month, and do we have a credit claim?" without checking a dozen pages by hand.

### Use cases

- **Vendor outage history.** An append-only ledger of every incident on every vendor status page you list, with each update (investigating, identified, monitoring, resolved) and its time.
- **SLA credit evidence.** Monthly uptime per vendor, minutes of major outage, partial outage and degraded performance, and the credit tier your contract gives for that uptime.
- **Incident alerts.** Run it on a schedule and send new incidents to Slack, email or a webhook with Apify integrations.
- **Vendor risk reviews.** Number of incidents, longest incident and mean time to resolve per vendor and month.
- **Status data for your own tools.** One dataset with the same fields for all three platforms.

### What you get

- **Full incident timelines.** Every update of every incident, not only the first one.
- **A durable ledger.** New incidents are added, updates are appended and nothing that was seen is deleted, even if the vendor edits or removes it later. A change of severity or end time is kept in the incident's history, and the worst severity ever reported is kept next to the current one.
- **No cap on the number of results per run.** Pages that offer more than one page of incidents are read page by page until a page adds nothing new.
- **Components kept apart from incidents.** Each status page row lists the components that are not operational. Optional component rows show components that are down or changed status since the last run.
- **One row per status page, always.** Every address in your list gets exactly one row with the result `ok`, `failed` or `unsupported` and a sentence that explains it. A run is never empty.
- **A monthly report** (optional) with uptime, downtime by severity, incident count, credit percent and an estimated credit in dollars when you give the monthly fee.

### Supported status pages

| Platform | What is read | Notes |
|---|---|---|
| Atlassian Statuspage (and compatible pages) | `/api/v2/summary.json`, `/api/v2/incidents.json` (all updates), `/api/v2/scheduled-maintenances.json` | The public incidents endpoint lists the 50 most recent incidents. The ledger grows beyond that with every run. |
| Statuspage pages whose robots.txt disallows `/api/` | `/index.json`, `/history.atom`, `/history.json` | Many large vendors do this. Only robots-permitted addresses are requested. The feed lists about the latest 25 incidents. |
| Instatus | `/v3/summary.json`, `/v3/components.json`, `/history.atom` | The feed carries the timelines; severity comes from the summary for current and recently resolved incidents and is `unknown` otherwise. |
| status.io | `https://api.status.io/1.0/status/<page id>` | Give the page as `statusio:<24-character page id>`. The API lists active incidents only, so an incident is closed when it disappears (marked `observed-absent`). |

Pages built on other products, pages without public JSON and pages that forbid automated access in robots.txt are reported as `unsupported` or `failed` with the reason. StatusLedger never logs in, never solves CAPTCHAs and never requests an address that robots.txt disallows.

### How to use

1. Open the Actor and enter your vendors under **Status pages**, one per line: `www.githubstatus.com`, `https://status.example.com`, or `statusio:<page id>`.
2. Press **Start**. The first run records the incidents of the last 7 days (**Look-back**); raise it once, for example to 35, if the first run should back-fill a month.
3. Click **Save as new task** and add a **Schedule**, for example daily. The task keeps its ledger in a named key-value store in your account (**Ledger name**).
4. For a monthly report set **Monthly SLA report** to *Previous month* (or *Automatic*) on the same schedule; the report is produced once per month.
5. Optional: add Slack, Gmail or a webhook under **Integrations**.

### Input

| Field | What it does |
|---|---|
| Status pages | One address per line. Host name, full link or `statusio:<id>`. Lines starting with `#` are ignored; duplicates are ignored. |
| Look-back for new incidents (days) | 1 to 3650, default 7. Incidents that started or changed within this window are recorded; older ones only if already in the ledger. |
| Rows for components that are not operational | Default on. Uncharged. |
| Monthly SLA report | *Off* (default), *Automatic*, *Previous month* or *Month to date*. |
| What counts as downtime | Major outages only, major and partial outages (default), or any impact including degraded performance. Maintenance is never counted. |
| SLA credit tiers | Default: below 99.9 percent 10 percent credit, below 99.0 percent 25, below 95.0 percent 50. The deepest breached tier applies. |
| Per vendor settings | Optional JSON keyed by host: `name`, `monthlyFeeUsd`, `creditTiers`. |
| Ledger name | Default `default`. A different name starts a separate ledger, for example one per client. |
| Webhook URL, Slack incoming webhook | Optional, stored as secrets. |

Sample input:

```json
{
  "statusPages": ["www.githubstatus.com", "www.cloudflarestatus.com", "statusio:55957a99e800baa4470002da"],
  "lookbackDays": 7,
  "slaReport": "previous-month",
  "vendorSettings": { "www.githubstatus.com": { "name": "GitHub", "monthlyFeeUsd": 400 } }
}
```

The input is checked before anything is charged. A free plan run reads up to 5 status pages; the others get a row that says so.

### Output

- **Status pages** (default view): one row per address with result, platform, read mode (`api` or `feed`), overall status, number of components, components not operational, unresolved incidents, incidents recorded and an explanation.
- **Incidents**: id, kind (incident or maintenance), name, status, impact, worst impact reported, start, end, duration, link, components, all updates, first-seen time and whether the row was billed.
- **Components**: components that are not operational or changed status, with the previous status.
- **SLA by vendor**: month, uptime percent, minutes of downtime by severity, incident count, longest incident, mean time to resolve, credit percent, estimated credit, data coverage and notes.
- **Run summary**: one uncharged row, always the last, with counts, warnings per code and the stopped reason.
- Key-value store: `OUTPUT` (JSON), `REPORT` (HTML), `INCIDENTS.csv` and, after a report, `SLA-YYYY-MM.csv`.

### Pricing

Pay per event. You pay only for what a run delivers:

| Event | Price (USD) | When |
|---|---|---|
| `vendor-checked` | 0.015 | Once for each status page that was read successfully, in every run. Pages that fail or are not supported are not charged. |
| `incident-recorded` | 0.05 | Once for each incident when it enters the ledger. Updates, resolutions, maintenance windows and component rows are free. |
| `monthly-sla-report` | 2.00 | Once for each monthly report (all vendors in it). |

**Your monthly bill is at most: status pages x runs per month x 0.015, plus 0.05 for every new incident, plus 2.00 for each monthly report.** Example: 80 status pages checked daily (30 runs) is 2,400 x 0.015 = 36.00; 40 new incidents in the month add 2.00; one report adds 2.00; the total is about 40.00. Ten pages checked daily is 4.50 plus incidents. The default Apify start event applies as usual. Compute for a short 256 MB run is a small fraction of a cent (check Insights in Console).

Spending limits are per run. Set a maximum charge per run in the run options if you want a hard cap; when it is reached the run stops cleanly, the rows of the remaining pages say so, and incidents that were not charged are recorded in the next run. The free plan has no separate trial period: Apify's free allowance and the low `vendor-checked` price let you test with up to 5 pages before you pay for more.

The first run records the incidents of the look-back window, and each new incident is billed once. A 35-day back-fill for a busy vendor can add up, so check the number of incidents on the vendor's own page first.

### Limits and honest notes

- **Depth of history.** The ledger holds what the status pages showed when it ran. Public endpoints list the latest 50 incidents (Statuspage JSON), about 25 (Statuspage and Instatus feeds) or only active incidents (status.io). Run it often enough that nothing falls out of those windows; each month's report states how many days have a check.
- **Estimates, not contract measurements.** Uptime and credit figures come from what the vendor published on its own status page. A credit claim follows your contract and the vendor's own measurement.
- **Severity.** Statuspage publishes it for each incident. Instatus and status.io publish less; incidents without a severity are listed separately and never counted as downtime.
- **End times for status.io** are the time of the first run that no longer saw the incident.
- **Status pages go down too.** If a page cannot be read, the row says `failed` and nothing is charged; the ledger keeps the last error.

### FAQ

**Does it use a browser or a proxy?** No. It sends a few plain HTTPS requests per status page and identifies itself in the User-Agent.

**What if a vendor's robots.txt blocks the API?** Then StatusLedger does not request it. For Statuspage pages it switches to the robots-permitted pages (`/index.json`, `/history.atom`, `/history.json`), and the row says `mode: feed` with a warning. If nothing is permitted, the row says so.

**Where is my data?** In your own Apify account: the run's dataset and key-value store, and the named ledger store `leons-statusledger-<name>-ledger`. The publisher receives nothing. See the privacy policy.

**Can I keep ledgers apart?** Yes, use a different **Ledger name** per client or team.

**Support:** atulhritik@hotmail.com

Third-party licences and attributions: see `docs/THIRD_PARTY_NOTICES.txt` in the Actor source.

# Changelog

This Actor's version history is a separate document: https://apify.com/leons0016/status-page-monitor-ledger/changelog.md

# Actor input Schema

## `statusPages` (type: `array`):

One address per line: the status page host (status.example.com), a full link, or statusio:<24-character page id> for a status.io page. Atlassian Statuspage, Instatus and status.io are supported. Example: www.githubstatus.com

## `lookbackDays` (type: `integer`):

Incidents that started or changed within this many days are recorded; older ones are ignored unless already in the ledger. Raise it once for a first run that should back-fill a month (each new incident is a billed event). Example: 35

## `includeComponentRows` (type: `boolean`):

Adds an uncharged row per component that is not operational or changed status since the last run. Components are kept apart from incidents.

## `slaReport` (type: `string`):

Off builds no report. Previous month and Automatic deliver the last full calendar month once (a second run with the same status pages is not charged again). Month to date is billed on every run that uses it.

## `slaBasis` (type: `string`):

Severity the vendor reported for an incident. Maintenance is never counted. Incidents without a severity are listed separately.

## `creditTiers` (type: `array`):

When uptime is below belowPercent the vendor owes creditPercent of the monthly fee (the deepest breached tier applies). Example: \[{"belowPercent": 99.9, "creditPercent": 10}]

## `vendorSettings` (type: `object`):

Optional, keyed by status page host: name, monthlyFeeUsd (to estimate the credit in dollars) and creditTiers for that vendor. Example: {"www.githubstatus.com": {"name": "GitHub", "monthlyFeeUsd": 400}}

## `ledgerName` (type: `string`):

Name of the ledger (a key-value store in your account that keeps the history between runs). Use a different name to keep a separate ledger, for example per client. Letters, digits and hyphens.

## `webhookUrl` (type: `string`):

Optional https address that receives the run summary and the incidents of the run.

## `slackWebhookUrl` (type: `string`):

Optional. Slack gets up to 10 incident lines and a link to the run.

## Actor input object example

```json
{
  "statusPages": [
    "www.githubstatus.com",
    "www.cloudflarestatus.com",
    "discordstatus.com",
    "status.atlassian.com",
    "instat.us",
    "statusio:55957a99e800baa4470002da"
  ],
  "lookbackDays": 7,
  "includeComponentRows": true,
  "slaReport": "off",
  "slaBasis": "partial-outage",
  "creditTiers": [
    {
      "belowPercent": 99.9,
      "creditPercent": 10
    },
    {
      "belowPercent": 99,
      "creditPercent": 25
    },
    {
      "belowPercent": 95,
      "creditPercent": 50
    }
  ],
  "vendorSettings": {
    "www.githubstatus.com": {
      "name": "GitHub",
      "monthlyFeeUsd": 400
    }
  },
  "ledgerName": "default"
}
```

# Actor output Schema

## `vendors` (type: `string`):

One row per status page: ok, failed or unsupported, with an explanation.

## `incidents` (type: `string`):

Incidents recorded or updated, with every update.

## `components` (type: `string`):

Components that are not operational or changed.

## `sla` (type: `string`):

Monthly uptime and SLA credit per vendor.

## `runSummary` (type: `string`):

Counts, warnings and the stopped reason.

## `report` (type: `string`):

One-page report.

## `incidentsCsv` (type: `string`):

Incidents of this run.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "statusPages": [
        "www.githubstatus.com",
        "www.cloudflarestatus.com",
        "discordstatus.com",
        "status.atlassian.com",
        "instat.us",
        "statusio:55957a99e800baa4470002da"
    ],
    "creditTiers": [
        {
            "belowPercent": 99.9,
            "creditPercent": 10
        },
        {
            "belowPercent": 99,
            "creditPercent": 25
        },
        {
            "belowPercent": 95,
            "creditPercent": 50
        }
    ],
    "vendorSettings": {
        "www.githubstatus.com": {
            "name": "GitHub",
            "monthlyFeeUsd": 400
        }
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("leons0016/status-page-monitor-ledger").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "statusPages": [
        "www.githubstatus.com",
        "www.cloudflarestatus.com",
        "discordstatus.com",
        "status.atlassian.com",
        "instat.us",
        "statusio:55957a99e800baa4470002da",
    ],
    "creditTiers": [
        {
            "belowPercent": 99.9,
            "creditPercent": 10,
        },
        {
            "belowPercent": 99,
            "creditPercent": 25,
        },
        {
            "belowPercent": 95,
            "creditPercent": 50,
        },
    ],
    "vendorSettings": { "www.githubstatus.com": {
            "name": "GitHub",
            "monthlyFeeUsd": 400,
        } },
}

# Run the Actor and wait for it to finish
run = client.actor("leons0016/status-page-monitor-ledger").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "statusPages": [
    "www.githubstatus.com",
    "www.cloudflarestatus.com",
    "discordstatus.com",
    "status.atlassian.com",
    "instat.us",
    "statusio:55957a99e800baa4470002da"
  ],
  "creditTiers": [
    {
      "belowPercent": 99.9,
      "creditPercent": 10
    },
    {
      "belowPercent": 99,
      "creditPercent": 25
    },
    {
      "belowPercent": 95,
      "creditPercent": 50
    }
  ],
  "vendorSettings": {
    "www.githubstatus.com": {
      "name": "GitHub",
      "monthlyFeeUsd": 400
    }
  }
}' |
apify call leons0016/status-page-monitor-ledger --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,leons0016/status-page-monitor-ledger"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/hUdCkXfaRnXSkqaXq/builds/teHKBSCQeVxpy9Hvw/openapi.json
