# SaaS Changelog Monitor — Release Notes to JSON | $0.01 (`devsef/saas-changelog-monitor`) Actor

Monitor competitor changelogs and product updates automatically. Get only new or changed release entries as structured JSON: title, version, date, categories, summary. Change detection built in — ideal for competitor monitoring and product intelligence. $0.01 per check.

- **URL**: https://apify.com/devsef/saas-changelog-monitor.md
- **Developed by:** [Steffano van Hoven](https://apify.com/devsef) (community)
- **Categories:** AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 changelog checkeds

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

## SaaS Changelog Monitor

### What does SaaS Changelog Monitor do?

**SaaS Changelog Monitor watches any list of public changelog pages and returns new or changed release entries as clean, structured JSON.** Point it at pages like [github.blog/changelog](https://github.blog/changelog/) or [tailscale.com/changelog](https://tailscale.com/changelog), and it extracts every release entry — title, version, date, categories and a summary. **Change detection persists between runs**: schedule it daily and you only get what's new since the last check.

Because it runs on the Apify platform, you get API access, scheduling, webhooks, integrations (Slack, Make, n8n, Zapier) and monitoring out of the box.

### Why use SaaS Changelog Monitor?

- **Competitive intelligence** — know the moment a competitor ships something new, without visiting dozens of changelog pages by hand.
- **Product & engineering teams** — track upstream dependencies and vendors for breaking changes and security fixes.
- **VCs & analysts** — monitor shipping velocity across a portfolio or market.
- **AI agents & RAG pipelines** — structured changelog entries are ideal input for agents that answer "what changed at X?". Categories are normalized (Added, Fixed, Improved, Security, …) so you can filter programmatically.
- **Predictable flat pricing** — you pay per source checked, never per entry. Competitors charge per page or per entry, which gets expensive fast.

### How to use SaaS Changelog Monitor

1. Open the Actor and go to the **Input** tab.
2. Paste the changelog URLs you want to monitor (up to 25 per run).
3. Click **Start**. The first run returns all current entries (your baseline).
4. Optionally, create a **Schedule** (e.g. daily). Subsequent runs return only new or changed entries.
5. Connect a webhook or integration to push new entries to Slack, email or your own API.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `changelogUrls` | array | – (required) | Public changelog/release-notes URLs to check. Hard cap: 25 per run. |
| `maxEntriesPerSource` | integer | 50 | Maximum entries parsed per source (cap: 200). |
| `onlyNewEntries` | boolean | `true` | Return only entries that are new or changed since the previous run. Set to `false` to always return everything. |

```json
{
    "changelogUrls": [
        "https://github.blog/changelog/",
        "https://tailscale.com/changelog"
    ],
    "onlyNewEntries": true
}
````

### Output

One dataset item per (new) changelog entry. You can download the dataset in various formats such as JSON, HTML, CSV, or Excel.

```json
{
    "sourceUrl": "https://tailscale.com/changelog",
    "title": "Tailscale v1.98.4",
    "version": "v1.98.4",
    "date": "2026-05-28T00:00:00.000Z",
    "categories": ["Fixed"],
    "summary": "An issue causing a deadlock when processing peer changes...",
    "entryHash": "3f9a…",
    "isNew": true,
    "detectedAt": "2026-07-04T20:55:41.141Z"
}
```

A per-run report is stored in the key-value store under `SUMMARY` (per source: status, entries found, new entries).

| Field | Description |
|---|---|
| `sourceUrl` | The changelog page this entry came from |
| `title` | Entry title |
| `version` | Detected version (e.g. `v1.98.4`), or `null` |
| `date` | Entry date in ISO 8601, or `null` if not detected |
| `categories` | Normalized labels: Added, Fixed, Improved, Changed, Security, Deprecated, Removed, New |
| `summary` | Plain-text summary (max 500 characters) |
| `entryHash` | Stable hash used for change detection |
| `isNew` | Whether this entry is new/changed vs. the previous run |

### How much does it cost to monitor changelogs?

Pricing is a **flat fee per source checked** — all entries included, no per-entry fees. A source is only charged when it is successfully checked and at least one entry is recognized; failed or unreadable pages are never charged. A run where nothing changed is still a successful check (that's the monitoring service), so it is charged.

Example: monitoring 10 changelogs daily costs 10 × the per-source fee per day — regardless of how many entries those pages contain. Comparable actors charge per page or per entry, which typically costs 5-10× more for the same monitoring job.

### Tips

- **Schedule daily or weekly** and set `onlyNewEntries: true` (default) — you'll only pay for the check and only receive what's new.
- The parser handles most common changelog layouts (heading-based and container-based). **JavaScript-rendered changelogs** that ship no HTML content are not supported; those sources are reported in `SUMMARY` and never charged.
- Use `maxEntriesPerSource` to bound very long changelog pages.

### FAQ & disclaimers

**Is it legal to scrape changelogs?** This Actor only reads public product-update pages — marketing content intended for broad distribution. It collects no personal data. Please still make sure your use complies with the target sites' terms.

**A source returns 0 entries — why?** The page is likely rendered client-side with JavaScript or uses a very unusual layout. You are not charged for such sources. Open an issue via the **Issues** tab and we'll look into adding support.

**Found a bug or missing a feature?** Please report it via the Issues tab — feedback is usually answered within a few days.

# Actor input Schema

## `changelogUrls` (type: `array`):

List of SaaS changelog page URLs to check (http/https only). Up to 25 URLs per run — extra URLs beyond that are ignored with a warning.

## `maxEntriesPerSource` (type: `integer`):

Maximum number of release entries to extract per changelog URL (hard cap: 200).

## `onlyNewEntries` (type: `boolean`):

When enabled, only entries that are new or changed since the previous run of the same URL are returned. On the very first run of a URL, everything found counts as new (baseline). When disabled, every entry found is returned on every run, each flagged with isNew.

## Actor input object example

```json
{
  "changelogUrls": [
    "https://github.blog/changelog/",
    "https://tailscale.com/changelog"
  ],
  "maxEntriesPerSource": 50,
  "onlyNewEntries": true
}
```

# Actor output Schema

## `entries` (type: `string`):

No description

## `summary` (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 = {
    "changelogUrls": [
        "https://github.blog/changelog/",
        "https://tailscale.com/changelog"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("devsef/saas-changelog-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 = { "changelogUrls": [
        "https://github.blog/changelog/",
        "https://tailscale.com/changelog",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("devsef/saas-changelog-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 '{
  "changelogUrls": [
    "https://github.blog/changelog/",
    "https://tailscale.com/changelog"
  ]
}' |
apify call devsef/saas-changelog-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=devsef/saas-changelog-monitor",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "SaaS Changelog Monitor — Release Notes to JSON | $0.01",
        "description": "Monitor competitor changelogs and product updates automatically. Get only new or changed release entries as structured JSON: title, version, date, categories, summary. Change detection built in — ideal for competitor monitoring and product intelligence. $0.01 per check.",
        "version": "0.0",
        "x-build-id": "B1sQS3RrcDJu23GGe"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/devsef~saas-changelog-monitor/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-devsef-saas-changelog-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/devsef~saas-changelog-monitor/runs": {
            "post": {
                "operationId": "runs-sync-devsef-saas-changelog-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/devsef~saas-changelog-monitor/run-sync": {
            "post": {
                "operationId": "run-sync-devsef-saas-changelog-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": [
                    "changelogUrls"
                ],
                "properties": {
                    "changelogUrls": {
                        "title": "Changelog URLs",
                        "type": "array",
                        "description": "List of SaaS changelog page URLs to check (http/https only). Up to 25 URLs per run — extra URLs beyond that are ignored with a warning.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "maxEntriesPerSource": {
                        "title": "Max entries per source",
                        "minimum": 1,
                        "maximum": 200,
                        "type": "integer",
                        "description": "Maximum number of release entries to extract per changelog URL (hard cap: 200).",
                        "default": 50
                    },
                    "onlyNewEntries": {
                        "title": "Only new entries",
                        "type": "boolean",
                        "description": "When enabled, only entries that are new or changed since the previous run of the same URL are returned. On the very first run of a URL, everything found counts as new (baseline). When disabled, every entry found is returned on every run, each flagged with isNew.",
                        "default": true
                    }
                }
            },
            "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
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
