Restaurant Menu Item-Price History avatar

Restaurant Menu Item-Price History

Pricing

from $10.00 / 1,000 results

Go to Apify Store
Restaurant Menu Item-Price History

Restaurant Menu Item-Price History

Track restaurant menu item prices over time. Records each dish's price from public schema.org menu data into an append-only price-history ledger, and computes a menu-price inflation index — the price history a one-shot scraper can't reconstruct.

Pricing

from $10.00 / 1,000 results

Rating

0.0

(0)

Developer

David Liu

David Liu

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

5 days ago

Last modified

Share

Restaurant Menu Item-Price History (Keystone N1 probe)

Probe-grade v0 of Keystone candidate N1 / GATE C1: a niche time-series scraper that extracts per-item restaurant menu prices and appends them to a never-delete, append-only history ledger. The accumulating dated price series is the product: a fresh, memoryless scraper can only see today, so the months of dated history retained here are the practical edge — you had to have been watching the source over time to have it.

Integrity claim (what is provable today). The ledger is append-only and tamper-evident to the operator: it is never rewritten or deleted, and fingerprint() is a self-computed SHA-256 over the file so the operator can detect after-the-fact mutation of their own store. It is not, on its own, cryptographically un-backfillable — a plain self-computed hash does not stop the operator from recomputing it over back-dated rows. Provable un-backfillable integrity (Ed25519 hash-chain + Merkle roots + external anchoring, carried from the Deadman pattern) is a deferred before-scale upgrade; until it ships the public copy claims only "append-only + tamper-evident," never "un-forgeable" or "un-backfillable." (Claim only what is provable — the Deadman discipline.)

Status: feature branch only, probe-grade. Not merged, not deployed, not published to Apify. The human publishes. See CHANGE_NOTE.md.

What it does

  1. Scrape a small, config-driven seed list of restaurants via a pluggable source adapter (default real source: schema.org Menu JSON-LD on each restaurant's own public page).
  2. Append each observation
    {source, restaurant_id, item_id, name, price, currency, captured_at}
    to an append-only JSONL ledger, keyed by (source, restaurant_id, item_id, date) -> price, idempotent (re-running the same day is a no-op; a price change on a later day appends a new dated row; nothing is ever deleted).
  3. Query the retained history: priceHistory(item/restaurant over time) and a menu-price index aggregate (mean/median % price change by cuisine or city over a window).

Layout

PathPurpose
.actor/actor.jsonApify Actor definition
.actor/input_schema.jsonActor input schema (source, seeds, flags)
.actor/dataset_schema.jsonDataset output shape
DockerfileApify Node image; installs apify SDK for the deployed Actor
src/ledger.jsThe product — append-only, idempotent, never-delete ledger
src/query.jsPrice-history + menu-price-index queries
src/scrape.jsSource-adapter dispatcher + polite runner
src/adapters/jsonld.jsDefault real source: schema.org Menu JSON-LD
src/adapters/fixture.jsNo-network adapter (selftest + safe default)
src/config.jsGating flags (default-OFF where they gate behavior)
src/main.jsActor entry (real run). Apify SDK loaded softly; runs standalone too
test/selftest.jsState-neutral, no-network selftest
test/fixtures/Canned menu payloads + a menu-page HTML fixture
seeds/restaurants.example.jsonExample seed list

Run the selftest (no network, mutates no committed state)

$node test/selftest.js # or: npm run selftest

It validates append / dedupe / change / never-delete / query / parser logic on fixtures, and hashes the committed project tree before and after to prove it mutates nothing. Ledger writes go to an OS temp dir that is cleaned up.

Run a real scrape (manual — NOT part of the selftest)

# 1) Build a small seed list of restaurant menu-page URLs (see seeds/).
# 2) Provide it via input.json or Actor input:
cat > input.json <<'JSON'
{ "source": "jsonld",
"seeds": [ { "restaurant_id": "some-place", "url": "https://…/menu", "cuisine": "Italian", "city": "NYC" } ],
"ledgerPath": "./data/price-history.jsonl" }
JSON
node src/main.js

Default source is fixture (no network). A real network run must explicitly opt into jsonld. Politeness (delay + normal UA) is enforced by src/scrape.js. No login, no PII, only the exact seed URLs.

Deferred / follow-ups

See CHANGE_NOTE.md for the source + ToS read and the deferred hardening list (robots.txt preflight, anti-bot resilience, additional adapters, SQLite backend, signed/Merkle ledger integrity carried from the Deadman pattern).