Group monthly summary

Read one client group's month as JSON: uptime, outages with their cause, certificate and domain expiry, and DNS, hosting and content changes, with every field explained.

Last reviewed

On this page

The monthly summary returns one group's month in a single request: totals for the group and one entry for each site in it. It is the same data as the monthly summary email, limited to the sites in the group, so the figures match what you send a client. Use it to build a client report from your own code. Any key can call it, a read key included.

Request

GEThttps://octoping.com/api/groups/{group_id}/monthly-summary

Get a group's monthly summary

Access needed: Read only

Returns the group's totals and one entry per site: uptime and average response time, every outage with its start, end, length and cause, the latest known certificate and domain expiry, and the DNS, hosting and content changes found in the month. It is the same data as the monthly summary email, limited to the sites in the group. A read key gets a site's address without any user name and password written into it, so https://user:pass@example.com/ is returned as https://example.com/. The rest of the address, including its query string, is returned as stored, and a read and write key or an admin key gets the whole address. month is a calendar month in UTC written YYYY-MM and defaults to the last complete month. A month that has not started, or a value that is not a month, is refused with 422. The month in progress is allowed and reports what has been recorded so far. Each list holds the newest 50 entries and the true number is given beside it (outage_count, dns_change_count, hosting_change_count and content_change_count). A site with nothing recorded in the month reports null instead of zero. days_left on the certificate and the domain is counted from generated_at. data_truncated is true when the start of the month has been deleted under the plan's data retention, so the figures are a minimum. It is false for a site added during the month, and for a site that still has checks from before the month. The answer is reused for a few minutes, and for an hour once the month is over, and generated_at says when it was computed. Only one summary is prepared at a time for a workspace. A request that needs a fresh one while another is being prepared is refused with 429, the code summary_in_progress and a Retry-After header. Wait that many seconds and ask again. Renaming the group does not make the figures stale.

  • group_id (path, string, required)
  • month (query, string, optional)

Find the group's id with the list groups operation in the API reference. A group that isn't in your workspace answers 404.

One group, October 2026
export GROUP_ID="the group's id"
curl -fsS -H "X-API-Key: $OCTOPING_API_KEY" "https://octoping.com/api/groups/$GROUP_ID/monthly-summary?month=2026-10"

To print one line for each site, pipe the answer to jq.

bash
curl -fsS -H "X-API-Key: $OCTOPING_API_KEY" "https://octoping.com/api/groups/$GROUP_ID/monthly-summary?month=2026-10" \
  | jq -r '.sites[] | [.url, .uptime_percentage, .outage_count] | @tsv'

Response

The answer is JSON with these keys. A trimmed example follows the tables.

KeyTypeMeaning
groupobjectThe group: its id and name.
monthstringThe month the summary covers, as YYYY-MM.
periodobjectstart is the first instant of the month and end the first instant of the next, both in UTC. The start is included and the end isn't.
generated_atstringWhen OctoPing computed the answer. See How current the answer is below.
totalsobjectFigures for the whole group.
siteslistOne entry for each site in the group, ordered by URL.

totals

KeyTypeMeaning
siteswhole numberThe number of sites in the group.
uptime_percentagenumber or nullThe mean of the sites' uptime, so each site weighs the same. A site with no counted checks is left out. It is the figure the monthly summary email shows.
outageswhole number or nullThe outages across all sites. Null when no site has anything recorded in the month.
downtime_secondsnumber or nullThe time the sites were down, in seconds. Null when no site has anything recorded.
average_response_time_msnumber or nullThe mean of the sites' average response times, in milliseconds.
content_changeswhole numberThe page content changes found across all sites.
dns_changeswhole numberThe DNS changes found across all sites.
hosting_changeswhole numberThe hosting changes found across all sites.
data_truncatedtrue or falseTrue when any site's is. See the field of the same name in each site.

Each entry in sites

KeyTypeMeaning
id, urlstringThe site's id and URL. A read key gets a site's address without any user name and password written into it.
namestringThe site's URL again. A site has no separate name.
uptime_percentage, average_response_time_msnumber or nullThe site's uptime and average response time for the month.
outage_count, downtime_secondsnumber or nullHow many outages the site had and how long it was down, in seconds.
outageslistThe site's outages. See the next table.
sslobject or nullThe latest known certificate: expires_at, days_left and issuer. Null until a certificate check has run.
domainobject or nullThe latest known domain registration: expires_at, days_left and registrar. Null until a domain check has run.
dns_change_count, hosting_change_count, content_change_countwhole numberThe true number of changes of each kind found in the month.
dns_changes, hosting_changeslistThe changes, each with detected_at and a short summary.
content_changeslistThe content changes, each with detected_at.
data_truncatedtrue or falseTrue when the plan's data retention has already deleted the start of the month for this site, so the outages, downtime and counts are a minimum. It is false for a site added during the month, and for a site that still has checks from before the month.

Each outage

KeyTypeMeaning
started_atstringWhen the outage began. It is the start of the month if the outage began earlier.
ended_atstring or nullWhen the site recovered. Null when it hadn't recovered by the end of the month, or by now for a month in progress.
duration_secondsnumberThe length of the outage inside the month. When outages holds as many entries as outage_count, the durations add up to downtime_seconds.
reasonobject or nullWhy the outage began, as a stable code and a readable label. The codes are timeout, dns, connection, tls, blocked, http_client, http_server, unknown, as the failure reasons page describes.
A trimmed answer
{
  "group": {
    "id": "00000000-0000-0000-0000-000000000000",
    "name": "Acme Corp"
  },
  "month": "2026-10",
  "period": {
    "start": "2026-10-01T00:00:00Z",
    "end": "2026-11-01T00:00:00Z"
  },
  "generated_at": "2026-11-02T08:15:00Z",
  "totals": {
    "sites": 2,
    "uptime_percentage": 99.84,
    "outages": 1,
    "downtime_seconds": 2760,
    "average_response_time_ms": 412.5,
    "content_changes": 3,
    "dns_changes": 0,
    "hosting_changes": 0,
    "data_truncated": false
  },
  "sites": [
    {
      "id": "00000000-0000-0000-0000-000000000001",
      "name": "https://acme.example",
      "url": "https://acme.example",
      "uptime_percentage": 99.68,
      "average_response_time_ms": 388.2,
      "outage_count": 1,
      "downtime_seconds": 2760,
      "outages": [
        {
          "started_at": "2026-10-14T02:10:00Z",
          "ended_at": "2026-10-14T02:56:00Z",
          "duration_seconds": 2760,
          "reason": {
            "code": "http_server",
            "label": "Service Unavailable (HTTP 503)"
          }
        }
      ],
      "ssl": {
        "expires_at": "2027-01-09T23:59:59Z",
        "days_left": 68,
        "issuer": "Let's Encrypt"
      },
      "domain": {
        "expires_at": "2027-06-30T00:00:00Z",
        "days_left": 240,
        "registrar": "Example Registrar"
      },
      "dns_change_count": 0,
      "dns_changes": [],
      "hosting_change_count": 0,
      "hosting_changes": [],
      "content_change_count": 3,
      "content_changes": [
        {
          "detected_at": "2026-10-22T09:30:00Z"
        },
        {
          "detected_at": "2026-10-12T14:05:00Z"
        },
        {
          "detected_at": "2026-10-03T08:00:00Z"
        }
      ],
      "data_truncated": false
    }
  ]
}

Null means no data

A site with nothing recorded in the month reports null for uptime_percentage, average_response_time_ms, outage_count and downtime_seconds. Zero outages means the site was checked and stayed up. Null means the month holds nothing to report. In the same way, totals.outages and totals.downtime_seconds are null when no site has anything recorded, so an empty month never reads as a clean one.

Lists are capped

Each list in a site's entry holds the newest 50 entries, newest first, and the true number sits beside it in outage_count, dns_change_count, hosting_change_count and content_change_count. A list that is shorter than its count is a cut list, and the count is the number to report.

Certificate and domain expiry

The certificate and domain expiry are the latest known values and are not limited to the month the summary covers. days_left is counted from generated_at to expires_at in whole days, and it is negative once the certificate or registration has expired.

How current the answer is

OctoPing reuses an answer for 5 minutes, or 60 minutes once the month is over. A change to the group's sites, such as a site moved in or out or a site's URL, gets a fresh answer. A new group name shows at once, because the name isn't part of the reused figures. generated_at says when the figures were computed, so polling faster than that returns the same numbers and spends requests from the workspace's limits.

When another summary is being prepared

Computing a month of checks takes a moment, so OctoPing prepares one summary at a time for each workspace. A request that needs a fresh one while another is being prepared is refused with HTTP 429, the code summary_in_progress and a Retry-After header. Wait that many seconds and ask again. A request that finds an answer it can reuse is never refused for this reason. The refused request counts against the workspace's limits, so don't retry in a tight loop. The API keys page lists the codes that requests made with a key are refused with.