# SiteVerdict — Website Change & Uptime Monitor (`siteverdict/siteverdict-change-uptime-monitor`) Actor

Schedule it to watch pages for downtime, slow responses, TLS expiry and redirects, plus real content changes: text or CSS/XPath regions, prices, keywords, SEO tags, robots.txt, sitemaps and screenshots. Noise-filtered diffs, Slack/webhook/e-mail alerts.

- **URL**: https://apify.com/siteverdict/siteverdict-change-uptime-monitor.md
- **Developed by:** [Aman Thakran](https://apify.com/siteverdict) (community)
- **Categories:** Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## SiteVerdict — Website Change & Uptime Monitor

**Know within the hour when a page goes down, a price changes, a competitor edits their pricing table, or a deploy quietly adds `noindex`, without false alarms from timestamps, rotating tokens or cookie banners.**

Put it on an [Apify Schedule](https://docs.apify.com/platform/schedules) (every 15 min, hourly or daily). Each run checks your URLs. It reports only what really changed, with a plain-English summary and a unified diff, and can alert you by webhook, Slack or e-mail.

### Who it is for

- **Site owners and agencies**: catch downtime, slow responses, expiring TLS certificates, broken redirects, accidental `noindex` and lost canonicals on client sites.
- **E-commerce and pricing teams**: watch competitor pricing tables, plan pages and stock status ("sold out" / "in stock").
- **SEO teams**: changes to title, meta description, canonical, H1, robots directives, `robots.txt` and sitemap URLs.
- **Compliance and procurement**: changes to terms, policies, tender pages and status pages.
- **AI agents (MCP)**: "tell me what changed on this page since yesterday".

### What each run checks

| Area | Details |
|---|---|
| **Uptime** | HTTP status, total response time and TTFB, DNS resolution and IPs, the full redirect chain (every hop and status), TLS certificate expiry date, days left, issuer and whether it is trusted. Failures are confirmed with a retry before anything is reported as down. |
| **Content change** | The whole page or one **region**, chosen by CSS selector (e.g. `#pricing`) or **XPath** (e.g. `//table[@id='plans']`). Text is extracted one block per line, with table cells separated by `\|`. The result is a line-based unified diff with +/− line counts and the change %. |
| **Noise filtering** | Built-in filter for dates, times, "5 minutes ago", Unix timestamps, UUIDs, long random tokens (CSRF/nonces) and copyright years. You can add your own regular expressions (`Visitors today: \d+`) and remove elements (nav, footer, ads, chat widgets) by CSS selector. Ignore patterns apply retroactively, because the raw text is stored. |
| **Structured extraction** | Prices (£ $ € ₹ ¥, ISO codes) with "£49 → £59" changes. A list of items by CSS selector (text or attribute, e.g. `data-sku`, `href`), reported as added or removed. |
| **Keywords** | Alert when "sold out", "discontinued" or `/only \d+ left/i` appears or disappears. |
| **SEO-critical** | Title, meta description, canonical, H1s, `lang`, final URL, meta robots and the `X-Robots-Tag` header. **noindex appearing is flagged as critical.** |
| **robots.txt and sitemap** | Rule changes (comments and whitespace ignored). Sitemap URLs added or removed (sitemap indexes and `.gz` supported; lastmod-only edits ignored). |
| **Visual diff** (browser mode) | A screenshot of the viewport, the full page or just the selected region, compared pixel by pixel (anti-aliasing ignored) against a % threshold. Dynamic areas can be masked. A diff image is saved for every visual change. |

### Alerts

Every alert carries a **short human-readable summary** and a **unified diff excerpt**:

```
Acme pricing: CHANGED — Text changed: +2 / -1 lines (12.5% of lines). NOINDEX APPEARED ("noindex, follow"): the page asks search engines to drop it.
title changed: "Pricing — Acme" -> "Pricing (new) — Acme". Keyword "sold out" appeared. Price changed: £49 -> £59.

@@ -2,3 +2,4 @@
 Basic | £19/mo
-Pro | £49/mo
+Pro | £59/mo
+Business | £199/mo
```

- **Webhook**: JSON POST to your URL (Zapier, Make, n8n, your API), retried with backoff.
- **Slack**: an incoming-webhook message with the summary and the diff.
- **E-mail**: one digest per run, sent through Apify's official [`apify/send-mail`](https://apify.com/apify/send-mail) Actor in your account. It uses a few seconds of your platform usage. You can also use Apify's built-in integrations (Slack, e-mail, Zapier, Make) on the Actor's runs.
- Pick which kinds of change alert you (`alertOn`). All changes are still recorded in the dataset and in the history.
- Downtime alerts fire once when a site goes down and once when it **recovers**, never on every run while it stays down. TLS alerts fire when the certificate crosses your threshold, and again at 7, 3, 1 and 0 days.

### Quick start

1. Add URLs under **URLs to monitor** (or per-page settings under **Advanced monitors**).
2. Optional: set a region (CSS/XPath), keywords, price tracking and alert channels.
3. Run once to save the **baseline**, then create a **Schedule** (e.g. hourly) with the same input.

#### Example input: competitor pricing table plus uptime

```json
{
  "urls": [{ "url": "https://www.example-shop.com/" }],
  "monitors": [
    {
      "url": "https://competitor.example/pricing",
      "name": "Competitor pricing",
      "selector": "#pricing-table",
      "trackPrices": true,
      "keywords": ["sold out", "limited time"],
      "excludeSelectors": [".countdown"]
    },
    {
      "url": "https://www.example-shop.com/landing",
      "name": "Landing page (visual)",
      "mode": "browser",
      "visualDiff": true,
      "visualThresholdPercent": 2,
      "visualMaskSelectors": [".carousel", "#chat-widget"]
    }
  ],
  "checkRobotsTxt": true,
  "checkSitemap": true,
  "slackWebhookUrl": "https://hooks.slack.com/services/XXX/YYY/ZZZ",
  "stateStoreName": "my-shop-monitors"
}
```

#### Output (one dataset row per URL per run)

```json
{
  "name": "Competitor pricing",
  "url": "https://competitor.example/pricing",
  "status": "up",
  "httpStatus": 200,
  "responseTimeMs": 412,
  "ttfbMs": 188,
  "redirectChain": [{ "url": "http://competitor.example/pricing", "status": 301 }],
  "tlsDaysToExpiry": 44,
  "changeStatus": "changed",
  "changeTypes": ["content", "extraction", "keywords"],
  "summary": "Competitor pricing: CHANGED — Text changed: +2 / -1 lines ... Price changed: £49 -> £59.",
  "diffExcerpt": "@@ -2,3 +2,4 @@\n Basic | £19/mo\n-Pro | £49/mo\n+Pro | £59/mo ...",
  "changes": { "text": {...}, "seo": [...], "keywords": [...], "prices": {...}, "items": {...}, "visual": {...}, "robotsTxt": {...}, "sitemap": {...} },
  "alertSent": true,
  "alertChannels": { "slack": "sent" },
  "chargedEvents": ["url-checked", "change-alert"]
}
```

Dataset views: **Overview**, **Changes and diffs**, **Uptime and TLS** and **SEO snapshot**. A `RUN_SUMMARY` record holds the run's counts and charged events.

### Statuses

| status | Meaning | Charged? |
|---|---|---|
| `up` | 2xx/3xx response; content compared | yes |
| `down` | 4xx/5xx, timeout, DNS or TLS failure (confirmed by retry) | yes (it is a monitoring result) |
| `blocked` | Bot protection, challenge page, WAF 403 or 429. **We never bypass it.** Allow-list the user agent `SiteVerdict/change-uptime-monitor`, or monitor a page you control. | no |
| `robots-disallowed` | robots.txt disallows the URL for `SiteVerdict` or `*` | no |
| `error` | Internal problem (e.g. browser crashed) | no |

`changeStatus` is one of `baseline` (first run), `no-change`, `changed` or `not-compared` (when the page was not up).

### State and history

Snapshots live in a **named key-value store** (`stateStoreName`, default `siteverdict-monitor`), so they survive between scheduled runs. Each monitor keeps:

- the last snapshot (raw region text, SEO fields, items, prices, keyword presence, robots/sitemap hashes, screenshot);
- its last up/down status and when that started;
- the last *N* changes (`historySize`, default 20), each with its summary and diff.

Use a different store name per client or project. `resetBaseline` starts a monitor fresh. A monitor's identity is its URL + mode + region. Changing the selector or XPath therefore starts a new history; editing ignore patterns, keywords or alerts does not.

### Pricing (pay per event)

- `url-checked`: one HTTP check ($0.002)
- `url-checked-browser`: one browser check with optional screenshot diff ($0.006)
- `change-alert`: once per monitor per run when an alert-worthy change is found ($0.01)

You are never charged for blocked, robots-disallowed or errored URLs. The run never exceeds your **Max total charge**. If the budget runs out before an alert, the change is *not* saved, so it is detected again next run. See PRICING.md for examples.

### FAQ

**Why didn't a change trigger an alert?** Check `changeTypes` against `alertOn`. Small text changes below `minChangePercent`, matches of ignore patterns, and elements removed by `excludeSelectors` are ignored on purpose.

**I get alerts for a ticker, counter or carousel.** Add an `excludeSelectors` entry (e.g. `.ticker`), an ignore pattern, or a `visualMaskSelectors` entry for screenshots.

**The page is built by JavaScript.** Use `"mode": "browser"`, with `waitForSelector` if needed.

**Does it log in, solve CAPTCHAs or rotate proxies?** No. It fetches public pages with an honest user agent. It respects robots.txt by default and stops at bot protection.

**Can I monitor JSON APIs or text files?** Yes. Non-HTML responses are compared as text (JSON is pretty-printed first, so the diff is per field).

**How fast?** HTTP checks take about 0.1–1 s per URL. Browser checks take about 3–8 s. Requests to the same site run one at a time, 1 s apart.

### Limits

- Up to 5 MB of HTML analysed per page. Stored text is capped at 300k characters. Diff excerpts are capped at 60 lines / 4,000 characters, and to 2,000 characters in history.
- Full-page screenshots are cut at `maxScreenshotHeight` (default 8,000 px).
- The sitemap check reads the sitemap file itself (or an index's list of child sitemaps), not every child sitemap.
- XPath runs on a static parse of the HTML (jsdom; scripts never execute). Use browser mode for JavaScript-built content.
- The TLS check reads the certificate presented on port 443 (or the URL's port) via SNI.
- Uptime checks run from Apify's cloud location. This is not a multi-region probe network.

### Compliance and sources

- Only public pages are fetched. The user agent is `SiteVerdict/change-uptime-monitor (+aman.thakran@bharatfare.com)` (browser mode adds it to Chromium's UA). robots.txt is respected by default; turn this off only for sites you own or are authorised to monitor. Follow each site's terms of service. Some sites (e.g. example.com) ask not to be used for monitoring.
- Bot protection, CAPTCHAs, logins and paywalls are **never** bypassed. Such pages are reported as `blocked` and are not charged.
- No personal data is collected on purpose. If a monitored page shows personal data, it may appear in snapshots and diffs. Under UK GDPR you are the controller of what you choose to monitor and store. Use regions and exclusions to avoid it, and do not monitor pages about private individuals.
- Webhook and Slack URLs are secret inputs (stored encrypted) and are never logged.
- This tool provides technical monitoring information, not legal advice (e.g. on terms changes).
- **Where data is kept (please read):** to compare runs, every run saves each monitored page's state in a **named** key-value store in your account, `siteverdict-monitor` by default (input `stateStoreName`). The state includes the watched region's text (capped), its hash, SEO fields, extracted items/prices, robots.txt/sitemap snapshots, a short change history and, when screenshots are on, the latest screenshot. **Apify keeps named stores until you delete them**, so delete the store (or use a new name) when you stop monitoring a page.
- **Per-run results** (dataset rows and reports) go to the run's default dataset and key-value store, which are unnamed. How long Apify keeps them is set by Apify and your plan, not by SiteVerdict ([Apify data retention](https://docs.apify.com/platform/storage/usage#data-retention)).
- **What SiteVerdict keeps:** nothing. The Actor runs in your Apify account and sends no data to SiteVerdict; our contact address appears only in the User-Agent sent to the sites it checks.

# Changelog

This Actor's version history is a separate document: https://apify.com/siteverdict/siteverdict-change-uptime-monitor/changelog.md

# Actor input Schema

## `urls` (type: `array`):

Pages to check on every run. All settings below apply to each of them. For per-page settings (different selector, keywords, browser mode) use 'Advanced monitors'.

## `monitors` (type: `array`):

Optional JSON list of monitors with their own settings, which override the global ones. Fields: url (required), name, id, mode (http|browser), selector, xpath, excludeSelectors, ignorePatterns, useDefaultIgnorePatterns, minChangePercent, keywords, keywordAlertOn, extractSelector, extractAttribute, trackPrices, seoChecks, checkRobotsTxt, checkSitemap, sitemapUrl, visualDiff, visualThresholdPercent, visualMaskSelectors, fullPageScreenshot, waitForSelector, waitAfterLoadMs, slowResponseMs, tlsWarnDays, alertOn. Example: \[{"url": "https://shop.example/pricing", "name": "Pricing", "selector": "#pricing-table", "trackPrices": true, "keywords": \["sold out"]}]

## `mode` (type: `string`):

'http' fetches the HTML directly (fast, cheapest, works for most sites). 'browser' renders the page in headless Chromium (needed for JavaScript-built content and screenshot diffs) and is billed as a browser check.

## `contentSelector` (type: `string`):

Only compare the text inside elements matching this CSS selector, e.g. '#pricing', 'main article', '.product-price'. Empty = the whole page body.

## `contentXPath` (type: `string`):

Alternative to the CSS selector, e.g. //table\[@id='pricing'] or //div\[contains(@class,'status')]. Attribute and text() results are supported. Ignored when a CSS selector is set.

## `excludeSelectors` (type: `array`):

Elements removed before comparing, e.g. nav, footer, .cookie-banner, .ad, #live-chat. Great for menus, carousels and counters.

## `ignorePatterns` (type: `array`):

Text matching these regular expressions is masked before comparing, e.g. 'Visitors today: \d+' or '/order #\d+/i'. Invalid regular expressions are treated as plain text.

## `useDefaultIgnorePatterns` (type: `boolean`):

Built-in noise filter: dates, times, '5 minutes ago', Unix timestamps, UUIDs, long random tokens and copyright years are masked so they never trigger an alert.

## `minChangePercent` (type: `number`):

Ignore text changes smaller than this share of lines (0 = any change counts). Example: 5 ignores small edits on long pages.

## `keywords` (type: `array`):

Alert when these words appear or disappear in the monitored region, e.g. 'sold out', 'in stock', 'discontinued'. Case-insensitive; '/regex/i' is also accepted.

## `keywordAlertOn` (type: `string`):

Alert when a keyword appears, disappears, or both.

## `trackPrices` (type: `boolean`):

Extract price-like values (£19, $1,299.99, EUR 45, 30 €) from the region and report each price change ('£49 -> £59').

## `extractSelector` (type: `string`):

Extract one item per matching element (e.g. '.product-card', 'table#plans tr') and report items added or removed. Good for product lists, job lists, plans.

## `extractAttribute` (type: `string`):

Use this attribute instead of the element text, e.g. 'href', 'data-sku', 'content'.

## `seoChecks` (type: `boolean`):

Track title, meta description, canonical, H1, lang, final URL and robots directives (meta robots + X-Robots-Tag). 'noindex' appearing is flagged as critical.

## `checkRobotsTxt` (type: `boolean`):

Alert when the site's robots.txt rules change (comments and whitespace are ignored).

## `checkSitemap` (type: `boolean`):

Alert when URLs are added to or removed from the sitemap (from robots.txt 'Sitemap:' or /sitemap.xml). lastmod-only changes are ignored.

## `sitemapUrl` (type: `string`):

Override the sitemap location, e.g. https://example.com/sitemap_index.xml. .gz sitemaps are supported.

## `visualDiff` (type: `boolean`):

Take a screenshot each run (of the CSS selector region if set, otherwise the viewport or full page) and compare pixels with the previous one. Turns on browser mode.

## `visualThresholdPercent` (type: `number`):

Alert only when at least this share of pixels differs.

## `visualMaskSelectors` (type: `array`):

Areas painted over before the screenshot is compared, e.g. carousels, ads, live chat, clocks.

## `fullPageScreenshot` (type: `boolean`):

Capture the whole page instead of the first screen (capped at 'Max screenshot height').

## `viewportWidth` (type: `integer`):

Browser window width. Use 390 for a phone-sized check.

## `viewportHeight` (type: `integer`):

Browser window height.

## `maxScreenshotHeight` (type: `integer`):

Full-page screenshots are cut at this height to keep runs fast.

## `waitForSelector` (type: `string`):

Wait until this element exists before reading the page, e.g. '.price' on JavaScript-built pages.

## `waitAfterLoadMs` (type: `integer`):

Additional settle time after the page has loaded.

## `slowResponseMs` (type: `integer`):

Responses slower than this are flagged as 'slow' (alerted only if 'slow' is selected in 'Alert on').

## `tlsWarnDays` (type: `integer`):

Alert when the HTTPS certificate expires within this many days (again at 7, 3, 1 and 0 days), or when it is not trusted.

## `retryOnFailure` (type: `integer`):

Re-check a URL this many times (2 s, 4 s apart) before reporting it down, to avoid false downtime alerts. Blocks are never retried.

## `timeoutSecs` (type: `integer`):

A page that does not respond within this time is reported as down (TIMEOUT).

## `alertOn` (type: `array`):

Which kinds of change send an alert (and are billed as a change alert). All changes are still listed in the dataset.

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

We POST a JSON alert (summary, diff excerpt, change details) here for every alert, e.g. a Zapier/Make/n8n hook or your own endpoint. Stored encrypted.

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

https://hooks.slack.com/services/... — posts a short message with the change summary and diff excerpt. Stored encrypted.

## `emailTo` (type: `string`):

One digest e-mail per run with all alerts, sent through Apify's official 'apify/send-mail' Actor in your account (it uses a few seconds of your platform usage). Leave empty to disable.

## `stateStoreName` (type: `string`):

Named key-value store that keeps the last snapshot and change history of each monitor between runs. Use a different name per project/client to keep histories apart.

## `historySize` (type: `integer`):

How many past changes are kept per monitor in the state store.

## `resetBaseline` (type: `boolean`):

Ignore saved snapshots and start fresh this run (history is cleared for the monitors in this run).

## `respectRobotsTxt` (type: `boolean`):

Skip (and do not charge for) URLs that robots.txt disallows for 'SiteVerdict' or '\*'. Only turn off for sites you own or are authorised to monitor.

## `maxConcurrency` (type: `integer`):

Checks run in parallel across different sites; requests to the same site are always sequential with a 1 s gap.

## `maxUrls` (type: `integer`):

Safety cap on monitors checked in one run. The run also never exceeds your 'Max total charge'.

## Actor input object example

```json
{
  "urls": [
    {
      "url": "https://apify.com/"
    }
  ],
  "monitors": [],
  "mode": "http",
  "excludeSelectors": [
    "nav",
    "footer",
    ".cookie-banner"
  ],
  "useDefaultIgnorePatterns": true,
  "minChangePercent": 0,
  "keywordAlertOn": "both",
  "trackPrices": false,
  "seoChecks": true,
  "checkRobotsTxt": false,
  "checkSitemap": false,
  "visualDiff": false,
  "visualThresholdPercent": 1,
  "fullPageScreenshot": false,
  "viewportWidth": 1366,
  "viewportHeight": 900,
  "maxScreenshotHeight": 8000,
  "waitAfterLoadMs": 1000,
  "slowResponseMs": 3000,
  "tlsWarnDays": 14,
  "retryOnFailure": 1,
  "timeoutSecs": 30,
  "alertOn": [
    "content",
    "seo",
    "keywords",
    "extraction",
    "visual",
    "robots",
    "sitemap",
    "downtime",
    "recovery",
    "tls"
  ],
  "stateStoreName": "siteverdict-monitor",
  "historySize": 20,
  "resetBaseline": false,
  "respectRobotsTxt": true,
  "maxConcurrency": 3,
  "maxUrls": 500
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `files` (type: `string`):

No description

# 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 = {
    "urls": [
        {
            "url": "https://apify.com/"
        }
    ],
    "monitors": [],
    "excludeSelectors": [
        "nav",
        "footer",
        ".cookie-banner"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("siteverdict/siteverdict-change-uptime-monitor").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 = {
    "urls": [{ "url": "https://apify.com/" }],
    "monitors": [],
    "excludeSelectors": [
        "nav",
        "footer",
        ".cookie-banner",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("siteverdict/siteverdict-change-uptime-monitor").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 '{
  "urls": [
    {
      "url": "https://apify.com/"
    }
  ],
  "monitors": [],
  "excludeSelectors": [
    "nav",
    "footer",
    ".cookie-banner"
  ]
}' |
apify call siteverdict/siteverdict-change-uptime-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,siteverdict/siteverdict-change-uptime-monitor"
        }
    }
}
```

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/qOUmbuVFhdGxky34M/builds/aAy3He3sY4W1EDohS/openapi.json
