Getting your menu into search results
A menu published as a photograph or a PDF is invisible to search engines: no dish, price or dietary note can be read from it. Marking the menu up as JSON-LD - a Menu containing MenuSection objects containing MenuItem objects, each with its own Offer and price - makes every item machine-readable, which is what allows a dish to appear in a search result or be recommended by an assistant.
Why a picture of a menu is worth nothing
Most restaurant sites publish the menu as an image or a PDF. Both are opaque. A search engine looking at a photograph of a menu board sees a photograph. It cannot tell you have a carbonara, it cannot tell what it costs, and it cannot offer you to somebody asking an assistant where to eat nearby.
That is the single most valuable content on the site, thrown away.
The shape of the data
Menu markup nests in a specific order, and getting the nesting wrong is the most common failure.
A Restaurant has a Menu, a Menu has MenuSection objects, and a MenuSection has
MenuItem objects. Prices live on an Offer attached to the item.
{
"@context": "https://schema.org",
"@type": "Restaurant",
"@id": "https://example.com/#restaurant",
"name": "Example Kitchen",
"servesCuisine": "Italian",
"hasMenu": {
"@type": "Menu",
"@id": "https://example.com/menu/#menu",
"name": "All day",
"hasMenuSection": [
{
"@type": "MenuSection",
"name": "Pasta",
"hasMenuItem": [
{
"@type": "MenuItem",
"name": "Carbonara",
"description": "Guanciale, pecorino, egg yolk, black pepper",
"suitableForDiet": "https://schema.org/LowLactoseDiet",
"offers": {
"@type": "Offer",
"price": "14.50",
"priceCurrency": "USD"
}
}
]
}
]
}
}
Three details in there matter more than they look:
priceCurrencyis required alongsideprice. A price with no currency is ambiguous and gets ignored.priceis a string without a symbol."14.50", not"$14.50"and not14.50.@idon the restaurant lets every other page reference the same entity instead of declaring a new one. A site that declares five unlinkedRestaurantnodes has told search engines it is five restaurants.
Link it, do not repeat it
The menu page declares the full Menu. Every other page should reference it by @id rather than
restating it:
{ "@type": "Restaurant", "@id": "https://example.com/#restaurant",
"hasMenu": { "@id": "https://example.com/menu/#menu" } }
This is the part most implementations miss. Repeating the whole menu on every page creates competing definitions of the same thing.
The mistakes we see most
- The menu is an image. Nothing else on this list matters until this is fixed.
- Items with no price. A
MenuItemwithout anOfferis a name floating in space. - Sections flattened away. Every item hung directly off the
Menuwith noMenuSectionloses the structure a human reader uses. - Markup that disagrees with the page. If the JSON-LD says $14.50 and the page says $16, you have a problem that is worse than having no markup.
AggregateRatinginvented. Covered in the local business guide - this one can get you a manual penalty.
Check it before you ship it
Paste the rendered page into Google’s Rich Results Test and the Schema.org validator. They disagree deliberately: the Schema.org validator tells you whether the vocabulary is correct, and Google’s tells you whether you are eligible for anything it shows. You want both clean.
We audited 190 US restaurant sites and 94% carry no menu markup at all, so doing this puts you ahead of almost everyone in the sector.
Villas Cafe is a worked example - every dish on a single-page cafe site is a
MenuItem inside a MenuSection carrying its own Offer and price.
Running in production
Every technique on this page is live on a client site, not a demonstration written for the article.

Cafe
Villas Cafe
Most cafe sites publish the menu as a picture or a PDF, which means no search engine can read a single dish or a single price…
Read the case study
Restaurant
Enos Restaurant
Restaurant bookings usually arrive through a marketplace that charges per cover and owns the diner relationship. The restaurant…
Read the case studyQuestions we get asked
Does menu markup guarantee a rich result?
No. Structured data makes a page eligible for a rich result; it never guarantees one. Google decides what to show based on the query, the page and its own confidence. What markup reliably does is make the content readable, which matters increasingly for assistants answering questions rather than only for the blue-link result.
Should I mark up prices if they change often?
Yes, but only if you will keep them current. Stale prices in structured data are worse than no prices, because a customer who sees one figure in a search result and another on the page has caught you being careless. If prices move monthly, generate the markup from the same source as the page so the two cannot drift.
Can I mark up a menu that lives in a PDF?
You can add the markup to the HTML page that links to the PDF, but the better answer is to stop using the PDF. A PDF is unreadable on a phone, slow to load, and invisible to most of what you want reading it. The menu is the most valuable content a restaurant has and it belongs in HTML.
Tell us what you need built.
A short call, a straight answer on scope and cost, and a fixed quote. No retainer, no monthly fee, no commission on your orders.