# Changelog & Release Notes Scraper (`scrapyx/changelog-releases-scraper`) Actor

Tracks release notes, changelogs and status incidents across 59 curated sources — AWS, Google Cloud, Azure, Cloudflare, GitHub, Anthropic, Stripe and more — plus any GitHub repo, npm/PyPI/crates package, or RSS/Atom feed. Every vendor lands in one normalised row shape with one UTC date column.

- **URL**: https://apify.com/scrapyx/changelog-releases-scraper.md
- **Developed by:** [Ibnu Adzim](https://apify.com/scrapyx) (community)
- **Categories:** Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.84 / 1,000 results

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

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — 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

## Changelog & Release Notes Scraper

Track what shipped — across your whole stack, in one dataset.

This actor pulls **release notes, changelogs, product updates and status
incidents** from 59 curated sources (AWS, Google Cloud, Azure, Cloudflare,
GitHub, Anthropic, Stripe, Shopify, Kubernetes, Linear, Postgres, Chrome…),
plus **any** GitHub repository, package registry or feed you point it at.

Public data only. No account, no API key, no login, and nothing to configure to
get started.

### Why use this actor

- **59 vendors, one row shape.** An AWS bulletin, a GitHub release, an npm
  version bump and a status incident all come back with the same fields and the
  same UTC date column — so you can sort, diff and alert across vendors without
  writing per-vendor glue.
- **Not limited to the catalog.** Any public GitHub repo (`github:redis/redis`),
  any package (`npm:react`, `pypi:django`, `crates:serde`, `gem:rails`,
  `nuget:…`, `packagist:…`), any RSS/Atom/JSON feed URL — mixed freely in one run.
- **Pick a stack in one word.** `preset: "cloud-providers"` or `"ai-platforms"`
  or `"databases"` instead of listing sources by hand.
- **Filter where it's cheap.** By date (`sinceDate`), by keyword across title and
  body, and by count per source.
- **Every source reports back.** Each one gets a summary row saying how many
  entries it carried, how many survived your filters, and why — and a source that
  fails produces an error row rather than vanishing.
- **Version-aware.** Releases carry a parsed `version` and an `isPrerelease`
  flag that understands semver (`1.0.0-rc1`), Python/PEP 440 (`1.4.0a2`) and
  vendor tags (`1.17.0-alpha20260827`).

### How it works

1. You name the sources you want — catalog keys, a preset, GitHub repos,
   packages, or plain feed URLs.
2. The actor fetches each one and reads whichever format that vendor publishes.
3. Entries are normalised to one shape, filtered by your date/keyword rules, and
   written to your dataset newest-first.

You don't manage credentials, scrapers, or browsers.

### Input

```json
{
  "preset": "",
  "sources": ["aws", "cloudflare", "github", "anthropic", "github:redis/redis"],
  "maxItemsPerSource": 50,
  "sinceDate": "2026-08-01",
  "keywords": [],
  "includeContent": true,
  "maxConcurrency": 8
}
```

| Field | Type | Description |
|---|---|---|
| `preset` | string | Select a whole family at once: `all`, `cloud-providers`, `ai-platforms`, `devtools`, `databases`, `languages`, `browsers-mobile`, `status-pages`, `saas`, `security`. Combines with `sources`. |
| `sources` | array | Catalog key (`aws`), `github:owner/repo`, `npm:react` / `pypi:django` / `crates:serde` / `gem:rails` / `nuget:Pkg` / `packagist:vendor/name` / `hashicorp:vault` / `eol:nodejs`, or any feed URL. |
| `maxItemsPerSource` | integer | Newest entries per source. `0` = everything. Default `50`. |
| `sinceDate` | string | Keep entries on or after this date (`YYYY-MM-DD` or ISO 8601). Undated entries are kept. |
| `keywords` | array | Keep only entries mentioning one of these (case-insensitive) in title, body, version or categories. |
| `includeContent` | boolean | Include full body and the untouched upstream object. Turn off for a compact index. Default `true`. |
| `githubToken` | string | Optional. Unlocks full release history for `github:` sources (see below). |
| `maxConcurrency` | integer | Requests in flight at once. Default `8`. |
| `minRequestInterval` | number | Seconds between request starts. Default `0`. |

#### About `githubToken`

Without a token, `github:` sources read the repo's public releases feed: no
credentials needed, but it serves **the 10 most recent releases** and cannot
page further. With a read-only token they switch to the full API — complete
release history, plus draft/pre-release flags and asset lists — and the hourly
request allowance rises from 60 to 5,000. Optional, and only affects `github:`
sources.

### Output

Two record types share the dataset, told apart by `recordType`.

#### `ENTRY` — one row per release / changelog post / incident

```json
{
  "_input": "cloudflare",
  "_source": "S1-feed",
  "_scrapedAt": "2026-08-31T16:16:17Z",
  "recordType": "ENTRY",
  "sourceKey": "cloudflare",
  "sourceName": "Cloudflare changelog",
  "vendor": "Cloudflare",
  "adapter": "feed",
  "entryRank": 1,
  "title": "Browser Run - Crawl endpoint now respects the Content Signals `use` directive",
  "url": "https://developers.cloudflare.com/changelog/post/2026-08-31-crawl-content-use/",
  "publishedAt": "2026-08-31T00:00:00Z",
  "author": null,
  "summary": "The /crawl endpoint now respects the use directive of the Content Signals standard ...",
  "contentHtml": "<p>The <a href=\"...\"><code>/crawl</code></a> endpoint now respects ... truncated",
  "contentText": "The /crawl endpoint now respects the use directive ... truncated",
  "categories": ["Browser Run"],
  "version": null,
  "isPrerelease": false,
  "raw": { "...": "the upstream item, preserved verbatim" }
}
```

A GitHub release row from the same run:

```json
{
  "_input": "github:redis/redis",
  "_source": "S1-github-atom",
  "recordType": "ENTRY",
  "sourceName": "redis/redis releases",
  "vendor": "redis",
  "adapter": "github",
  "title": "8.10.1",
  "url": "https://github.com/redis/redis/releases/tag/8.10.1",
  "publishedAt": "2026-08-17T16:46:02Z",
  "author": "YaacovHazan",
  "summary": "Update urgency: SECURITY: There are security fixes in the release. Security fixes (CVE-2026-62356) ...",
  "version": "8.10.1",
  "isPrerelease": false
}
```

| Field | Type | Description |
|---|---|---|
| `sourceKey` / `sourceName` / `vendor` | string | Which source this came from. |
| `adapter` | string | How it was read: `feed`, `github`, `registry`, `markdown`. |
| `title` | string | Entry headline, or the version/date for release feeds. |
| `url` | string | Link to the entry. |
| `publishedAt` / `updatedAt` | string | UTC ISO 8601. Normalised from every upstream date format. |
| `author` | string | Where the source provides one. |
| `summary` | string | Short plain-text summary; falls back to the opening of the body. |
| `contentHtml` / `contentText` | string | Full body, when `includeContent` is on. |
| `categories` | array | Upstream tags/labels. |
| `version` | string | Parsed version. Authoritative for releases and packages; best-effort for prose titles. |
| `isPrerelease` | boolean | Alpha/beta/rc/experimental. |
| `entryRank` | integer | Position within this source, newest first. |
| `raw` | object | The upstream item verbatim, so nothing is lost. |

#### `SOURCE_SUMMARY` — one row per source

```json
{
  "recordType": "SOURCE_SUMMARY",
  "sourceKey": "cloudflare",
  "feedKind": "rss",
  "feedTitle": "Cloudflare changelogs",
  "entriesAvailable": 1186,
  "entriesReturned": 2,
  "requestsMade": 1,
  "newestEntry": "2026-08-31T00:00:00Z",
  "oldestEntry": "2026-08-30T00:00:00Z",
  "filtersApplied": { "sinceDate": null, "keywords": [], "maxItemsPerSource": 2 },
  "notes": ["truncated_to_2: 1186 entries matched; raise `maxItemsPerSource` for the rest."]
}
```

`notes` is the honesty channel — it tells you *why* a count is what it is
(truncated, filtered, or a known upstream limitation) instead of leaving you to
guess.

#### `ERROR` — one row per source that failed

Every source you ask for produces at least one row, so a source going missing is
visible in the data rather than as an absence:

```json
{
  "_input": "totally-bogus-source",
  "_source": "none",
  "recordType": "ERROR",
  "_error": "unknown_source",
  "_errorDetail": "'totally-bogus-source' is not a catalog key, not a `github:owner/repo`, not a `<registry>:<package>` token and not a URL."
}
```

### Known limits

- **GitHub without a token gives 10 releases per repo.** The public feed offers
  no pagination. Set `githubToken` for full history. Flagged in `notes`.
- **NuGet publishes no dates.** Its version index is a bare list, so
  `publishedAt` is empty for those rows and `sinceDate` can't filter them.
  Flagged in `notes` rather than silently returning nothing.
- **`version` on prose entries is a guess.** For releases and packages it comes
  from the actual tag; for a headline like "Announcing 2.0" it is parsed from
  text and may be absent or approximate.
- **Some feeds are large.** Oracle Cloud carries 2,960 entries, Vercel 1,532,
  npm packages several thousand versions. `preset: "all"` with
  `maxItemsPerSource: 0` produces a very big dataset — keep the per-source cap on.
- **Catalog sources can move.** Vendors relocate feeds without redirects; that
  surfaces as a `not_found` error row naming the URL, not a silent empty result.

# Actor input Schema

## `preset` (type: `string`):

Pick a whole family of sources at once, instead of naming them one by one. Combines with `sources` — you get the union, de-duplicated.

`all` crawls the entire 59-source catalog. Leave blank to use only what you list in `sources`.

## `sources` (type: `array`):

One source per entry. Four forms are accepted and can be mixed freely in one run:

• **Catalog key** — `aws`, `gcp`, `azure`, `cloudflare`, `github`, `anthropic`, `stripe`, `vercel`, `kubernetes`, `linear`… (59 curated sources; a catalog key always wins over the other forms)
• **GitHub repo** — `github:owner/repo`, e.g. `github:redis/redis`. Works for any public repo; a pasted `https://github.com/owner/repo` URL is accepted too
• **Package registry** — `npm:react`, `pypi:django`, `crates:serde`, `gem:rails`, `nuget:Newtonsoft.Json`, `packagist:laravel/framework`, `hashicorp:vault`, `eol:nodejs`
• **Any feed URL** — any RSS, Atom, RDF or JSON Feed address, e.g. `https://blog.rust-lang.org/feed.xml`

## `maxItemsPerSource` (type: `integer`):

Newest entries first, per source. Set to 0 for everything the source carries — worth knowing that some feeds are large (Oracle Cloud 2,960 entries, Vercel 1,532, npm packages several thousand versions), so 0 across a big preset produces a very large dataset.

## `sinceDate` (type: `string`):

Keep only entries published on or after this date. `YYYY-MM-DD` or a full ISO 8601 timestamp.

Entries with no date are KEPT rather than dropped — some sources (notably NuGet) publish no timestamps at all, and dropping them would make those sources look empty rather than undated.

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

Keep only entries mentioning at least one of these (case-insensitive), matched against title, summary, body text, version and categories. Leave empty for no keyword filtering.

Useful for watching one product across many vendors — e.g. `kubernetes`, or `deprecat` to catch both "deprecated" and "deprecation".

## `includeContent` (type: `boolean`):

Include the full release body (`contentHtml`, `contentText`) and the untouched upstream object (`raw`) on every row. Turn this off for a compact release index — titles, dates, versions and links only, which is dramatically smaller across a large preset.

## `githubToken` (type: `string`):

Optional, and only affects `github:` sources. Without it those sources read github.com's `releases.atom`, which needs no credentials but serves exactly 10 releases and cannot paginate.

With a token, they switch to the REST API: full release history, plus draft/prerelease flags and asset lists. It also lifts GitHub's unauthenticated limit of 60 requests/hour per IP to 5,000/hour — worth setting if you track many repos. A read-only classic token with no scopes is enough for public repos.

(This field intentionally has no default value — Apify rejects a `default` on a secret input at build time.)

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

Upper bound on requests in flight at once, across all sources. Sources sit on ~57 independent hosts, so concurrency here spreads across vendors rather than hammering one.

## `minRequestInterval` (type: `number`):

Paces when requests START, without holding a concurrency slot. The default of 0 is deliberate: these are public feeds published to be polled, spread across dozens of hosts, and none rate-limited during testing. Raise it if you crawl one vendor's feeds heavily.

## `proxyConfiguration` (type: `object`):

Off by default, unusually for this portfolio — and deliberately. Every source is a public feed a vendor publishes for machines to read: no auth, no cookie, no bot challenge was observed on any of the 59 during testing. A proxy adds latency and a failure mode without buying access. Enable it only if you need a specific egress country or IP.

## Actor input object example

```json
{
  "preset": "",
  "sources": [
    "aws",
    "cloudflare",
    "github",
    "anthropic",
    "github:redis/redis"
  ],
  "maxItemsPerSource": 50,
  "sinceDate": "",
  "keywords": [],
  "includeContent": true,
  "maxConcurrency": 8,
  "minRequestInterval": 0,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `items` (type: `string`):

One row per scraped record. See the dataset's default view for field definitions.

# 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 = {
    "sources": [
        "aws",
        "cloudflare",
        "github",
        "anthropic",
        "github:redis/redis"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapyx/changelog-releases-scraper").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 = { "sources": [
        "aws",
        "cloudflare",
        "github",
        "anthropic",
        "github:redis/redis",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("scrapyx/changelog-releases-scraper").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 '{
  "sources": [
    "aws",
    "cloudflare",
    "github",
    "anthropic",
    "github:redis/redis"
  ]
}' |
apify call scrapyx/changelog-releases-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapyx/changelog-releases-scraper"
        }
    }
}
```

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/7XWHOCBVT4Tefg4BK/builds/7x2HLbTb2AT9COS4L/openapi.json
