# Third-Party OpenAPI Change Monitor (`monema/third-party-openapi-change-monitor`) Actor

Monitor public OpenAPI and Swagger specifications for structural changes. Get persistent baselines plus review-oriented records for removed operations, new required inputs, response/schema changes, and additive updates.

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

## Pricing

$5.00 / 1,000 successful openapi specification checks

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## Third-Party OpenAPI Change Monitor

Monitor public **OpenAPI 3.x and Swagger 2.0 specifications** for structural changes between runs. Each specification URL produces one dataset record with a persistent hash, API-size summary, and review-oriented change list.

This Actor is designed for integration, platform, and reliability teams that depend on APIs they do not control. It can surface removed paths or operations, new required request inputs, changed response/schema definitions, security-requirement changes, and additive API updates before a human reviews the source specification.

It does **not** guarantee that a change will or will not break an integration. `potentially_breaking` is a conservative heuristic, not a compatibility proof.

> **Independent community Actor:** This Actor is not affiliated with or endorsed by the OpenAPI Initiative, SmartBear/Swagger, or any API provider whose public specification you monitor. You are responsible for choosing lawful public URLs and a polling schedule that respects each provider's terms and infrastructure.

### Input

| Field | Required | Description |
|---|---:|---|
| `monitorId` | Yes | Stable ID for one independent watchlist, for example `production-vendor-apis`. |
| `specUrls` | Yes | 1–50 public HTTP(S) OpenAPI or Swagger URLs. JSON and YAML are supported. Duplicate URLs are checked once. |
| `ignoreJsonPointers` | No | Exact RFC 6901 paths removed before hashing and comparison, useful for volatile metadata such as `/info/x-generated-at`. |
| `timeoutSeconds` | No | Per-URL request timeout, 5–120 seconds (default `30`). |
| `maxChanges` | No | Maximum detailed entries in each output `changes` array (default `200`, maximum `500`). Summary counts still cover every detected change. |
| `maxConcurrency` | No | Concurrent URL checks, 1–10 (default `5`). |

The default input checks the public Swagger Petstore specification, so a default run completes without credentials. Replace the default `monitorId` and URL before creating a production schedule.

Example:

```json
{
  "monitorId": "production-vendor-apis",
  "specUrls": [
    "https://petstore3.swagger.io/api/v3/openapi.json"
  ],
  "ignoreJsonPointers": [
    "/info/x-generated-at"
  ],
  "maxChanges": 200
}
````

### Output

Every URL emits one dataset record.

| `status` | Meaning |
|---|---|
| `initial_snapshot` | The first valid parsed specification was stored. |
| `unchanged` | The normalized parsed document matches the previous successful check. |
| `changed` | The parsed document changed; counts and detailed structural change records are included. |
| `fetch_error` | The URL was invalid, blocked, unavailable, too large, or returned an HTTP/network error. Prior state is preserved. |
| `parse_error` | The response was not a valid OpenAPI/Swagger JSON or YAML object. Prior state is preserved. |

Common fields include:

- `url`, `final_url`, `checked_at`, `http_status`, and `content_type`
- `spec_format`, `openapi_version`, `api_title`, and `api_version`
- `path_count`, `operation_count`, and `component_schema_count`
- `content_hash`, `previous_hash`, and `state_store_mode`
- `change_count`, severity counts, `change_summary`, `changes`, and `changes_truncated`

A detailed change record contains `severity`, `type`, `location`, `message`, and compact `before`/`after` values where useful.

#### Severity labels

- `potentially_breaking` — conservative signals such as removed operations, new required parameters, or removed schema properties.
- `review` — changes whose compatibility depends on client behavior, such as response schemas, operation IDs, server lists, or security requirements.
- `additive` — new paths, operations, optional parameters, responses, or optional properties.
- `informational` — version, deprecation, or unclassified document metadata changes.

### Persistence and scheduling

Successful parsed snapshots are stored in the named key-value store `third-party-openapi-change-monitor-state-v1`. A named store is used because the default run store is run-scoped in Apify Cloud. Snapshot keys are derived from `monitorId` + URL, so one stable `monitorId` retains history while different watchlists remain separate.

Schedule the Actor hourly, daily, or weekly and route `changed` records through Apify integrations or webhooks. The Actor itself does not send external messages.

### Charging behavior

The code emits one `openapi_spec_check` charge event only after a specification is successfully fetched, parsed, compared, and stored. Invalid URLs, fetch failures, and parse failures are not charged. Refer to the live Actor listing for the current event price after publication.

### Security and data handling

- Only public HTTP(S) URLs are supported.
- Private, loopback, link-local, multicast, and reserved destinations are blocked, including redirect targets.
- Credentials embedded in URLs are rejected, and the Actor has no input field for authorization headers or secrets.
- Parsed public specifications are stored in the named key-value store to enable later comparisons.
- Responses larger than 5 MB are rejected.
- Cyclic YAML aliases, nesting beyond 100 levels, and documents exceeding 200,000 parsed nodes are rejected to keep parsing bounded.

### Local development

```bash
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/python smoke_test.py
```

The smoke test runs the Actor three times against a deterministic local fixture and verifies `initial_snapshot` → `unchanged` → `changed`, including removed-operation and new-required-parameter signals. Private URL access is enabled only inside that local test process.

### Limitations

- External `$ref` documents are not fetched or dereferenced. Changes to the `$ref` value are seen, while changes inside a separately hosted referenced document require adding that document as another `specUrls` entry if it is itself a complete OpenAPI document.
- The Actor compares published specifications; it does not probe live endpoints or prove that implementation and documentation match.
- Classification is intentionally conservative and cannot replace contract tests, generated-client tests, source changelogs, or engineering review.
- Very large change sets are summarized and truncated according to `maxChanges`.
- Authenticated or private specifications are intentionally unsupported in this public Actor design.

# Actor input Schema

## `monitorId` (type: `string`):

A stable, unique ID for this watchlist, such as production-vendor-apis. It keeps history separate from other watchlists.

## `specUrls` (type: `array`):

Public HTTP(S) URLs returning OpenAPI or Swagger as JSON or YAML. Duplicate URLs are checked once.

## `ignoreJsonPointers` (type: `array`):

Optional exact RFC 6901 paths removed before comparison, for example /info/x-generated-at. Each value must start with /.

## `timeoutSeconds` (type: `integer`):

Per-specification network timeout.

## `maxChanges` (type: `integer`):

Summary counts include every detected change; the changes array is capped to keep dataset records manageable.

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

Number of specification URLs fetched in parallel.

## Actor input object example

```json
{
  "monitorId": "qa-default",
  "specUrls": [
    "https://petstore3.swagger.io/api/v3/openapi.json"
  ],
  "ignoreJsonPointers": [],
  "timeoutSeconds": 30,
  "maxChanges": 200,
  "maxConcurrency": 5
}
```

# 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 = {
    "specUrls": [
        "https://petstore3.swagger.io/api/v3/openapi.json"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("monema/third-party-openapi-change-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 = { "specUrls": ["https://petstore3.swagger.io/api/v3/openapi.json"] }

# Run the Actor and wait for it to finish
run = client.actor("monema/third-party-openapi-change-monitor").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "specUrls": [
    "https://petstore3.swagger.io/api/v3/openapi.json"
  ]
}' |
apify call monema/third-party-openapi-change-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=monema/third-party-openapi-change-monitor",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Third-Party OpenAPI Change Monitor",
        "description": "Monitor public OpenAPI and Swagger specifications for structural changes. Get persistent baselines plus review-oriented records for removed operations, new required inputs, response/schema changes, and additive updates.",
        "version": "0.1",
        "x-build-id": "qKM7zPqwvaA6sysp1"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/monema~third-party-openapi-change-monitor/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-monema-third-party-openapi-change-monitor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/monema~third-party-openapi-change-monitor/runs": {
            "post": {
                "operationId": "runs-sync-monema-third-party-openapi-change-monitor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/monema~third-party-openapi-change-monitor/run-sync": {
            "post": {
                "operationId": "run-sync-monema-third-party-openapi-change-monitor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "monitorId",
                    "specUrls"
                ],
                "properties": {
                    "monitorId": {
                        "title": "Monitor ID",
                        "minLength": 1,
                        "maxLength": 200,
                        "type": "string",
                        "description": "A stable, unique ID for this watchlist, such as production-vendor-apis. It keeps history separate from other watchlists.",
                        "default": "qa-default"
                    },
                    "specUrls": {
                        "title": "OpenAPI specification URLs",
                        "minItems": 1,
                        "maxItems": 50,
                        "type": "array",
                        "description": "Public HTTP(S) URLs returning OpenAPI or Swagger as JSON or YAML. Duplicate URLs are checked once.",
                        "items": {
                            "type": "string"
                        },
                        "default": [
                            "https://petstore3.swagger.io/api/v3/openapi.json"
                        ]
                    },
                    "ignoreJsonPointers": {
                        "title": "Ignore JSON Pointers",
                        "type": "array",
                        "description": "Optional exact RFC 6901 paths removed before comparison, for example /info/x-generated-at. Each value must start with /.",
                        "items": {
                            "type": "string"
                        },
                        "default": []
                    },
                    "timeoutSeconds": {
                        "title": "Request timeout (seconds)",
                        "minimum": 5,
                        "maximum": 120,
                        "type": "integer",
                        "description": "Per-specification network timeout.",
                        "default": 30
                    },
                    "maxChanges": {
                        "title": "Maximum detailed changes per specification",
                        "minimum": 1,
                        "maximum": 500,
                        "type": "integer",
                        "description": "Summary counts include every detected change; the changes array is capped to keep dataset records manageable.",
                        "default": 200
                    },
                    "maxConcurrency": {
                        "title": "Maximum concurrent requests",
                        "minimum": 1,
                        "maximum": 10,
                        "type": "integer",
                        "description": "Number of specification URLs fetched in parallel.",
                        "default": 5
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
