# Restaurant Menu & Price Change Monitor (`peterdrucker481/restaurant-menu-delta-monitor`) Actor

Monitor public restaurant menus and ordering pages for price changes, new or removed dishes, availability, descriptions, and category moves. Keeps a persistent baseline, emits clean deltas, and supports Toast/JavaScript menus with proxy + browser fallback.

- **URL**: https://apify.com/peterdrucker481/restaurant-menu-delta-monitor.md
- **Developed by:** [Peter Drucker](https://apify.com/peterdrucker481) (community)
- **Categories:**
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $50.00 / 1,000 successful restaurant scans

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

## Restaurant Competitor Menu & Price Monitor

Turn ordinary restaurant URLs into a recurring competitor-intelligence feed. The Actor discovers same-site menu pages, builds a normalized baseline, and reports only material menu and pricing decisions on later runs.

### Best for

- restaurant groups benchmarking local competitors;
- franchise and hospitality agencies monitoring many locations;
- food distributors watching category and ingredient trends;
- restaurant-data and market-research pipelines.

### Paste URLs, not scraper instructions

Input a restaurant homepage or exact menu page. Exact public ordering URLs give the highest success rate. The Actor follows same-site menu links and extracts from:

- Schema.org `Menu`, `MenuSection`, `MenuItem`, and `Product` JSON-LD;
- public application-state JSON embedded in the page;
- common server-rendered menu markup and microdata;
- JavaScript-rendered ordering pages, including public Toast menus, through an automatic browser fallback.

No restaurant login, access-control bypass, OCR, or AI billing is used. Modern ordering pages commonly reject data-center requests, so the default input uses Apify residential proxy traffic and only launches a browser when the faster HTML path returns no items.

### Intelligence events

- `ITEM_ADDED` and `ITEM_REMOVED`
- `PRICE_UP` and `PRICE_DOWN`
- `AVAILABILITY_CHANGED`
- `DESCRIPTION_CHANGED`
- `CATEGORY_CHANGED`

Every event contains old/current evidence, integer-cent price movement, percentage movement, source URL, severity, extraction confidence, detection time, and a deterministic fingerprint. A run summary ranks the largest price moves and counts changed restaurants.

### Noise controls

Use `minimumPriceChangePercent`, `keywords`, and `eventTypes` to deliver only changes relevant to a buyer's watchlist. `emitFullSnapshot` turns the same Actor into a normalized menu feed; leave it off for an exception-only monitoring workflow.

### First and later runs

The first run always writes one `BASELINE_SUMMARY` per successfully parsed restaurant, so setup is visibly confirmed without flooding the dataset. Set `emitBaseline` to include every item. Reuse the same `monitorKey` on a schedule to compare with that state.

### Example input

```json
{
  "restaurantUrls": [
    "https://famousrestaurant.toast.site/order/famous-restaurant",
    "https://restaurantconstance.toast.site/order"
  ],
  "monitorKey": "toronto-burger-competitors",
  "currencyFallback": "CAD",
  "maxPagesPerRestaurant": 1,
  "minimumPriceChangePercent": 2,
  "keywords": ["burger", "chicken", "combo"],
  "emitFullSnapshot": false
}
```

For Toast and other exact ordering URLs, start with one page per restaurant. Increase `maxPagesPerRestaurant` only when beginning from a homepage. Failed restaurants are reported in the run summary and are not charged.

### Recommended schedule

Run weekly for ordinary competitor pricing, daily during promotion or seasonal-menu periods, and reuse the same `monitorKey`. Results are available as JSON/CSV through the default dataset and can also be delivered to an HTTPS webhook.

### Recommended Store pricing

Configure two simple pay-per-event charges:

- `restaurant-scan` — **$0.05 per successfully parsed restaurant**;
- `changed-restaurant` — **$0.05 per restaurant with one or more matched changes**.

Remove the automatic `apify-default-dataset-item` event in the pricing setup so summary and evidence rows are not double-charged.

This prices a scan below the leading broad menu extractor while charging for the recurring state and alert layer. Failed restaurants are not charged. Offer initial Store trial credit rather than artificially billing every menu item.

### Local validation

```bash
npm install
npm test
npm run smoke
```

The unit suite covers JSON-LD, public application-state extraction, rendered ordering rows, international prices, all change classes, severity, and filtering. In the live 10-site Toast validation, the first run parsed 7/10 menus and 998 items; the immediate second run parsed 8/10 and emitted zero raw or matched changes. A focused three-site check also parsed all three and stayed quiet on its second run.

### Responsible use

Only public business-menu information is processed. Customers remain responsible for source terms, robots directives, rate limits, and applicable law.

# Actor input Schema

## `restaurantUrls` (type: `array`):

Restaurant homepages or exact menu URLs. Same-site menu links are discovered automatically.

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

Stable key for this watchlist. Reuse it on scheduled runs to compare against the same baseline.

## `currencyFallback` (type: `string`):

Three-letter ISO currency code used when a menu price has no explicit currency.

## `emitBaseline` (type: `boolean`):

When enabled, the first run emits BASELINE\_ITEM rows. The baseline is always stored.

## `emitFullSnapshot` (type: `boolean`):

Also write every normalized menu item on every run. Leave off for change-only monitoring.

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

Residential proxy access is recommended for modern ordering platforms that block data-center traffic. Proxy traffic is billed by Apify.

## `useBrowserFallback` (type: `boolean`):

Automatically open JavaScript-only menus in a browser when fast HTML extraction returns no items.

## `renderWaitSecs` (type: `integer`):

Seconds to wait after the menu page loads so dynamic menu items can appear.

## `maxPagesPerRestaurant` (type: `integer`):

Maximum same-site pages fetched while discovering the menu.

## `minimumPriceChangePercent` (type: `number`):

Suppress smaller price changes from the output and webhook. Other event types are unaffected.

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

Optional watchlist such as burger, chicken, lunch, or vegan. Empty means all items.

## `eventTypes` (type: `array`):

Optional event allowlist. Empty means every change type.

## `webhookUrl` (type: `string`):

Optional HTTPS endpoint that receives one run summary plus all changes.

## `requestTimeoutSecs` (type: `integer`):

Maximum seconds to wait for each public restaurant page request.

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

Maximum number of restaurant websites checked at the same time.

## Actor input object example

```json
{
  "monitorKey": "default",
  "currencyFallback": "USD",
  "emitBaseline": false,
  "emitFullSnapshot": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  },
  "useBrowserFallback": true,
  "renderWaitSecs": 3,
  "maxPagesPerRestaurant": 3,
  "minimumPriceChangePercent": 0,
  "keywords": [],
  "eventTypes": [],
  "requestTimeoutSecs": 25,
  "maxConcurrency": 2
}
```

# Actor output Schema

## `results` (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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("peterdrucker481/restaurant-menu-delta-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 = {}

# Run the Actor and wait for it to finish
run = client.actor("peterdrucker481/restaurant-menu-delta-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 '{}' |
apify call peterdrucker481/restaurant-menu-delta-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,peterdrucker481/restaurant-menu-delta-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/hx09y1pxWjDe38dii/builds/nofmJekvbDCh8Hzt7/openapi.json
