Restaurant Menu Pricing Modifiers Scraper avatar

Restaurant Menu Pricing Modifiers Scraper

Pricing

from $1.00 / 1,000 menu item extracteds

Go to Apify Store
Restaurant Menu Pricing Modifiers Scraper

Restaurant Menu Pricing Modifiers Scraper

Extract public menu items, prices, descriptions and modifier groups from ChowNow, Square Online, BentoBox and supported Schema.org Menu pages. Export structured restaurant data for menu research, competitor pricing and FoodTech workflows.

Pricing

from $1.00 / 1,000 menu item extracteds

Rating

0.0

(0)

Developer

Arezdev

Arezdev

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

1

Monthly active users

14 days ago

Last modified

Categories

Security

Limited permissions

Share

Turn public restaurant ordering pages into clean menu, price, and modifier data for FoodTech research, POS prospecting, and menu inflation tracking.

Extract one flat record per menu item from ChowNow Direct, Square Online catalogs, BentoBox menu markup, and Schema.org Menu pages. Results include published descriptions, prices, categories, dietary labels, images, restaurant contact details, and modifier groups. Requests use HTTP streams and direct JSON endpoints.

Best for menu research and comparing published item prices. Each successful row is a menu item. Use a direct ordering or menu URL; Toast pages need compatible public menu markup, and checkout-only pricing or browser-only menus may be unavailable.

Ready-to-run example

The Input form includes public example values. A fresh apify run uses the input schema defaults. To explicitly select the small sample, including when input was saved by an earlier local run, use:

apify run --input-file .actor/input.example.json

You can also paste .actor/input.example.json into the JSON input editor in Apify Console. Replace the example sources with your own targets and increase the limits when you are ready.

How to use

  1. Paste public restaurant menu or ordering URLs into Restaurant ordering or menu URLs.
  2. Leave Include modifier summaries enabled to capture add-ons, selection limits, and option prices.
  3. Choose a global record limit and run the Actor.
  4. Open the Restaurant menu items output table or export JSON, CSV, Excel, or another supported Apify format.

Try this small Square Online menu example:

{
"restaurantUrls": [
"https://hudsonsplace.square.site/"
],
"includeModifiers": true,
"maxItems": 3,
"timeoutSecs": 15,
"proxyConfiguration": {
"useApifyProxy": false
}
}

The Console sample uses a public Square Online menu and saves at most three rows. Check the sample output, then replace the URL with your target restaurant. Live menus can change or become unavailable; HTTP errors appear as diagnostic rows.

What data can you extract?

FieldsMeaning
orderingPlatform, statusDetected platform and SUCCESS or ERROR
restaurantName, restaurantPhonePublished restaurant identity and phone
streetAddress, city, state, zipCodePublished restaurant address
menuCategory, itemId, itemNameMenu section, stable source ID, and dish name
itemDescription, basePrice, currencyDescription, numeric starting price, and published currency
dietaryLabelsPublished dietary labels separated by semicolons
hasModifiers, modifierGroupsSummaryModifier flag and readable option groups with price adjustments
itemImageUrl, sourceUrl, scrapedAtPublic image, submitted source URL, and UTC extraction time
errorMessageSafe error information for failed targets; null on successful items

All output fields are scalar values or null. Unknown prices and currencies remain null. Prices are expressed in major currency units. A zero price remains numeric zero. Prices exclude taxes, delivery fees, service fees, and checkout totals.

ChowNow serving-size children are combined into one menu item when the API publishes their parent relationship. The default size supplies the base price; the summary lists each size's full price. Square SKU variations are similarly summarized as full variation prices. Other modifier prices represent published adjustments. Popular-item duplicates are removed by source item ID. Dietary labels are extracted from published structured labels.

Input settings

SettingDefaultDescription
restaurantUrlsRequiredUp to 10,000 public HTTP or HTTPS menu URLs; duplicate URLs are removed
includeModifierstrueInclude flat group and option summaries; preserve known modifier flags when disabled
maxItems3Global saved-record limit, 1-10,000; Console prefill is 3
timeoutSecs15Total request deadline in seconds, 3-60, including retries and redirects
maxConcurrency25Upper bound, 1-100; actual workers adapt to memory and the remaining record limit
proxyConfigurationOptionalConfigure direct-only requests, custom proxies, or an explicitly enabled final Apify proxy tier

Without a forced proxy setting, requests start directly. HTTP 403, 429, and 503 can trigger an Apify datacenter retry: SHADER first, then the account's default datacenter proxy when SHADER is unavailable. Your configured final proxy tier is used only when useApifyProxy is explicitly true. Residential groups are never enabled automatically.

Setting useApifyProxy to false forces direct requests. Nonempty proxyUrls force those proxies. Custom proxies must support HTTP CONNECT. Proxy tunnels use validated destination IPs while preserving the restaurant hostname for HTTP authority and TLS verification. Apify proxy retries require the platform-provided proxy password; the Actor does not retrieve account details or call the users/me endpoint.

Output example

{
"orderingPlatform": "ChowNow",
"status": "SUCCESS",
"restaurantName": "Example Kitchen",
"restaurantPhone": "+12125550100",
"streetAddress": "12 Main St",
"city": "New York",
"state": "NY",
"zipCode": "10001",
"menuCategory": "Lunch",
"itemId": "soup-1",
"itemName": "Tomato Soup",
"itemDescription": "Slow-cooked tomato soup.",
"basePrice": 8,
"currency": "USD",
"dietaryLabels": "Vegan",
"hasModifiers": true,
"modifierGroupsSummary": "Extras [min 0, max 2]: Bread (USD 1.50), Croutons (USD 0.00)",
"itemImageUrl": null,
"sourceUrl": "https://restaurant.example.com/menu",
"scrapedAt": "2026-10-20T12:00:00.000Z",
"errorMessage": null
}

This example is illustrative. Menu text is converted to plain text. Potential spreadsheet formulas are prefixed with an apostrophe for safer CSV exports, while valid phone numbers and numeric prices are preserved. Render extracted text as text in downstream applications.

Use the results in Clay, n8n, Make, or your API

  • Clay: Import the exported CSV or connect your Apify workflow. Match restaurant domains and names to enrichment tables.
  • n8n: Run the Actor with an Apify node, wait for completion, then read the run's defaultDatasetId. Filter status to SUCCESS before enrichment.
  • Make: Trigger an Apify run, retrieve dataset items, and route records to Sheets, Airtable, or your CRM.
  • API: Start the Actor through Apify's API and fetch items from the default dataset returned in the run. Use sourceUrl and itemId as a comparison key across scheduled runs.

Apify API documentation describes authenticated runs, dataset exports, and scheduling integrations.

Pricing and limits

The custom pay-per-event event is menu-item-scraped. Each saved SUCCESS item triggers that event only when it is configured in the run's pricing information. ERROR rows cost $0 in custom event fees. Network failures remain visible in the dataset.

For a concrete illustration, at an event price of $0.001 per successful item, 1,000 successful items cost $1.00 in event fees. Adding 100 error records adds $0.00 in custom event fees. This is an arithmetic example; the actual event price must be configured and displayed in Apify Console. Compute, storage, proxy charges, or other platform fees can still apply.

Free Apify accounts are limited to 25 saved records per run, including SUCCESS and ERROR records. Paying accounts follow maxItems, up to 10,000 records. Local runs follow maxItems without the free-account cap. Reaching either the record cap or the event budget stops new work, cancels in-flight requests, flushes buffered records, and exits cleanly.

Batches flush every 100 records or five seconds. Error batches are pushed without the custom event name. Success batches are serialized and restricted to the remaining chargeable budget.

Apify's pay-per-event documentation explains event setup and budget behavior.

FAQ and troubleshooting

Which ordering URLs work?

ChowNow URLs must contain both company and location IDs, such as /order/25580/locations/37827. Square Online subdomains and custom domains exposing public storefront identifiers are supported. BentoBox and other restaurant websites must publish JSON-LD MenuItem data, MenuItem microdata, or recognizable semantic menu cards.

Does this require a browser or an API key?

Extraction uses public HTTP endpoints and page markup. Merchant authentication is not required. Private dashboards, ordering checkout sessions, and login-only catalogs are outside the extraction scope.

Why did a target produce an ERROR row?

The restaurant may be inactive, blocked, unavailable, or missing a publicly readable menu. Error codes identify DNS, TLS, timeout, HTTP, unsafe URL, oversized payload, or missing-menu problems. Per-restaurant failures do not fail the whole run. Invalid Actor input exits with code 1.

Why are some fields null?

A site may omit a phone number, address, image, currency, or price. The Actor preserves missing values rather than inventing them. Schema.org menus often lack modifier details.

Are PDF or image menus supported?

Provide a public HTML ordering or menu page. OCR, PDF menu extraction, and menus rendered solely by JavaScript without a public catalog or embedded data are outside this Actor's supported formats. When a homepage links to an HTML menu, the Actor tries up to three same-origin menu links.

How are large or interrupted responses handled?

Decompressed limits are 4 MiB for HTML, 2 MiB for JSON, and 15 MiB for XML requests. Oversized HTML can retain its prefix. A timeout or connection reset after a successful response and at least 16 KiB of HTML can also retain a partial page. Only complete recognized menu items are emitted; an incomplete JSON response is rejected. A warning identifies partial HTML. User cancellation does not salvage in-flight responses.

How are requests secured?

Targets and each of up to six redirect hops are checked. Private, local, reserved, and mixed public/private DNS results are blocked. Requests pin validated DNS addresses, preserve domain authority, and verify TLS certificates. JSON pollution keys and external markup declarations are rejected or removed. JSON and HTML nesting are capped at 128 levels.

Development and deployment

Requires Node.js 22 or newer.

npm ci
npm run build
npm test
npm run smoke
npm run verify:local

npm test uses node:test with deterministic fixtures and no network dependency. npm run smoke makes live, direct requests to public Square and BentoBox examples and reports timing and process memory. Smoke targets can be passed as command-line arguments. npm run verify:local launches isolated SDK processes for a live Square menu, an inactive source, and invalid input. All child runners use UV_THREADPOOL_SIZE=64 and NODE_ENV=production.

For a local Actor run, place a valid input in storage/key_value_stores/default/INPUT.json, then run npm start. Local dataset records appear under storage/datasets/default. The examples/input.json file contains a small live sample.

The multi-stage Dockerfile builds on node:22-alpine, installs from the lockfile, and runs as the non-root node user. It sets UV_THREADPOOL_SIZE=64, NODE_ENV=production, and a bounded V8 heap. The default Apify run uses 256 MiB and a 3,600-second run timeout.

When deploying in Apify Console, select LIMITED_PERMISSIONS, configure the menu-item-scraped pricing event and its price, and build from this repository. Limited-permission compatibility is enforced by avoiding account-profile API calls and using the current run's default storage. Publishing and pricing configuration happen in Apify Console; local source files do not activate monetization.

The implementation was validated against the ChowNow public ordering application, Square storefront markup, and the Schema.org Menu vocabulary.

The lockfile overrides the SDK's transitive basic-ftp dependency to 6.2.3 to include the upstream parser security fixes. FTP target URLs remain unsupported.