# Recipe Extractor: TikTok, Instagram & YouTube Video to Recipe (`scrape-lads/recipe-extractor`) Actor

Turn TikTok, Instagram Reel, YouTube and recipe-site URLs into clean recipe JSON: ingredients with quantity, unit and item, timestamped steps, times, servings, tags and diets, with confidence per ingredient. Pay only for successful recipes: $30 per 1,000.

- **URL**: https://apify.com/scrape-lads/recipe-extractor.md
- **Developed by:** [Scrape Lads](https://apify.com/scrape-lads) (community)
- **Categories:** AI, Videos, Social media
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $21.00 / 1,000 recipe extracteds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

### What does Recipe Extractor do?

**Recipe Extractor turns a cooking video or recipe page URL into clean, structured recipe JSON.** Paste a **TikTok video, Instagram Reel, YouTube video or Short, or any recipe website**, and get back the title, **ingredients split into quantity, unit and item**, numbered steps with video timestamps, prep and cook times, servings, tags, diets, and optional nutrition. Every ingredient carries a **confidence score and the signal it came from** (caption, spoken transcript, linked recipe page, cover image), so your app can trust the exact amounts and send guesses to review.

It runs on the Apify platform, so you get an API, scheduling, webhooks, and integrations with Make, Zapier, n8n, and MCP-based AI agents out of the box. **You only pay for successful recipes**: failed URLs, non-recipe videos, and videos that only say "comment RECIPE" are free.

### What data can you extract from a recipe video?

| 🍽️ Recipe title | 🖼️ Cover image | 👥 Servings |
|---|---|---|
| ⏱️ Prep, cook and total time | 🥕 Ingredients with amounts | 📋 Step-by-step instructions |
| 🍳 Equipment | 🥗 Nutrition | 🏷️ Tags |
| 📱 Platform | 👤 Creator | 🎯 Confidence score |

### Why use Recipe Extractor?

- **Built for recipe and meal-planning apps.** "Save a recipe from TikTok or Instagram" is the feature every recipe app wants. This gives you the extraction pipeline as one API call instead of a scraper, a speech-to-text step, and a prompt you have to maintain.
- **App-ready schema, not a transcript.** Ingredients come parsed (`2 cups flour, sifted` becomes quantity 2, unit `cup`, item `flour`, note `sifted`), with optional metric or US conversion for grocery lists.
- **Exact recipes when they exist.** Many creators link the full written recipe in their caption. Recipe Extractor follows that link and reads the site's schema.org recipe, so amounts are exact rather than guessed from speech.
- **Multi-signal extraction.** Caption, the platform's own subtitles (TikTok speech-to-text, YouTube captions), linked recipe pages, and the cover frame are merged by an AI model (DeepSeek V4 Flash by default, Claude optional) into one recipe.
- **Guards against invented amounts.** Each ingredient must be backed by a quote from the source. Anything the source never mentions is flagged as `inferred` with low confidence.

### How to extract a recipe from a TikTok, Instagram Reel or YouTube video

1. Open the **Input** tab.
2. Paste one or more URLs (one per line). Short links such as `vm.tiktok.com/...` work too.
3. Pick options: unit conversion, translation, nutrition.
4. Click **Start**. Each URL becomes one row in the **Output** tab, usually within 10 to 30 seconds.
5. Download the results as JSON, CSV, Excel, or HTML, or fetch them through the API.

### Input

| Field               | What it does                                                                                       | Default                             |
| ------------------- | -------------------------------------------------------------------------------------------------- | ----------------------------------- |
| `urls`              | TikTok videos, Instagram Reels and posts, YouTube videos and Shorts, or recipe web pages           | required                            |
| `units`             | `original`, `metric` (adds ml and g for cups, ounces, pounds), or `us` (adds cups, ounces, spoons) | `original`                          |
| `translateTo`       | Write the recipe in another language, for example `English`                                        | empty (keep the creator's language) |
| `includeNutrition`  | Per-serving calories and macros, from the recipe page or estimated                                 | `false`                             |
| `followRecipeLinks` | Read the written recipe linked from the caption                                                    | `true`                              |
| `useCover`          | Show the cover frame to the model                                                                  | `true`                              |
| `useCache`          | Reuse a result from the last 30 days for the same URL and options                                  | `true`                              |
| `maxConcurrency`    | URLs processed in parallel                                                                         | `5`                                 |

```json
{
    "urls": [
        "https://www.tiktok.com/@recipes/video/7447547249720053000",
        "https://www.instagram.com/reel/Dd-VQPnRz0t/",
        "https://youtube.com/shorts/PM4lHUNQSvY"
    ],
    "units": "metric",
    "includeNutrition": true
}
```

### Output

One row per URL. A shortened example:

```json
{
    "inputUrl": "https://www.instagram.com/reel/Dd-VQPnRz0t/",
    "status": "success",
    "errorCode": null,
    "recipe": {
        "title": "Spaghetti Squash Lasagna",
        "servings": 4,
        "times": { "prepMin": 10, "cookMin": 60, "totalMin": 70 },
        "ingredients": [
            {
                "raw": "1 tablespoon olive oil",
                "quantity": 1,
                "quantityMax": null,
                "unit": "tbsp",
                "item": "olive oil",
                "note": null,
                "group": null,
                "optional": false,
                "converted": null,
                "confidence": 0.99,
                "source": "linked_recipe_page"
            },
            {
                "raw": "1 pound lean ground beef",
                "quantity": 1,
                "unit": "lb",
                "item": "lean ground beef",
                "converted": { "quantity": 454, "unit": "g" },
                "confidence": 0.99,
                "source": "linked_recipe_page"
            }
        ],
        "steps": [
            { "n": 1, "text": "Preheat the oven to 400ºF.", "timestampSec": null, "source": "linked_recipe_page" }
        ],
        "tags": ["main course", "italian", "spaghetti squash"],
        "cuisine": "Italian",
        "dietary": ["gluten-free"],
        "nutrition": { "estimated": false, "perServing": true, "calories": 513, "proteinG": 34 },
        "coverImageUrl": "https://..."
    },
    "source": {
        "platform": "instagram",
        "author": "feelgoodfoodie",
        "videoDurationSec": 30,
        "linkedRecipeUrl": "https://feelgoodfoodie.net/recipe/spaghetti-squash-lasagna-boats/"
    },
    "signalsUsed": ["linked_recipe_page"],
    "overallConfidence": 0.99,
    "method": "jsonld",
    "charged": true
}
```

You can download the dataset in various formats such as JSON, HTML, CSV, or Excel.

#### Statuses and error codes

| `status`       | Meaning                                                                                                                                   | Charged |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| `success`      | A recipe with ingredients and steps                                                                                                       | yes     |
| `incomplete`   | A recipe, but the ingredients or steps are not in the content (for example "comment RECIPE for the recipe")                               | no      |
| `not_a_recipe` | Food review, restaurant visit, ad, or unrelated video                                                                                     | no      |
| `failed`       | See `errorCode`: `invalid_url`, `unsupported_url`, `not_found`, `private_or_removed`, `blocked`, `no_content`, `llm_error`, `llm_refused` | no      |

### Data fields

| Field                                                                                  | Description                                                                                                                              |
| -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `recipe.title`, `recipe.description`                                                   | Dish name and short description                                                                                                          |
| `recipe.servings`, `recipe.yield`                                                      | Servings as a number, and the yield text                                                                                                 |
| `recipe.times`                                                                         | `prepMin`, `cookMin`, `totalMin`                                                                                                         |
| `recipe.ingredients[]`                                                                 | `raw`, `quantity`, `quantityMax` (ranges), `unit`, `item`, `note`, `group` (sub-recipe), `optional`, `converted`, `confidence`, `source` |
| `recipe.steps[]`                                                                       | `n`, `text`, `timestampSec` (where it happens in the video), `source`                                                                    |
| `recipe.equipment`, `recipe.tags`, `recipe.cuisine`, `recipe.course`, `recipe.dietary` | Classification for filtering                                                                                                             |
| `recipe.nutrition`                                                                     | Per serving; `estimated: true` when computed from ingredients                                                                            |
| `source`                                                                               | Platform, canonical URL, creator, duration, publish date, linked recipe URL                                                              |
| `signalsUsed`, `overallConfidence`, `method`                                           | Provenance: `jsonld` (exact written recipe) or `llm` (merged by the model)                                                               |

### How much does it cost to extract recipes from videos?

**$30 per 1,000 recipes ($0.03 each), charged only for successful recipes**, plus $0.002 per run. Failed URLs, videos that are not recipes, and incomplete recipes (for example "comment RECIPE for the recipe") are free. Higher Apify plans get volume discounts down to $0.018 per recipe. Apify's free plan credits cover your first recipes.

| Example                                      | Cost      |
| -------------------------------------------- | --------- |
| 1 TikTok recipe                              | $0.032    |
| 100 Instagram Reels, 60 of them full recipes | $1.80     |
| 1,000 YouTube recipe videos                  | about $30 |

### Tips for the best results

- **Keep `followRecipeLinks` on.** When a creator links their blog, you get the exact published recipe.
- **Use `units: metric` or `us`** to feed grocery lists without writing your own conversion.
- **Route on confidence.** Show ingredients below 0.6 confidence, or `source: "inferred"`, to the user for a quick check.
- **Batch your URLs.** One run with 100 URLs pays one start fee.
- **Translate on the way in** with `translateTo` if your app is single-language.

### Run it on a schedule

Save your input as a task, then open **Schedules** in Apify Console and pick how often it runs. Daily runs on a creator's latest videos keep a recipe collection up to date. Each run creates a new dataset, so you can compare runs over time.

To send results somewhere automatically, use the **Integrations** tab: Google Sheets, Slack, Make, Zapier, n8n and webhooks are built in. AI agents such as Claude or ChatGPT can run it as a tool through the [Apify MCP server](https://mcp.apify.com).

### Related actors

| Actor | What it does |
| --- | --- |
| [Video Transcript Scraper](https://apify.com/scrape-lads/video-transcript-scraper) | Transcripts from TikTok, Instagram, YouTube and X videos. |
| [TikTok Scraper Pro](https://apify.com/scrape-lads/tiktok-scraper-pro) | TikTok hashtags, profiles, keyword search, comments and followers. |
| [YouTube Shorts Scraper & Channel Email Finder](https://apify.com/scrape-lads/youtube-scraper) | YouTube Shorts, channel videos and creator contact emails. |
| [Instagram Scraper](https://apify.com/apify/instagram-scraper) | Instagram posts, profiles, hashtags and comments. |
| [Coles Woolworths ALDI Scraper](https://apify.com/scrape-lads/au-grocery-scraper) | Australian supermarket prices and specials. |

### FAQ, disclaimers, and support

**Does it download or store videos?** No. It reads the public caption, the platform's public subtitles, linked pages, and the cover image. It does not download or redistribute video.

**Instagram audio?** Instagram does not publish subtitles, so Reels are read from the caption, linked recipe pages, and the cover image. Reels whose recipe is only spoken come back as `incomplete` and are free.

**Why "incomplete"?** Many creators put the recipe behind "comment RECIPE and I will DM you". The content then does not contain the recipe, so the row is free and says so.

**Is this legal?** It processes publicly available content. Results are structured facts with attribution and a link back to the creator. Respect creators' rights and the platforms' terms when you use the output.

**Why was my result instant?** Popular videos are often already extracted. A stored result for the same video is returned (with your unit and nutrition choices applied) and `fromCache` is `true`. Set `useCache` to `false` to force a fresh extraction.

**Cover image URLs** from TikTok and Instagram are signed and expire after some days. Copy the image if you need to keep it.

Found a recipe that came out wrong, or need another platform? Open an issue on the **Issues** tab. Custom extraction pipelines are available on request.

**Does TikTok, Instagram or YouTube have an official API? Why use this instead?** No platform offers an API that returns a recipe from a video. Their APIs give captions or metadata at best, and only to approved apps. This Actor reads the video's caption, subtitles or speech and turns them into a structured recipe with ingredients, quantities and steps.

**What are the alternatives to this Actor?** You can type recipes out by hand, use a recipe-saving app, or run a transcript through a chatbot yourself. Apps keep the data inside their own interface, and doing it by hand does not scale. This Actor returns clean JSON you can store or publish, at a fixed price per recipe.

### Privacy

To see which searches are useful, this Actor sends anonymous run inputs (such as keywords, hashtags and usernames) and a hashed account ID to the developer. Tokens, cookies and proxy settings are never sent. Nothing from your results is collected.

# Actor input Schema

## `urls` (type: `array`):

TikTok videos (including vm.tiktok.com short links), Instagram Reels and posts, YouTube videos and Shorts, or any recipe web page. One URL per line.

## `units` (type: `string`):

Adds a converted amount to each ingredient. Original keeps only the creator's units. Metric converts cups, ounces and pounds to ml and g (spoons stay as spoons). US converts grams and milliliters to ounces, cups and spoons.

## `translateTo` (type: `string`):

Optional language for the recipe text, for example English or Spanish. Leave empty to keep the creator's language. Ingredient raw text always stays in the original language.

## `includeNutrition` (type: `boolean`):

Per-serving calories, protein, carbs, fat, fiber, sugar and sodium. Taken from the recipe page when published there, otherwise estimated from the ingredients and marked as estimated.

## `followRecipeLinks` (type: `boolean`):

When a caption or description links to the written recipe (for example a food blog), read the exact recipe from that page. Most accurate option; turn off to use only the video itself.

## `useCover` (type: `boolean`):

Send the video's cover frame to the model as an extra signal (dish name, visible ingredients, on-screen text).

## `useCache` (type: `boolean`):

Return a stored result when this video or page was already extracted (with the same translation language) in the last 60 days, in any unit system. Faster, and identical to a fresh extraction. Results are still charged as recipes. Turn off to force a fresh extraction.

## `maxConcurrency` (type: `integer`):

How many URLs are processed at the same time.

## Actor input object example

```json
{
  "urls": [
    "https://www.tiktok.com/@recipes/video/7447547249720053000",
    "https://www.instagram.com/reel/Dd-VQPnRz0t/",
    "https://www.youtube.com/watch?v=i84Sc5uvQa8"
  ],
  "units": "original",
  "translateTo": "",
  "includeNutrition": false,
  "followRecipeLinks": true,
  "useCover": true,
  "useCache": true,
  "maxConcurrency": 5
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `runSummary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "urls": [
        "https://www.tiktok.com/@recipes/video/7447547249720053000",
        "https://www.instagram.com/reel/Dd-VQPnRz0t/",
        "https://www.youtube.com/watch?v=i84Sc5uvQa8"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrape-lads/recipe-extractor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "urls": [
        "https://www.tiktok.com/@recipes/video/7447547249720053000",
        "https://www.instagram.com/reel/Dd-VQPnRz0t/",
        "https://www.youtube.com/watch?v=i84Sc5uvQa8",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("scrape-lads/recipe-extractor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "urls": [
    "https://www.tiktok.com/@recipes/video/7447547249720053000",
    "https://www.instagram.com/reel/Dd-VQPnRz0t/",
    "https://www.youtube.com/watch?v=i84Sc5uvQa8"
  ]
}' |
apify call scrape-lads/recipe-extractor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrape-lads/recipe-extractor"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/4nbLmK8bh6Jb1MD0x/builds/YbMDI54RF5nwlfHX4/openapi.json
