# Google Maps Listing Change Monitor: Ratings, Hours, Closures (`transparent_meteorite/maps-listing-change-monitor`) Actor

Track a fixed set of Google Maps listings (clients, competitors or a category search) and get one row per change: closed, reopened, rating drop, review spike or stall, hours, phone, website or category changed, listing gone. Run 1 records a health baseline. No login.

- **URL**: https://apify.com/transparent_meteorite/maps-listing-change-monitor.md
- **Developed by:** [Detroit Web Agent - Open Data](https://apify.com/transparent_meteorite) (community)
- **Categories:** Lead generation, SEO tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.80 / 1,000 place signals

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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Google Maps Listing Change Monitor: Ratings, Hours, Closures

![Google Maps Listing Change Monitor: features, price and a real output row](https://api.apify.com/v2/key-value-stores/9BAn0msnxToXlD9TO/records/sf-maps-listing-change-monitor-banner-10140227.png)

[![Run it free on Apify](https://api.apify.com/v2/key-value-stores/9BAn0msnxToXlD9TO/records/sf-cta.png)](https://console.apify.com/actors/edI8NoxuP43k2evgg/input)

| Price per 1,000 listings | Free plan | Starter | Scale | Business |
|---|---|---|---|---|
| **This Actor** | **$3.00** | **$2.50** | **$2.00** | **$1.80** |
| Most-used alternative | $10.00 | $10.00 | $10.00 | $10.00 |

<sub>Pay per event, no monthly rental. Prices read from each Actor's public Apify pricing on 2026-10-15; check the Pricing tab for the current figure.</sub>

![Google Maps Listing Change Monitor sample output table from a real run](https://api.apify.com/v2/key-value-stores/9BAn0msnxToXlD9TO/records/sf-maps-listing-change-monitor-output-10140227.png)

Track a fixed set of Google Maps listings and get one row for every change that matters: a place closes or reopens, its rating drops, reviews suddenly spike or stop coming in, reviews get removed, hours change, the phone number or website changes, the primary category changes, or the listing disappears. The first run records a health baseline for every place, with a listing completeness score and its rank inside the set by rating and by review count.

It is built for the job local SEO agencies and multi-location brands already do by hand or pay a listing-management tool for: watching their own listings and their competitors' listings week after week. No login, no Google API key, no browser.

### What it does

You give the actor a set of places to watch. A set is either:

- **A search you re-check**, such as `dentist in Royal Oak, MI` or `coffee shop in Ferndale, MI`. The top N results (default 40) become the monitored set: a fixed competitor landscape.
- **A list of specific listings**, as Google Maps place URLs or place IDs (`ChIJ...`). These form the set "Tracked places" and are read from their own place page on every run. Use this for client listings and for named competitors.

You can mix both in one run.

**Run 1 (baseline).** Every place gets one `baseline` row: status (operational, temporarily closed, permanently closed), rating, review count, hours, phone, website, category, a listing completeness score out of 100 with the missing fields named, and its rank in the set by rating and by reviews. Each set also gets a free `setSummary` row: how many places, how many closed, average rating, median reviews, the top-rated and most-reviewed place, and the least complete listing.

**Run 2 and later (changes only).** The actor reads the same set again, compares each place with what it stored last time, and writes one `event` row per change. Places that did not change produce no paid rows. The free set summary row is written every run, so a quiet week still shows you the current state.

State lives in a named key-value store (`maps-listing-change-monitor`), keyed by your `monitorKey`. Runs with the same key compare against each other. Schedule the actor and it becomes a listing monitor.

### Who it is for

**The local SEO agency with 30 client listings.** An agency manages Google Business Profiles for dentists, HVAC companies and law firms. Clients ask "did anything change?" and the honest answer is usually "we would not know until someone complains". The agency pastes its 30 client place URLs, sets `monitorKey: "agency-clients"`, picks the `clientListings` preset and schedules a weekly run. When someone at a client's office edits the phone number, when Google marks a location temporarily closed, or when the rating slips from 4.6 to 4.3 after a run of bad reviews, a row lands in the dataset (and in Slack or email through an Apify integration) with a one-line summary the account manager can forward.

**The multi-location brand watching its own estate.** A regional chain has 60 locations. Head office wants to know when a store's hours on Google drift from the hours on the website, when a location starts losing its rating, or when a store's review flow stalls (often a sign the review-request process stopped at that branch). The brand tracks its own place IDs with the `reputationWatch` preset for reviews and ratings, and a second task with `closureWatch` for hours and closures.

**The competitor set for a pitch or a market report.** A marketing consultant tracks `med spa in Birmingham, MI` and `med spa in Troy, MI` with the `competitorSet` preset. Run 1 gives a ranked table of every competitor by rating and reviews with completeness scores, ready for a pitch deck. Weekly runs after that show who closed, who is gaining reviews fast (a competitor running a review campaign), and which new places entered the top results.

### Input examples

Empty input works. It runs a small demo: the search `coffee shop in Ferndale, MI`, 20 places, baseline rows plus one summary row, in well under two minutes.

Client listings, checked weekly:

```json
{
  "placeUrls": [
    "https://www.google.com/maps/place/?q=place_id:ChIJn8X_1xevJIgR0nUJVsjFMb4",
    "ChIJ2eUgeAK6j4ARbn5u_wAGqWA"
  ],
  "preset": "clientListings",
  "monitorKey": "agency-clients"
}
```

A competitor set from two searches, including places entering the results:

```json
{
  "searches": ["med spa in Birmingham, MI", "med spa in Troy, MI"],
  "maxPlacesPerSearch": 30,
  "preset": "competitorSet",
  "monitorKey": "medspa-oakland-county"
}
```

Reputation only, with a stricter rating threshold:

```json
{
  "placeUrls": ["ChIJ2eUgeAK6j4ARbn5u_wAGqWA"],
  "searches": ["dentist in Royal Oak, MI"],
  "preset": "custom",
  "alertOn": ["ratingDrop", "reviewSpike", "reviewStall", "reviewsRemoved"],
  "ratingDropThreshold": 0.1,
  "monitorKey": "dental-reputation"
}
```

Input fields:

| Field | What it does | Default |
|---|---|---|
| `searches` | Google Maps searches to re-check; each one is a monitored set | `["coffee shop in Ferndale, MI"]` |
| `placeUrls` | Place URLs or place IDs to track individually | empty |
| `maxPlacesPerSearch` | Size of each search set (1 to 120) | 40 (demo: 20) |
| `preset` | `custom`, `clientListings`, `competitorSet`, `reputationWatch`, `closureWatch` | `custom` |
| `alertOn` | Change types to report when preset is `custom` | all except `enteredSet` |
| `ratingDropThreshold` | Stars of drop that count as `RATING_DROP` | 0.2 |
| `spikeMinReviews` | Minimum new reviews for a `REVIEW_SPIKE` | 5 |
| `monitorKey` | Name of the stored history | derived from the input |
| `resetBaseline` | Start over with a fresh baseline | false |
| `maxDisappearChecks` | Place-page lookups per run for places that dropped out of a search | 25 |
| `language`, `countryCode` | Google `hl` and `gl` | `en`, `us` |

### Output example

A change event:

```json
{
  "rowType": "event",
  "eventType": "RATING_DROP",
  "severity": "medium",
  "summary": "RATING_DROP: \"Northside Dental\" (Dentist, Royal Oak) fell from 4.6 to 4.3 stars (-0.3) since 2026-10-06; 212 reviews.",
  "setName": "Tracked places",
  "title": "Northside Dental",
  "categoryName": "Dentist",
  "status": "OPERATIONAL",
  "rating": 4.3,
  "reviewsCount": 212,
  "field": "rating",
  "oldValue": "4.6",
  "newValue": "4.3",
  "ratingDelta": -0.3,
  "completenessScore": 86,
  "missingFields": "website",
  "placeId": "ChIJ...",
  "mapsUrl": "https://www.google.com/maps/search/?api=1&query=Northside%20Dental&query_place_id=ChIJ...",
  "sourceUrl": "https://www.google.com/maps/place/?q=place_id:ChIJ...&hl=en&gl=us",
  "previousCheckedAt": "2026-10-06T08:00:00.000Z",
  "daysSinceLastCheck": 7,
  "monitorKey": "dental-reputation",
  "runNumber": 3,
  "checkedAt": "2026-10-13T08:00:00.000Z"
}
```

A baseline row (run 1) carries the same place fields with `rowType: "baseline"`, `ratingRankInSet`, `reviewsRankInSet`, `setSize` and a summary such as `BASELINE: "Honey Dirt Coffee" (Coffee shop, Ferndale) is operational, 4.7 stars from 310 reviews, listing 100/100 complete; #3 by rating and #2 by reviews of 20 in coffee shop in Ferndale, MI.` (the names and numbers here are illustrative).

The set summary row has `rowType: "setSummary"`, `placesCount`, `operationalCount`, `closedCount`, `closedShare`, `avgRating`, `medianReviews`, `avgCompleteness`, `topByRating`, `topByReviews`, `eventsCount` and `eventsByType`.

The dataset has two views: **Changes and baseline** (the columns you read first) and **All fields**.

### Signals explained

| Event | When it fires | Severity |
|---|---|---|
| `CLOSED` | Status moves from operational to temporarily or permanently closed | high |
| `REOPENED` | A closed place shows as operational again | medium |
| `RATING_DROP` | Rating falls by `ratingDropThreshold` or more from the reference rating. The reference only moves up (or resets after a reported drop), so a slow slide of 0.1 per week is still caught | medium, high at 0.4+ |
| `REVIEW_SPIKE` | At least `spikeMinReviews` new reviews since the last check and a pace at least 3x the earlier pace; without history, at least 10 new reviews and 15% growth | medium |
| `REVIEW_STALL` | The earlier pace predicts 4+ new reviews, at least 3 days passed, and none arrived | low |
| `REVIEWS_REMOVED` | Review count falls by 3 or more (and at least 2%) since the last check | medium |
| `HOURS_CHANGED` | New opening hours seen on two checks in a row (a one-day holiday change does not fire) | medium |
| `CONTACT_CHANGED` | Phone number or website domain added, changed or removed (`field` says which) | medium, high when removed |
| `CATEGORY_CHANGED` | Primary Google category changed | low |
| `DISAPPEARED` | The listing no longer resolves on Google Maps on two checks in a row | high |
| `ENTERED_SET` | A place new to the monitor enters a monitored search (only with `enteredSet` switched on, which the `competitorSet` preset does) | info |

**Completeness score.** Seven checks, each worth one seventh: phone, website, hours, category, address, photos, reviews. `missingFields` names the ones that fail. It is a quick listing-hygiene number, not Google's own profile strength score.

**Dropping out of a search vs closing.** A place can slide out of the top 40 results without anything happening to it. When a known place is missing from a search, the actor looks it up on its own place page (up to `maxDisappearChecks` per run). If it is still there, it is diffed normally; if it cannot be found twice in a row, you get `DISAPPEARED`. Operational places that stay outside the results for three runs leave the set quietly.

### Pricing

Pay per event. You pay for rows that carry information, nothing for the rest.

| Event | FREE plan | BRONZE | SILVER | GOLD and above |
|---|---|---|---|---|
| `place-signal` (one baseline row or one change event) | $0.003 | $0.0025 | $0.002 | $0.0018 |
| Actor start | $0.00005 | $0.00005 | $0.00005 | $0.00005 |

Set summary rows and unchanged places are free. Typical cost on the FREE plan:

| Scenario | Rows charged | Cost |
|---|---|---|
| Baseline of 30 client listings | 30 | about $0.09 |
| Weekly check of those 30, 2 changes found | 2 | about $0.006 |
| Baseline of 2 searches x 40 places | 80 | about $0.24 |
| Weekly check of 80 places, 5 changes | 5 | about $0.015 |
| A year of weekly checks on 80 places, ~5 changes a week | 80 + 260 | about $1.02 |

Platform compute is billed by Apify separately and is small: a 40-place search check usually runs in under a minute at 512 MB.

`maxTotalChargeUsd` is respected. If the run reaches your limit it stops before the next place, never pushes a paid row it did not charge, and keeps the undelivered changes pending: the next run reports them.

### Monitor mode and schedule recipe

1. Run once with your input and a `monitorKey` you choose, for example `agency-clients`. This is the baseline.
2. In Apify Console, save the input as a task, then add a schedule: weekly (`0 8 * * 1`, Monday 08:00) suits most agencies; daily is fine for closure watching.
3. Add an integration on the task (Slack, email, webhook, Make, Zapier, n8n) that fires on run success, and filter rows where `rowType` is `event`.
4. Keep `language` and `countryCode` the same across runs. Hours and category text are compared as Google shows them in that language.
5. To start over (for example after you change the client list a lot), run once with `resetBaseline: true`.

Adding a place URL to an existing tracked list gives that place a baseline row on its next run. Removing one stops checking it; its history ages out after 180 days.

### Use via API or MCP

Start a run and read changes with the Apify API:

```bash
curl -X POST "https://api.apify.com/v2/acts/transparent_meteorite~maps-listing-change-monitor/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"placeUrls":["ChIJ2eUgeAK6j4ARbn5u_wAGqWA"],"preset":"clientListings","monitorKey":"agency-clients"}'
```

Python:

```python
from apify_client import ApifyClient
client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("transparent_meteorite/maps-listing-change-monitor").call(
    run_input={"searches": ["dentist in Royal Oak, MI"], "preset": "competitorSet", "monitorKey": "dentists-ro"})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    if row["rowType"] == "event":
        print(row["summary"])
```

AI agents can call it through the Apify MCP server:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com/?actors=transparent_meteorite/maps-listing-change-monitor",
      "headers": { "Authorization": "Bearer <APIFY_TOKEN>" }
    }
  }
}
```

Then ask the agent something like "check my tracked listings with monitor key agency-clients and list anything that closed or lost rating".

### Limits and honest caveats

- **Public Google Maps data only.** The actor reads the same JSON the public google.com/maps page loads. It does not log in, does not use the Google Places API, and cannot see Business Profile insights, owner-only fields or review text history.
- **Review counts, not review text.** Spike, stall and removal signals come from the review count Google shows. If you need the reviews themselves, use a review scraper on the places this actor flags.
- **Temporary vs permanent closure.** Google marks closed listings with one internal flag. When the page text says "Temporarily closed" the actor reports `CLOSED_TEMPORARILY`; otherwise a closed listing is reported as `CLOSED_PERMANENTLY`. In our test captures every closed listing was a permanent closure, so treat the temporary/permanent split as best effort and read the `summary`.
- **Search sets move.** Google's ranking shifts with time and location signals. That is why dropouts are confirmed on the place page, and why `ENTERED_SET` is off by default.
- **Hours are compared as text**, after normalising spaces and dashes. A change of interface language looks like a change of hours, so keep `language` fixed.
- **Contact changes compare like with like.** Search cards and place pages occasionally format phone and website differently, so a search-set observation is only compared with an earlier search-set observation of the same place.
- **Scale.** Built for hundreds of places per run, not tens of thousands. Requests run at most two at a time with pauses between pages. Google lists about 120 places per search at most.
- **Proxy.** It uses your proxy setting (Apify Proxy automatic by default) and falls back to a direct connection when rate limited. It does not escalate to residential proxies on its own; pick a residential group in the Proxy field if you need it.
- **Baselines are paid once per place per set.** A place that appears in two of your searches gets a baseline row in each set, and change events only once per run.

### Run in 30 seconds

1. Open the Actor in Apify Console and click **Try for free** (or **Start** if you already have an account).
2. Open the **Input** tab, switch to **JSON**, and paste the example below. It is small on purpose, so the first run is quick and cheap.
3. Click **Start**. Most small runs finish in well under two minutes.
4. Open the **Output** tab to read the Overview table, or use **Export** to download JSON, CSV or Excel.

```json
{
  "searches": [
    "coffee shop in Ferndale, MI"
  ],
  "maxPlacesPerSearch": 10,
  "preset": "competitorSet",
  "monitorKey": "ferndale-coffee-test"
}
```

Once the sample looks right, replace the example values with your own list and raise the limits.

### Example API call

The first run for a given `monitorKey` stores a baseline and reports baseline rows plus one summary row. Changes appear from the second run onward, so schedule the Actor daily or weekly with the same `monitorKey`. Start it from code or with curl; replace the placeholder token and username.

```python
from apify_client import ApifyClient

client = ApifyClient("APIFY_TOKEN")
run = client.actor("YOUR_USERNAME/maps-listing-change-monitor").call(run_input={"searches": ["coffee shop in Ferndale, MI"], "maxPlacesPerSearch": 10, "preset": "competitorSet", "monitorKey": "ferndale-coffee"})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(row)
```

```bash
curl -X POST "https://api.apify.com/v2/acts/YOUR_USERNAME~maps-listing-change-monitor/runs?token=APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"searches": ["coffee shop in Ferndale, MI"], "maxPlacesPerSearch": 10, "preset": "competitorSet", "monitorKey": "ferndale-coffee"}'
```

### Tips to keep monitoring reliable

- Always set a stable `monitorKey`. Changing it starts a new history and a new baseline.
- Use `resetBaseline` only when you intentionally redefine the set, for example after changing the search wording.
- Keep `maxPlacesPerSearch` modest while testing; raise it once alerts look right.
- Lower `maxDisappearChecks` if you want shorter runs, at the cost of fewer closure confirmations.

### FAQ

**How is this different from a Google Maps scraper?** A scraper returns every place every time. This actor remembers what it saw and returns only what changed, plus a one-time baseline. On a weekly schedule you pay for a handful of rows instead of the whole list.

**Does it find new businesses in an area?** Not as its purpose. It watches a set you define. With `competitorSet` it tells you when a new place enters a search you already monitor, which is useful for competitor tracking but is not a full new-business feed.

**What happens if a run fails halfway?** The monitor state is saved at the end of a run. Places checked in a failed run are compared again next time, so a change is reported late rather than lost.

**Can I run several monitors?** Yes. Use a different `monitorKey` for each client or market. Each key keeps its own history in the same key-value store.

**How do I see the stored state?** Open the key-value store `maps-listing-change-monitor` in your Apify account; the record `snap-<monitorKey>` holds it. The run's `OUTPUT` record summarises counts per set.

**Why did I get no rows on run 2?** Nothing changed. The set summary row confirms the check ran and shows the current numbers.

**Can I change the rating threshold?** Yes, `ratingDropThreshold` (0.1 to 2 stars).

**Is this allowed?** It reads publicly visible listing data at a low request rate, without logging in or bypassing any access control. You are responsible for how you use the data in your jurisdiction.

### Related actors

- [Google Maps Scraper: Places, Phones, Emails, Reviews](https://apify.com/transparent_meteorite/google-maps-places-scraper): full place lists with contact enrichment and reviews, when you need every place rather than changes.
- [Company Enrichment by Domain](https://apify.com/transparent_meteorite/company-enrichment-by-domain): enrich the websites of places you are tracking.
- [Domain Intelligence Lookup](https://apify.com/transparent_meteorite/domain-intelligence-lookup): DNS, hosting and tech details for a business website.

New to Apify? [Create a free account](https://console.apify.com/sign-up?fpr=rnmakg) and run this Actor in under a minute. Some links are affiliate links.

# Actor input Schema

## `searches` (type: `array`):

Google Maps searches to re-check each run, e.g. "dentist in Royal Oak, MI". Each search is one monitored set; its places get a baseline row on the first run and change events after that. Leave empty and add place URLs below to monitor only specific listings.

## `placeUrls` (type: `array`):

Specific listings to track (your clients or named competitors): Google Maps place URLs (https://www.google.com/maps/place/...) or place IDs (ChIJ...). They form the set "Tracked places" and are checked on their own place page every run.

## `maxPlacesPerSearch` (type: `integer`):

How many top results of each search make up the monitored set (Google lists at most about 120). The demo search with empty input is capped at 20.

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

A preset sets the alert list for you. Choose Custom to use the alert list below.

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

Which change types become rows. Only used when Preset is Custom. Changes you switch off are still recorded in the monitor state, just not delivered or charged.

## `ratingDropThreshold` (type: `number`):

Report RATING_DROP when the average rating falls by at least this much from the reference rating (0.1 to 2).

## `spikeMinReviews` (type: `integer`):

A REVIEW_SPIKE needs at least this many new reviews since the last check, on top of the velocity test.

## `monitorKey` (type: `string`):

Name for this monitor's stored history (letters, digits, dashes). Runs with the same key compare against each other. Leave empty to derive a key from the searches and places, which changes if you edit them.

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

Forget the stored history for this monitor key and record a fresh baseline (every place is charged once as a baseline row again).

## `maxDisappearChecks` (type: `integer`):

When a known place drops out of a search, it is looked up on its own place page to tell "moved down the list" from "closed or removed". This caps those lookups per run.

## `language` (type: `string`):

Google interface language (hl), e.g. en, de, fr. Keep it the same between runs: hours and category text are compared as strings.

## `countryCode` (type: `string`):

Google country code (gl), e.g. us, gb, de.

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

Defaults to Apify Proxy (automatic). A direct connection is tried if the proxy is rate limited. No residential proxy is used unless you choose it here.

## Actor input object example

```json
{
  "searches": [
    "coffee shop in Ferndale, MI"
  ],
  "maxPlacesPerSearch": 40,
  "preset": "custom",
  "alertOn": [
    "closed",
    "ratingDrop",
    "reviewSpike",
    "reviewStall",
    "reviewsRemoved",
    "hoursChanged",
    "phoneOrWebsiteChanged",
    "categoryChanged",
    "disappeared"
  ],
  "ratingDropThreshold": 0.2,
  "spikeMinReviews": 5,
  "resetBaseline": false,
  "maxDisappearChecks": 25,
  "language": "en",
  "countryCode": "us",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `full` (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 = {
    "searches": [
        "coffee shop in Ferndale, MI"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("transparent_meteorite/maps-listing-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 = {
    "searches": ["coffee shop in Ferndale, MI"],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("transparent_meteorite/maps-listing-change-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 '{
  "searches": [
    "coffee shop in Ferndale, MI"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call transparent_meteorite/maps-listing-change-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,transparent_meteorite/maps-listing-change-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/edI8NoxuP43k2evgg/builds/aH45serRlJtDBPlom/openapi.json
