Tutorial
Watch a competitor's published menu prices
Add a permitted public menu URL, read the extraction status before the prices, and understand why the result is usually shorter than their real menu.
About 7 minutes. Last reviewed against the product on .
By the end of this you will have one competitor's published menu prices stored beside your own, and you will understand why that list is usually shorter than the menu they actually serve.
1. Put your own menu in first
Open Menu and add the items you want to compare. Without them there is nothing to compare against, and a competitor's price list on its own tells you very little: a dish you do not sell at a price you do not charge is trivia.
2. Add the competitor
Open Competitors. Give the competitor a name, paste the public URL of the page that carries their menu, and tick the permission confirmation. That tick is stored as a record: the site keeps who confirmed it and when.
Before anything is fetched, the URL has to survive several checks. It must use HTTP or HTTPS, it must carry no username or password, and it must not resolve to a local or private address. The site then reads the origin's robots.txt and refuses the path if robots.txt disallows it.
3. Read the status before the prices
Each competitor card shows an extraction status and a message. Read those first, because three of the four outcomes mean there are no prices to look at.
| Status | What happened | What to do |
|---|---|---|
| Processing | The row was created and no result has been recorded yet | Submit the URL again |
| Completed | Structured menu items were found and stored | Compare them with your menu |
| Empty | The page published no MenuItem or Product structured data | Nothing here can be read this way |
| Failed | A check or the fetch stopped it, and the card states the reason | Read the message before retrying |
Measured from the competitor server action at app/app/competitors/actions.ts.
An empty result is the common one, and it is a statement about their markup rather than about their prices. Only structured data embedded in the page is read. A menu that is an image, a PDF, or ordinary HTML text produces nothing at all, no matter how clearly a person can read it.
- Fetch timeout
- 12 seconds
- per request, including the robots.txt read
- Redirects followed
- 5
- each hop is re-checked against the same URL rules
- Page size read
- 2 MB
- a page declaring more is refused, an undeclared one is cut here
- Items stored
- 500 maximum
- per competitor, after duplicates are removed
Measured from the competitor extractor at src/lib/competitors/extractor.ts.
4. Compare only what is comparable
A stored price is what that page published, in the currency that page declared. It is not their cost, not their margin, and not necessarily current: a menu page can sit stale for months and nothing here can detect that. Portion size, inclusions and service model are not captured at all, so two dishes sharing a name are not evidence of the same offer.
5. Leave with one item
Choose the single dish where their published price and yours differ most, and decide one thing about it: hold, change, or find out more. A whole-menu response to a competitor's whole menu is the reliable way to act on nothing.
What is measured and what is assumed
Measured: the item names, descriptions, prices and currency codes that page published as structured data, the URL they were read from, and the moment they were read.
Assumed: that the published structured data matches what the kitchen actually charges, and that a matching dish name means a comparable dish.
Not available at all: history. Re-submitting the same URL replaces that competitor's stored items rather than adding to them, and nothing re-runs on a schedule. There is no record of what a competitor charged last month, so this surface can answer what they publish now and cannot answer whether they have raised it. Someone with viewer access cannot add a competitor or trigger an extraction.