A recipe with the right structured data appears in search as a card: photo, star rating, cooking time, calories, and on mobile in the recipe carousel. A recipe without it is a blue link between the cards. The markup is not hard, but it has more fields than most types, two of them (durations and instructions) have formats people get wrong, and the rating rules are enforced. This guide gives the full JSON-LD, explains each field, and covers the plugin situation on WordPress, where most recipes live.
Quick answer
- Required for the rich result:
nameandimage. Everything else is recommended, and the card is thin without it. - Add
author,datePublished,description,prepTime,cookTime,totalTime(ISO 8601 durations:PT1H30M),recipeYield,recipeCategory,recipeCuisine,keywords,nutrition.calories. recipeIngredientis an array of strings, one per ingredient.recipeInstructionsis an array ofHowToStepobjects, one per step.- Ratings go in
aggregateRatingon theRecipe, from real ratings visible on the page. - One
Recipeper page. On a roundup page, anItemListof links to the recipe pages. - Run the schema validator after any change; then Google's Rich Results Test for the preview.
Why recipe markup matters
Recipe search is almost entirely rich results. A query like "banana bread" returns a carousel of cards on mobile and card-style results on desktop, each with an image, a rating, a time and calories. A plain listing next to them gets a fraction of the clicks. The card is built only from Recipe structured data; Google does not infer ingredients from the page.
The same markup feeds Google's recipe filters ("under 30 minutes", "vegetarian") and the image search recipe badge. So the fields that look optional decide which filters your page qualifies for.
How getReport checks it
The validator parses every JSON-LD block on the page, lists the root types, checks each known type against its required-property table, and flags duplicates. Recipe is in the table, with the same two required properties Google lists: name and image.
Recipe blocks are long and often assembled by a plugin from user-entered text, so this is where they break: a quotation mark inside an ingredient ("1 cup "00" flour"), a stray comma after the last step. The finding names the block number and the parser message; the whole block is invisible to Google until it parses.
On a recipe post you should see Recipe plus the article and site entities your SEO plugin adds (BlogPosting, WebSite, Organization, BreadcrumbList). If the list shows Recipe ×2, or HowTo appears alongside it, read the platform notes below; when the two recipes share a name and URL the duplicate-entities finding names them too. The rich results page lists every type the validator has a table for.
For Recipe the check is name and image. Everything else in this guide is beyond what the validator tests: it confirms the block is present, parses and has the two required fields, and does not check duration formats, ingredient arrays or rating counts. Those rules are yours to keep, and the Rich Results Test enforces some of them.

Google's requirements
Google's recipe documentation lists two required and many recommended properties. The recommended ones are what fill the card:
| Property | Type | What it shows |
|---|---|---|
name (required) | Text | The recipe title |
image (required) | URL or array | The card photo; several aspect ratios |
aggregateRating | AggregateRating | The stars and count |
author | Person or Organization | The byline |
prepTime, cookTime, totalTime | Duration (ISO 8601) | "1 hr 30 min" on the card; the time filter |
datePublished | Date | Freshness |
description | Text | The snippet |
keywords | Text | Comma-separated, for search matching |
nutrition.calories | NutritionInformation | "320 calories" on the card |
recipeCategory, recipeCuisine | Text | "Dessert", "Italian"; the filters |
recipeIngredient | Text array | Ingredient filters ("without eggs") |
recipeInstructions | HowToStep array | The steps |
recipeYield | Text | "4 servings" |
video | VideoObject | The video thumbnail and a possible video result |
Two surfaces are worth knowing. The recipe card and the mobile carousel are live and built from the recipe page's own Recipe. A host page (a roundup like "12 soups for autumn") can carry an ItemList of ListItems whose url points at each recipe page, and Google may show a carousel for it. The guided recipes that once fed Google Assistant are no longer part of Google's recipe documentation; ignore old advice about them.
Step by step
1. The complete block
On the recipe page, once, in a <script type="application/ld+json">:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Recipe",
"name": "Štrukli with fresh cheese",
"image": [
"https://example.com/img/strukli-1x1.jpg",
"https://example.com/img/strukli-4x3.jpg",
"https://example.com/img/strukli-16x9.jpg"
],
"author": { "@type": "Person", "name": "Ana Kovač" },
"datePublished": "2026-09-12",
"description": "Baked štrukli with a soft cheese filling, the way they are made in Zagorje.",
"prepTime": "PT40M",
"cookTime": "PT35M",
"totalTime": "PT1H15M",
"keywords": "štrukli, cheese pastry, croatian, baked",
"recipeYield": "6 servings",
"recipeCategory": "Main course",
"recipeCuisine": "Croatian",
"nutrition": {
"@type": "NutritionInformation",
"calories": "410 calories"
},
"recipeIngredient": [
"500 g plain flour",
"1 egg",
"250 ml lukewarm water",
"600 g fresh cow's cheese",
"200 ml sour cream",
"50 g butter",
"1 tsp salt"
],
"recipeInstructions": [
{
"@type": "HowToStep",
"name": "Make the dough",
"text": "Mix the flour, egg, water and salt into a smooth dough. Rest it under a bowl for 30 minutes.",
"url": "https://example.com/recipes/strukli/#step-1",
"image": "https://example.com/img/strukli-step-1.jpg"
},
{
"@type": "HowToStep",
"name": "Stretch and fill",
"text": "Stretch the dough thin over a floured cloth, spread the cheese mixed with sour cream and egg, and roll it up using the cloth.",
"url": "https://example.com/recipes/strukli/#step-2"
},
{
"@type": "HowToStep",
"name": "Bake",
"text": "Cut into pieces, lay in a buttered dish, pour over cream and bake at 200 °C for 35 minutes until golden.",
"url": "https://example.com/recipes/strukli/#step-3"
}
],
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.7",
"ratingCount": "38"
},
"video": {
"@type": "VideoObject",
"name": "How to stretch štrukli dough",
"description": "Stretching the dough thin over a cloth without tearing it.",
"thumbnailUrl": "https://example.com/img/strukli-video.jpg",
"contentUrl": "https://example.com/video/strukli.mp4",
"embedUrl": "https://example.com/video/strukli/embed",
"uploadDate": "2026-09-12T09:00:00+02:00",
"duration": "PT2M40S"
}
}
</script>2. Durations, the field most often wrong
prepTime, cookTime and totalTime are ISO 8601 durations, not text: PT15M is fifteen minutes, PT1H an hour, PT1H30M ninety minutes, P1D a day for something that rests overnight. "30 minutes" or "1.5 hours" is rejected. totalTime should equal prep plus cook; if you give only one of prep and cook, give totalTime too, because the card and the time filter read it.
3. Ingredients and instructions as arrays
recipeIngredient is one string per ingredient, with quantity and unit in the string: "250 ml lukewarm water". A single string with line breaks, or an HTML list pasted in, is one ingredient as far as Google is concerned.
recipeInstructions is an array of HowToStep objects with text; name, url (an anchor on the page) and image per step are optional but feed the step-by-step display. Long recipes can group steps in HowToSection objects with itemListElement arrays of steps. A single block of text is accepted but shows as one paragraph. Do not mark the recipe as a separate HowTo as well; HowTo no longer earns a rich result and a second entity for the same steps is a duplicate.
4. Ratings: real, visible, on the Recipe
aggregateRating needs ratingValue and either ratingCount or reviewCount. The rules Google enforces: the ratings must come from visitors, the same rating must be visible on the page (a star widget or "4.7 from 38 ratings"), and there must be at least one. A recipe with "ratingCount": "0" or a rating the page does not show fails a review snippet check and, repeated across a site, can cost the site its review snippets. Put the rating on the Recipe itself; do not add a separate Review block per rating or a Review of your own recipe.
5. Images
image accepts one URL or an array. Google's guidance for recipes asks for images that are crawlable, that belong to the recipe (the finished dish, not a stock photo of ingredients), and offered in the 1:1, 4:3 and 16:9 aspect ratios so each surface can pick its crop. 1200 px wide covers every surface, including Discover. The image must not be blocked in robots.txt, which is where CDN image paths sometimes are.
6. Video
A VideoObject on the recipe shows a thumbnail on the card and can earn a video result. Required by Google for the video object: name, thumbnailUrl, uploadDate, and contentUrl or embedUrl (the validator's own required list is name, thumbnailUrl, uploadDate). duration in ISO 8601 and description are recommended. Key moments are optional: hasPart with Clip objects carrying startOffset, endOffset and url with a timestamp.
7. The long story before the recipe
The narrative before the recipe card does not affect the structured data; Google reads the JSON-LD regardless of where the card sits. It affects visitors, who have to scroll, and time on page, which matters less than people think. A "Jump to recipe" link at the top, pointing at the card's id, is the standard answer and the recipe plugins add it. Keep the card itself complete and visible: the markup must match what the page shows, so the ingredients and steps in the JSON-LD have to be on the page as text.
8. Validate
Run the schema validator on the recipe URL. Read the parse finding first, then types detected (one Recipe, one article entity), then required properties. Then paste the URL into Google's Rich Results Test (linked from the Recipe structured data documentation), which checks the formats above and shows a preview of the card. The JSON-LD basics guide covers how the two tools differ and how to read a parser error.
Platform notes
WordPress. The three common recipe plugins, WP Recipe Maker, Tasty Recipes and Create by Mediavine, all print a Recipe JSON-LD block from the recipe card's fields and add a star rating widget whose votes feed aggregateRating. The block is only as complete as the card: leave the prep time or the servings empty and the property is missing. Run the validator on one recipe, open the types-detected finding, and check that there is exactly one Recipe. Two means the theme, a second plugin or an SEO plugin's "Recipe" schema type is also printing one; turn the extra one off. Yoast and Rank Math both hand recipe markup to the recipe plugin when one is active, but a manually chosen schema type on the post overrides that.
Squarespace and Wix. Neither has recipe fields or recipe structured data; the block has to be added by hand in a code block per page, or the site does without the card. Keep the durations and arrays exactly as in the example.
Shopify. Recipe pages on a food shop are usually blog posts; recipe apps exist and print the block, or add it in a theme section with metafields. Do not combine Recipe and Product for the same item on one page unless the page genuinely sells the dish.
Verify
- The validator's parse finding passes,
Recipeappears once under types detected, and the required-properties finding passes forRecipe. - The Rich Results Test shows "Recipe" as eligible with no errors; work through its warnings for missing recommended fields.
- The rating, times and ingredients in the JSON-LD match what the page shows.
- After a few weeks, the recipe appears as a card with stars and a time in a search for its name.
Common mistakes
- Durations as text.
"cookTime": "30 minutes"is invalid. UsePT30M. - Ingredients as one string. A single
recipeIngredientwith commas or line breaks is one ingredient. One array element per ingredient. - Instructions as one string. Accepted, but no steps on the card. One
HowToStepper step. - Ratings with zero reviews.
ratingCount: 0or a rating the page does not display violates the review snippet guidelines. Show the widget or removeaggregateRating. - A second Recipe block from the theme. Two
Recipeentities for one dish; types detected showsRecipe ×2and, when they share a name and URL, the duplicate finding names both. Keep the plugin's, remove the theme's. - Marking every post as a recipe. A plugin setting that adds a
Recipeblock to posts without a recipe card earns a warning from Google and nothing else. Only recipe pages get the block.