Skip to content
    ↑↓ to choose · Enter to open

    · 13 min read

    Recipe Scraper (Universal / schema.org): 24 Data Fields per Record

    By CrawlerBros Engineering Team

    Each record carries 24 output fields extracted directly from schema.org JSON-LD blocks on supported culinary publishers and food blogs. The free-plan price is $5.00 per 1,000 results, yielding standardized ingredient strings, step-by-step cooking instructions, prep and cook durations, and nutrition facts without manual selector maintenance. This Actor is built for engineers constructing meal-planning catalogs, nutrition calculators, and SEO teams tracking recipe schema performance. It is not for anyone who needs AllRecipes data, which this Actor rejects upfront.

    Try it before you read further. Apify's free plan includes $5.00 of usage every month with no credit card, enough for up to 1,000 results at $0.005 each before platform usage. Open Recipe Scraper (Universal / schema.org) on Apify and run the prefilled example.

    How reliable is Recipe Scraper (Universal / schema.org) in production?

    Across the last 30 days of public runs on the Apify platform, Recipe Scraper (Universal / schema.org) recorded 58 runs with the following outcomes.

    Outcome Runs Share
    Succeeded 39 67.2%
    Failed 16 27.6%
    Aborted by the user 2 3.4%
    Timed out 1 1.7%
    Total 58 100.0%

    When running this Actor unattended, expect about 29 in a hundred runs to fail or time out, which represents roughly one run in 3. Production schedulers must implement automated retry routines and alerting on failed exit statuses. Ensure your pipeline handles non-recipe failures gracefully without stalling batch jobs when individual target pages drop connection or block access.

    What does it cost to run Recipe Scraper (Universal / schema.org)?

    Each result costs $0.005 on Apify's free plan, which is $5.00 per 1,000 results. Starting a run is charged separately at $0.005 per GB of Actor memory. Apify also bills the platform usage each run consumes, at the rates of your Apify plan, on top of these charges.

    Apify plan Per result Per 1,000 results
    FREE $0.005 $5.00
    BRONZE $0.00433 $4.33
    SILVER $0.00367 $3.67
    GOLD $0.003 $3.00
    PLATINUM $0.003 $3.00
    DIAMOND $0.003 $3.00

    Worked example: collecting 10,000 results costs $50.00 in result charges before run-start fees and platform usage. With 29.3% of runs failing or timing out in the last 30 days, budget for re-running a portion of those batches rather than assuming every run completes.

    The primary lever affecting your result charges is maxItems, alongside the length of your recipeUrls, tagUrls, or sitemapUrls arrays. Because the run-start fee is charged every time a run starts, whether or not it returns results, launching multiple tiny runs increases baseline spend. The first run caps maxItems at 50, so it returns at most 50 results and costs at most $0.25 in result charges, making it the most cost-effective way to validate parsing accuracy.

    How do you run Recipe Scraper (Universal / schema.org) from the API?

    The schema marks 1 of its 10 controls as required: mode. Nothing in the payload below is illustrative. Those are the schema's prefilled defaults for Recipe Scraper (Universal / schema.org), so the request works once your token is in place.

    Call the synchronous endpoint to start a run and receive dataset items in one request:

    curl -X POST "https://api.apify.com/v2/acts/crawlerbros~recipe-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"mode":"byUrls","recipeUrls":["https://www.epicurious.com/recipes/food/views/banana-bread"],"tagUrls":[],"sitemapUrls":[],"proxy":{"useApifyProxy":true,"apifyProxyGroups":["RESIDENTIAL"]},"maxItems":50}'
    

    The same run from Python, using the official client:

    from apify_client import ApifyClient
    
    client = ApifyClient("<YOUR_APIFY_TOKEN>")
    
    run_input = {
      "mode": "byUrls",
      "recipeUrls": [
        "https://www.epicurious.com/recipes/food/views/banana-bread"
      ],
      "tagUrls": [],
      "sitemapUrls": [],
      "proxy": {
        "useApifyProxy": True,
        "apifyProxyGroups": [
          "RESIDENTIAL"
        ]
      },
      "maxItems": 50
    }
    
    run = client.actor("crawlerbros~recipe-scraper").call(run_input=run_input)
    
    for item in client.dataset(run["defaultDatasetId"]).iterate_items():
        print(item)
    

    And from Node.js:

    import { ApifyClient } from 'apify-client'
    
    const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' })
    
    const input = {
      "mode": "byUrls",
      "recipeUrls": [
        "https://www.epicurious.com/recipes/food/views/banana-bread"
      ],
      "tagUrls": [],
      "sitemapUrls": [],
      "proxy": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
          "RESIDENTIAL"
        ]
      },
      "maxItems": 50
    }
    
    const run = await client.actor('crawlerbros~recipe-scraper').call(input)
    const { items } = await client.dataset(run.defaultDatasetId).listItems()
    console.log(items)
    

    Because the call is synchronous, your client waits for the whole run. Keep it for exploration. For scheduled work, start the run without waiting and collect the dataset afterwards, so network trouble costs you a retry rather than the results.

    Which Recipe Scraper (Universal / schema.org) inputs matter, and which can you skip?

    The primary control is mode, which selects whether the run processes raw URLs, tag categories, or sitemaps. Pair this with recipeUrls, tagUrls, or sitemapUrls depending on your chosen ingestion strategy. Leave optional filtering parameters like minRating, minRatingCount, and keywordIncludes empty during initial connectivity tests.

    • mode (string): How to source recipe URLs. Default: "byUrls".
    • recipeUrls (array): Recipe URLs from any schema.org-compliant site (Epicurious, BBC Good Food, Tasty, NYT Cooking, Bon Appétit, Serious Eats, Food Network, King Arthur Baking, food blogs, etc.). AllRecipes URLs are rejected as unsupported. Default: ["https://www.epicurious.com/recipes/food/views/banana-bread"].
    • tagUrls (array): Epicurious tag/category URLs OR bare slugs. Examples: https://www.epicurious.com/recipes/category/main-course, /ingredient/banana, dessert. The actor scrapes the tag page → finds recipe URLs → fetches each. Default: [].
    • sitemapUrls (array): Sitemap.xml URLs from any recipe site (e.g. https://www.bbcgoodfood.com/sitemap.xml, https://www.epicurious.com/sitemap.xml, or a deeper recipe-only sitemap). The actor walks the sitemap (one level deep into sitemap indexes), filters recipe-looking URLs, and runs them through the JSON-LD pipeline. Default: [].
    • minRating (integer): Drop recipes with ratingValue below this. Scale 0-500 (e.g. 400 = 4.0/5).
    • minRatingCount (integer): Drop recipes with fewer than this many ratings.
    • maxTotalTimeMinutes (integer): Drop recipes whose prep + cook time exceeds this many minutes.
    • keywordIncludes (string): Drop recipes whose name + description don't contain this keyword.
    • proxy (object): Apify Proxy is optional. For Epicurious / Tasty / BBC Good Food / NYT Cooking / most food blogs, no proxy is needed; datacenter or residential both work. Provided here for sites that occasionally rate-limit. Default: {"useApifyProxy":true,"apifyProxyGroups":["RESIDENTIAL"]}.
    • maxItems (integer): Hard cap on emitted records. Default: 50.

    Fixed-choice controls: mode accepts byUrls (supply a list of direct recipe URLs), byTag (supply Epicurious category/ingredient tag URLs (the actor crawls them)), bySitemap (supply sitemap.xml URLs from any recipe site (the actor extracts recipe URLs)).

    What does Recipe Scraper (Universal / schema.org) return?

    Output records provide normalized arrays for ingredients and cooking directions alongside aggregate ratings and cooking times. The data conspicuously omits individual reviewer comment text and step-by-step instruction photography. If your workflow requires individual review sentiment or full reader comment threads, this dataset will not satisfy your requirements.

    • url: Canonical recipe URL
    • id: Same as url
    • platform: Site slug (epicurious, tasty, bbcgoodfood, nytcooking, bonappetit, seriouseats, …)
    • name: Recipe title
    • description: Short blurb (HTML stripped)
    • image: Hero image URL
    • author: {name, [url]}
    • ratingValue: Average rating 0-5
    • ratingCount: Number of ratings
    • reviewCount: Number of written reviews
    • prepTimeMinutes: Prep time in minutes
    • cookTimeMinutes: Cook time in minutes
    • totalTimeMinutes: Total time (uses totalTime if present, else prep + cook)
    • recipeYield: Servings / pieces (e.g. 8 slices)
    • recipeCategory: Array (e.g. ["Dessert"])
    • recipeCuisine: Array (e.g. ["American"])
    • keywords: Array of free-form keyword tags
    • recipeIngredient: Array of ingredient strings
    • recipeInstructions: Array of step strings
    • nutrition: {calories, proteinContent, carbohydrateContent, fatContent, sodiumContent, …}
    • video: {url, name, thumbnailUrl} if present
    • datePublished: ISO publish date
    • dateModified: ISO last-modified date
    • scrapedAt: ISO 8601 UTC timestamp

    These are the documented fields. Optional ones can be empty on a given record, so measure how often each field your deliverable depends on is populated across a real sample before automating the handoff.

    How do you build the workflow end to end?

    Open Recipe Scraper (Universal / schema.org) and work through these in order. Each step ends with something to check, so a bad configuration surfaces on a small run rather than a scheduled one.

    1. Select byUrls for mode and supply a single known schema.org URL in recipeUrls, leaving all filtering parameters unset.
    2. Execute the Actor and confirm that the dataset item has recordType set to recipe rather than url_failed.
    3. Verify that the returned object populates recipeIngredient and recipeInstructions as clean arrays rather than empty lists.
    4. Inspect optional attributes like nutrition, prepTimeMinutes, and ratingValue to gauge how thoroughly the target domain implements schema.org markup.
    5. Switch mode to bySitemap or byTag if crawling full archives, and provide a test endpoint in sitemapUrls or tagUrls.
    6. Set maxItems to 50 to cap initial dataset output and keep result charges at most $0.25 on the initial run.
    7. Scan output logs for sentinel records with reason set to anti_bot_block or no_recipe_jsonld to confirm domain compatibility before scaling.

    How do you apply it? Three worked playbooks

    These are Recipe Scraper (Universal / schema.org)'s own documented use cases, each worked through as an operating pattern rather than a description.

    Use case 1: Recipe SEO / content audits

    Outcome: Pull schema.org Recipe data across competitor sites for content gap analysis.

    Configure: Set mode to byUrls, populate recipeUrls with competitor article links, leave minRating unset, and set maxItems to 50.

    Working method: Execute against competitor recipe URLs and compare schema completeness across keywords, recipeCategory, and aggregate rating markup.

    Deliverable: A comparative spreadsheet detailing field-level schema coverage, ingredient naming structures, and review volumes across targets.

    Stop condition: Emitted records show recordType as url_failed with reason set to anti_bot_block.

    Use case 2: Meal-planning apps

    Outcome: Build recipe libraries by ingesting curated URL lists.

    Configure: Set mode to bySitemap, supply a target sitemap in sitemapUrls, set maxTotalTimeMinutes to 60, and set maxItems to 200.

    Working method: Parse recipe targets extracted from sitemaps, automatically discarding dishes whose total preparation time exceeds the threshold.

    Deliverable: A normalized recipe database containing ingredient arrays, step instructions, servings from recipeYield, and hero image URLs.

    Stop condition: The count of emitted recipe items reaches maxItems or the sitemap feeder produces no recipe-matching URLs.

    Use case 3: Nutrition trackers

    Outcome: Standardize ingredient + nutrition data across sources.

    Configure: Set mode to byTag, enter Epicurious ingredient tags in tagUrls such as /ingredient/banana, and set maxItems to 100.

    Working method: Crawl ingredient collections to ingest uniform food listings, extracting macro values from the nutrition nested object.

    Deliverable: A dataset mapping parsed recipeIngredient lists to standardized calories, proteinContent, and carbohydrateContent values.

    Stop condition: Over half of the parsed records arrive without a populated nutrition dictionary.

    What breaks, and how do you design around it?

    • The actor reads schema.org/Recipe JSON-LD only. Sites that don't embed it (or use a custom microformat) won't yield data.
    • AllRecipes specifically requires residential proxy; without one you'll get a recipe_blocked sentinel.
    • Some sites strip nutrition data or vary on prepTime / cookTime; missing fields are simply omitted.
    • Per-comment / per-review data isn't included (only aggregate ratingCount / reviewCount).
    • Video transcripts / step-by-step photos are not captured.

    Extraction relies entirely on the presence of valid schema.org/Recipe JSON-LD tags, and pages lacking them generate a failure record rather than falling back to layout scraping. When target blogs omit nutrition data or cooking times, those fields are dropped from the JSON payload. For unsupported properties like AllRecipes, switch to supported publishers rather than altering proxy configurations.

    When should you not use Recipe Scraper (Universal / schema.org)?

    Do not run this Actor if your project requires recipe data from AllRecipes, because Akamai blocks requests to that domain and this tool rejects those URLs upfront. If your application needs raw grocery ingredient barcodes and standardized industrial nutritional profiles rather than home cooking recipes, use the Open Food Facts Scraper instead. If you want a static, community-curated culinary library with simple endpoints that require no dynamic HTML fetching, query TheMealDB Recipe Scraper which provides ingredient ratios and instructions without relying on third-party HTML parsing.

    What should you check before trusting the output?

    • Discard records where recipeIngredient is absent or contains zero elements.
    • Flag records where recipeInstructions fails to return a populated array of distinct step strings.
    • Check whether the nutrition object is present when building dietary calculators, as many food blogs omit calorie and macronutrient schema fields.
    • Abort downstream ingestion if recordType matches url_failed with reason equal to unsupported_site or anti_bot_block.

    None of this proves a record is correct. It gives a scheduled Recipe Scraper (Universal / schema.org) run defined points where it should stop instead of quietly passing bad data downstream.

    Frequently asked questions

    What does this Actor cost on Apify's free tier?

    Apify's free plan includes $5.00 of monthly usage with no credit card, covering up to 1,000 results of this Actor. Results are billed at $0.005 per item written, which equals $5.00 per 1,000 results on the free tier. The run-start fee is charged every time a run starts, whether or not it returns results, and Apify bills platform usage separately.

    Why does the scraper reject AllRecipes links?

    AllRecipes sits behind Akamai Bot Manager, which rejects requests from both datacenter and residential IPs. To prevent wasted run costs and hanging tasks, the Actor intercepts AllRecipes URLs upfront and writes a url_failed record with reason marked as unsupported_site.

    What happens if a recipe URL lacks schema.org markup?

    The Actor reads schema.org/Recipe JSON-LD only. If a target webpage uses legacy microformats or lacks standard JSON-LD structures, the scraper emits a url_failed item with reason set to no_recipe_jsonld instead of returning an incomplete recipe record.

    How are preparation steps formatted in the output?

    Instructions are delivered as an array of individual strings. The scraper parses HowToStep and HowToSection nodes within the recipe schema, stripping all HTML formatting to provide clean text steps in the recipeInstructions field.

    How reliable is this Actor in automated production pipelines?

    In the last 30 days: 58 runs, 39 succeeded (67.2%), 16 failed (27.6%), 2 aborted by users (3.4%), 1 timed out (1.7%). Together, 29.3% of runs failed or timed out, about 29 in a hundred, roughly one run in 3. Unattended setups must implement retry handlers to manage failures.

    Where to go next

    When you are ready to run it, open Recipe Scraper (Universal / schema.org) on Apify; the free plan covers up to 1,000 results a month.

    Other Actors we maintain for related data:

    Related guides:

    Resources

    • Actor documentation, input schema, and pricing: verified against the published Actor on 2026-09-27.

    • Actor last updated by its maintainers on 2026-05-08.

    • Run outcome figures cover the 30 day public window ending 2026-09-27.

    • Recipe Scraper (Universal / schema.org) on Apify

    Featured actors

    Recipe Scraper (Universal / schema.org)

    Scrape any schema.org-compliant recipe site like Epicurious, BBC Good Food, Tasty, NYT Cooking, Serious Eats, Food Network, plus thousands of food blogs. Extracts ingredients, instructions, nutrition, ratings, prep/cook time, yield, author, and images via JSON-LD parsing.

    Run on Apify ↗