# App Update Monitor - No API Key (`renzomacar/app-update-monitor`) Actor

Watch iOS and Android apps for new versions. Returns the current version, release notes and days since the last update, and flags what changed since your previous run.

- **URL**: https://apify.com/renzomacar/app-update-monitor.md
- **Developed by:** [Renzo Madueno](https://apify.com/renzomacar) (community)
- **Categories:** E-commerce, Marketing, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.80 / 1,000 app 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/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

## App Update Monitor - New Versions & Release Notes

Watch any set of iOS and Android apps for new releases. Each run returns the current version, when it shipped, the release notes, how many days it has been since the last update - and, once you have a baseline, a flag on exactly which apps shipped something new since your previous run.

Mix both stores in one input list. App Store IDs, `apps.apple.com` URLs, Google Play package names and `play.google.com` URLs are detected automatically per entry. No API key, no developer account, no App Store Connect, no Play Developer API, no proxy.

### What you get per app

| Field | What it holds |
|---|---|
| `store` | `appstore` or `googleplay` |
| `appId` / `appName` | Identifier and resolved title |
| `country` | Storefront checked |
| `currentVersion` | The version string, when the store publishes one |
| `versionAvailable` | Boolean - whether a version string exists at all |
| `versionReleaseDate` | When the current build shipped, ISO 8601 |
| `releaseNotes` | "What's New" on iOS, "Recent changes" on Android |
| `previousVersion` | What this app was on during your last run |
| `previousSeenAt` | When that observation was made |
| `isNewVersion` | **The column you schedule this actor for** |
| `isFirstCheck` | True on the baseline run, so you can ignore those rows |
| `daysSinceLastUpdate` | Computed from the release date |
| `developer` | Publisher |
| `averageUserRating` / `userRatingCount` | Rating and rating volume at check time |
| `appUrl` | Store link |
| `checkedAt` | Run timestamp |

### Coverage, measured rather than promised

The two stores are not equally forthcoming, and pretending otherwise would set you up to trust a column that is empty.

**App Store: complete.** Across an 11-app live measurement, `version`, `currentVersionReleaseDate` and `releaseNotes` came back on **11 of 11**. Apple publishes a real version string for every app.

**Google Play: partial, by Google's choice.** In a 5-app live measurement only **1 of 5** large apps exposed a version string. The other four show "Varies with device" and publish nothing. There is no setting, no header and no parameter that recovers it.

So for Play apps with no version, this actor falls back to the **last-updated date plus the recent-changes text** as the change fingerprint, and marks the row `versionAvailable: false` so you know which signal you are reading. In practice this works: an app that ships a new build gets a new "updated" date, and the actor catches it.

Verified end to end. A live run against ChatGPT (iOS), WhatsApp (iOS), WhatsApp (Android, no version string) and Pokémon GO (Android, has one) produced correct baselines, and on a subsequent run with a modified prior state all four correctly reported `isNewVersion: true` - including the Android app with no version, caught by its date fingerprint.

### Set up change detection properly - this is the one step people miss

Change detection compares against your previous run, so the previous run's state has to survive. Set **`namedStateStore`** to a name of your choosing:

```json
{
  "appIds": ["6448311069", "com.whatsapp", "com.spotify.music"],
  "countries": ["us"],
  "namedStateStore": "app-monitor-state",
  "onlyChanged": false
}
```

Without `namedStateStore`, state lives in the run's own storage. Every run then looks like a first run, `isFirstCheck` stays `true`, and `isNewVersion` never fires. The actor logs a warning when this happens rather than letting you discover it in week three.

Recommended sequence:

1. **Baseline run** with `onlyChanged: false`. Every app comes back with `isFirstCheck: true`. Nothing is flagged as new, because there is nothing to compare against yet. This is correct.
2. **Schedule it** daily or weekly with the same `namedStateStore`.
3. Once the baseline exists, optionally set **`onlyChanged: true`** so runs only write rows for apps that actually shipped.

### `onlyChanged` and the "nothing happened" run

With `onlyChanged: true`, a run where no monitored app released anything writes **no rows and charges nothing**. It logs how many apps were checked, records the run summary, updates the state, and exits cleanly.

That is deliberate. A scheduled monitor spends most of its runs finding nothing, and a monitor that bills you on quiet days is a monitor you turn off. The quiet-day answer still reaches you through the run log and the `RUN_SUMMARY` record.

### Input

```json
{
  "appIds": [
    "6448311069",
    "https://apps.apple.com/us/app/id310633997",
    "com.whatsapp",
    "https://play.google.com/store/apps/details?id=com.spotify.music"
  ],
  "countries": ["us", "gb"],
  "onlyChanged": false,
  "includeReleaseNotes": true,
  "namedStateStore": "app-monitor-state",
  "stateKey": "APP_VERSION_STATE",
  "maxItems": 200
}
```

**Input aliases.** `appIds` also accepts `ids`, `appId`, `apps`, `appUrls` and `urls`. `countries` also accepts `country`, `countryCodes`, `storefronts` and `gl`. `onlyChanged` also accepts `onlyNewVersions` and `changesOnly`. `maxItems` also accepts `maxResults` and `limit`. Comma separated strings work anywhere a list does.

Store detection needs no configuration. A numeric ID or an `apps.apple.com` URL is treated as App Store; a dotted package name or a `play.google.com` URL is treated as Google Play. iOS bundle IDs are resolved through Apple's lookup endpoint.

### Competitor release monitoring

Put your competitive set in `appIds`, schedule daily, set `onlyChanged: true`. What arrives is a feed of exactly one thing: which competitor shipped, when, and what their release notes say they shipped.

Release notes are the most underused competitive intelligence source in mobile. Companies that will not discuss roadmap publicly describe new features in "What's New" the day they launch them. Collected over months, that dataset is a competitor's product history in their own words, timestamped.

### Spotting abandoned apps

`daysSinceLastUpdate` is computed on every row. Sort a set descending and abandoned apps rise to the top.

Pair it with `userRatingCount`. An app with 40,000 ratings and 500 days since its last build is a category position with real demand behind it and nobody defending it. That combination is a specific opportunity, and it is two columns in this dataset.

### Release cadence benchmarking

Run the actor weekly against your category's top twenty apps and keep the datasets. Over a quarter you can compute each competitor's real shipping interval. Teams use this to answer "are we shipping fast enough for this category" with a number instead of an opinion, and to spot the moment a competitor's cadence changes - a studio going from monthly to weekly releases has usually just raised money or started a push.

### Watching your own apps across storefronts

Add the same app under several `countries`. Store rollouts are not simultaneous, and the dataset shows you which storefronts have the new build live and which are still serving the previous one. For a release manager that is a rollout dashboard from a single scheduled run.

### How this actor behaves when something goes wrong

1. **Errors never enter the dataset.** An app not available in a storefront, a package name that does not exist, a value that is not an app identifier - each is recorded in a `FAILURES` key-value record with the exact reason per input. None becomes a dataset row, because a row that says "error" would bill you for receiving an error.
2. **A run that delivered nothing is marked FAILED** - with one deliberate exception: an `onlyChanged` run where everything was checked successfully and nothing had changed. That is a successful run with a real answer, and it is reported through the log and `RUN_SUMMARY` rather than as a failure.
3. **A missing version string is reported, not faked.** `versionAvailable: false` tells you the store published nothing and the date fingerprint is in use.
4. **Apple lookups are batched** up to 50 apps per request, so monitoring 200 iOS apps costs a handful of HTTP calls.
5. **The free tier returns data.** Proxy is optional and off by default.

### Pricing

Pay per event.

- **$0.0008 per app checked** and delivered - $0.80 per 1,000 checks.
- **$0.004 per new version detected**, charged only for a confirmed change against your previous run.

No subscription, no charge for starting a run. Monitoring 50 apps daily is $0.04 per run, about **$1.20 a month**, plus a fraction of a cent for each release actually caught. With `onlyChanged: true` the quiet days cost nothing at all.

`maxItems` is a hard ceiling on both rows and spend.

### Output sample

```json
{
  "store": "appstore",
  "appId": "6448311069",
  "appName": "ChatGPT",
  "country": "us",
  "currentVersion": "1.2026.224",
  "versionAvailable": true,
  "versionReleaseDate": "2026-08-22T01:20:24.000Z",
  "releaseNotes": "Bug fixes and performance improvements",
  "previousVersion": "1.2026.218",
  "previousSeenAt": "2026-08-21T04:18:16.545Z",
  "isNewVersion": true,
  "isFirstCheck": false,
  "daysSinceLastUpdate": 0,
  "developer": "OpenAI",
  "averageUserRating": 4.8303,
  "userRatingCount": 9552259,
  "appUrl": "https://apps.apple.com/us/app/chatgpt/id6448311069?uo=4",
  "checkedAt": "2026-08-22T04:18:16.545Z"
}
```

And the Android case where Google publishes no version string:

```json
{
  "store": "googleplay",
  "appId": "com.whatsapp",
  "appName": "WhatsApp Messenger",
  "country": "us",
  "currentVersion": null,
  "versionAvailable": false,
  "versionReleaseDate": "2026-08-19T14:50:47.000Z",
  "releaseNotes": "You can now start voice chats in all groups...",
  "isNewVersion": false,
  "isFirstCheck": true,
  "daysSinceLastUpdate": 2,
  "checkedAt": "2026-08-22T04:18:16.545Z"
}
```

### Dataset views

- **Version state** - store, app, version, release date, days since, new flag, previous version. The monitoring table.
- **Changelog** - store, app, version, release date and the full release notes. The reading view, and the one worth archiving.

### Frequently asked questions

**Why is nothing flagged as new on my first run?** Because there is no previous run to compare against. Every row will have `isFirstCheck: true`. That is the baseline; the second run onward is where `isNewVersion` becomes meaningful.

**Why is `isNewVersion` always false?** Almost certainly `namedStateStore` is not set, so each run starts from an empty state. See the setup section above.

**Why is `currentVersion` null for some Android apps?** Google shows "Varies with device" and publishes no version string - 4 out of 5 large apps in a live measurement. The actor falls back to the last-updated date and sets `versionAvailable: false` so you know.

**Can I mix iOS and Android apps in one run?** Yes. The store is detected per entry and each row is tagged with `store`.

**Do I need a developer account for either store?** No. Everything read here is public.

**How often should I schedule it?** Daily is the sweet spot for competitor monitoring - fast enough to catch releases while they are news, cheap enough to ignore. Add `onlyChanged: true` after the baseline so quiet days cost nothing.

**Does it work on apps I do not own?** Yes. That is the primary use.

### Related actors

- **App Store App Details Scraper** - full iOS metadata including in-app purchase names and prices.
- **Google Play Developer Portfolio Scraper** - every app a publisher ships, with ratings and install bands.
- **App Store Reviews Scraper** and **Google Play Reviews Scraper** - pair a release with the reviews it generated to see whether a build landed well.
- **ASO Keyword Rank Tracker** - App Store search positions for a keyword and app.

# Actor input Schema

## `appIds` (type: `array`):

Mix freely: App Store numeric IDs, apps.apple.com URLs, Google Play package names, play.google.com URLs. The store is detected per entry. Aliases: ids, appId, apps, appUrls.

## `countries` (type: `array`):

Storefront / country code used for both stores. Aliases: country, countryCodes.

## `onlyChanged` (type: `boolean`):

Off by default so the very first run gives you a full baseline. Turn on once the baseline exists and you only want the diffs.

## `includeReleaseNotes` (type: `boolean`):

App Store release notes are present on 11 of 11 apps tested. Google Play exposes a "recent changes" text instead, and no version string for roughly 4 out of 5 large apps.

## `stateKey` (type: `string`):

Key in the named key-value store where the previous run's versions are kept, so change detection survives across scheduled runs.

## `namedStateStore` (type: `string`):

Set this to a named store (for example app-monitor-state) so scheduled runs compare against each other. Without it, state lives in the run's own store and every run looks like a first run.

## `maxItems` (type: `integer`):

Hard ceiling on apps delivered and therefore on what you are charged.

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

Not required.

## Actor input object example

```json
{
  "appIds": [
    "6448311069",
    "com.whatsapp",
    "https://apps.apple.com/us/app/id310633997"
  ],
  "countries": [
    "us"
  ],
  "onlyChanged": false,
  "includeReleaseNotes": true,
  "stateKey": "APP_VERSION_STATE",
  "maxItems": 200,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

One row per app checked, flagging whether a new version appeared since the last run.

# 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 = {
    "appIds": [
        "6448311069",
        "com.whatsapp",
        "https://apps.apple.com/us/app/id310633997"
    ],
    "countries": [
        "us"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("renzomacar/app-update-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 = {
    "appIds": [
        "6448311069",
        "com.whatsapp",
        "https://apps.apple.com/us/app/id310633997",
    ],
    "countries": ["us"],
}

# Run the Actor and wait for it to finish
run = client.actor("renzomacar/app-update-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 '{
  "appIds": [
    "6448311069",
    "com.whatsapp",
    "https://apps.apple.com/us/app/id310633997"
  ],
  "countries": [
    "us"
  ]
}' |
apify call renzomacar/app-update-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,renzomacar/app-update-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/msRm9DqbnSe0KcnmG/builds/gQA6uyGgQ3EoywX6Y/openapi.json
