Bulk Recipe Import / Export

Table of Contents

  • Getting Started
  • Downloading the Template
  • CSV Column Reference
  • Formatting Rules
  • Importing Recipes
  • Exporting Recipes
  • Troubleshooting

Getting Started

The CSV import/export system is found on the Tools page of your Recipe Kit app. From there you can:

  • Download a template CSV with example data
  • Export your existing recipes to CSV
  • Import recipes from a CSV file

The fastest way to get started is to download the template, fill it in with your recipes, and import it.


Downloading the Template

Click Download Template on the Tools page. This gives you a CSV file with:

  • A header row containing all column names
  • One example row showing the correct format for each field

Open the template in a spreadsheet app (Google Sheets, Excel, etc.), use the example as a reference, then delete it and add your own recipes.

The example row's blog_id and article_id are placeholders that point at nothing. Delete the row rather than importing it, otherwise the import will tell you Shopify rejected the blog.


CSV Column Reference

Each row in the CSV represents one recipe. The columns are:

Column Required Description
recipe_id No Leave empty for new recipes. Fill with an existing recipe ID to update that recipe.
recipe_title Yes The name of the recipe.
recipe_author No Author name.
recipe_description No Full description text.
recipe_category No Category (e.g., "Dessert", "Main Course").
recipe_cuisine No Cuisine type (e.g., "Italian", "American").
recipe_calories No Calorie count. Whole number between -32,768 and 32,767.
serving_size No Serving size text (e.g., "4 servings", "24 cookies").
prep_time No Preparation time
cook_time No Cooking time
recipe_image No URL to the recipe's main image
recipe_image_alt_text No Alt text for the main image.
recipe_video No URL to a recipe video.
recipe_note No Additional notes about the recipe.
enable_rating No 1 to enable star ratings, 0 to disable.
blog_id Conditional The Shopify blog to connect to. Exports write this as gid://shopify/Blog/106700000000. A plain numeric ID works too. Required when "Create blog posts" is enabled.
article_id No An existing Shopify article to link to. Exports write this as gid://shopify/Article/1234567890123. A plain numeric ID works too.
recipe_tags No Tags separated by semicolons
recipe_ingredients Yes Ingredients separated by semicolons
recipe_equipment No Equipment separated by semicolons
recipe_directions Yes Directions separated by semicolons
direction_images No Image URLs for direction steps
nutrition_data No Nutrition info as Title:Value pairs separated by semicolons
nutrition_template No Nutrition label layout. One of linear, horizontal or vertical. Defaults to linear.
nutrition_serving_size No Nutrition serving size text (e.g., "Per cookie").
custom_field_1_key No Label for first custom field.
custom_field_1_value No Value for first custom field.
custom_field_2_key No Label for second custom field.
custom_field_2_value No Value for second custom field.
update_blog_post_tags No 1 to sync tags to the linked blog post, 0 to skip.
update_blog_post_image No 1 to sync the recipe image to the linked blog post, 0 to skip.
status Yes 1 for published, 0 for hidden/draft. A blank cell is rejected, so fill this in on every row.

Formatting Rules

Ingredients

Ingredients are separated by semicolons (;).

2 cups flour;1 cup sugar;2 eggs;1 cup butter

Each entry between semicolons becomes one ingredient line in your recipe.

Directions

Directions are separated by semicolons (;). Each entry becomes one numbered step.

Preheat oven to 350F;Cream butter and sugar;Add eggs one at a time;Mix in flour;Bake for 12 minutes

Equipment

Equipment items are separated by semicolons (;).

Large mixing bowl;Whisk;Baking sheet;Parchment paper

Images

Recipe main image (recipe_image column):

Provide a full URL to your image:

https://example.com/images/chocolate-cake.jpg
  • Images already hosted on Shopify's CDN are used directly without re-uploading.
  • Images from external URLs are downloaded and uploaded to your Shopify store automatically.
  • When updating an existing recipe, if the image URL hasn't changed, the upload is skipped.
  • If the image fails to upload, the recipe is still imported (just without the image).

Direction step images (direction_images column):

Provide image URLs separated by semicolons, matched by position to your direction steps in the recipe_directions column. Use an empty entry (no text between semicolons) for steps without images.

https://example.com/step1.jpg;;https://example.com/step3.jpg;

In this example:

  • Step 1 gets step1.jpg
  • Step 2 has no image (empty between semicolons)
  • Step 3 gets step3.jpg
  • Step 4 has no image

The positions in direction_images must match the positions in recipe_directions exactly, including headings. For example, if your directions are:

HEAD:Prep;Preheat oven to 350F;Grease pan;HEAD:Bake;Pour batter;Bake 30 min

Then your direction images should have 6 positions (one per entry, including headings):

;;https://example.com/preheat.jpg;;;https://example.com/baking.jpg

Headings always have empty image positions. When you export recipes that have direction images, they are automatically exported in this format.

Direction images are handled the same way as the main recipe image:

  • Images already on Shopify's CDN or Recipe Kit hosting are used directly without re-uploading.
  • External image URLs are uploaded to your Shopify store automatically.
  • When updating an existing recipe, images that match the current step are skipped.
  • If an upload fails, the original URL is kept.

Product Links

You can link ingredients to Shopify products or external URLs using the double pipe (||) delimiter.

Shopify product linking (recommended):

Use the shopify: prefix followed by the product handle to link an ingredient directly to a product in your store. The product handle is the URL-friendly name from your product's URL (e.g., if your product URL is yourstore.com/products/organic-flour, the handle is organic-flour).

Format: ingredient text||shopify:product-handle

2 cups flour||shopify:organic-flour;1 cup sugar;2 eggs||shopify:free-range-eggs

On import, Recipe Kit automatically looks up the product in your Shopify store and attaches the full product data (title, variants, images). This is the same as dragging a product onto an ingredient in the recipe editor. The ingredient becomes a clickable link to the product page and can show the "Add to Cart" button if enabled.

When you export recipes that have Shopify products linked to ingredients, they are automatically exported in this shopify:handle format so they can be re-imported without losing the product connections.

If a product handle is not found in your store during import, the ingredient is still imported but without the product link. A warning is logged.

Plain URL linking:

You can also link to any external URL:

Format: ingredient text||url

2 cups flour||https://yourstore.com/products/organic-flour;1 cup sugar;2 eggs

This makes the ingredient text a clickable link to that URL.

Rules:

  • Only one || per ingredient entry is allowed.
  • The URL or shopify:handle goes after the || (spaces are trimmed).
  • shopify:handle is preferred over plain URLs for products in your own store because it preserves the full product data (variants, images, Add to Cart support).

Section Headings

You can organize ingredients, directions, and equipment into sections using the HEAD: prefix.

Ingredients with headings:

HEAD:Dry Ingredients;2 cups flour;1 cup sugar;1 tsp baking powder;HEAD:Wet Ingredients;2 eggs;1 cup milk;1/2 cup butter

This produces:

Dry Ingredients

  • 2 cups flour
  • 1 cup sugar
  • 1 tsp baking powder

Wet Ingredients

  • 2 eggs
  • 1 cup milk
  • 1/2 cup butter

Directions with headings:

HEAD:Preparation;Preheat oven to 350F;Grease a 9x13 pan;HEAD:Mix Batter;Cream butter and sugar;Add eggs;HEAD:Bake;Pour into pan;Bake for 30 minutes

Equipment with headings:

HEAD:Mixing;Large mixing bowl;Whisk;HEAD:Baking;Baking sheet;Parchment paper

The HEAD: prefix works the same way in all three fields.

Tags

Tags are separated by semicolons (;). Commas work as a separator too.

dessert;cookies;chocolate;baking;holiday

Use plain text only. HTML such as anchor or paragraph tags is stored as literal text and will show up inside the tag.

If the recipe is connected to a blog post and update_blog_post_tags is set to 1, these tags are also written to the Shopify article's tags.

Nutrition Data

Nutrition data uses colon-separated Title:Value pairs, with entries separated by semicolons.

Format: Title:Value;Title:Value

Calories:250;Carbs:30;Fat:12;Protein:3;Sugar:18;Sodium:200

Every entry must contain a colon. An entry written without one (for example Calories 250) is reported as invalid in the preview, and the row is skipped.

Enter numbers only. The unit is implied by the title, so write Fat:12, not Fat:12g.

A title you leave blank on the value side (Fiber:) is allowed and imports as an empty value. A title that is not in the list below still imports and displays, it just does not get a unit or structured data markup.

Recognized nutrition titles:

Title Unit
Calories calories
Carbs grams
Fat grams
Protein grams
Sugar grams
Fiber grams
Sodium milligrams
Cholesterol milligrams
Saturated Fat grams
Trans Fat grams
Unsaturated Fat grams

Nutrition label layout (nutrition_template column):

Value Layout
linear Minimal text
horizontal Horizontal
vertical Vertical (US standard label)

Any other value falls back to linear. Use nutrition_serving_size for the text describing what the figures apply to (e.g., "Per cookie").

Custom Fields

You can add up to two custom fields per recipe. Both the key and value must be provided together — you cannot have a key without a value or vice versa.

Column Example
custom_field_1_key Difficulty
custom_field_1_value Easy
custom_field_2_key Best Time
custom_field_2_value Afternoon

Time Fields

The prep_time and cook_time columns accept flexible time formats:

Input Interpretation
15 minutes 15 minutes
1 hour 12 minutes 1 hour and 12 minutes
2 hours 2 hours
15 15 minutes (bare numbers are treated as minutes)

Blog Post Connections

To connect recipes to Shopify blog posts:

  • Link to an existing article: Provide both blog_id and article_id.
  • Create a new article: Provide blog_id only (leave article_id empty). You must also enable "Create blog posts" during import.
  • No blog connection: Leave both columns empty.

Use update_blog_post_tags (1 or 0) and update_blog_post_image (1 or 0) to control whether recipe tags and the recipe image are synced to the connected blog post.

A note on ID formats. Recipe Kit exports blog and article IDs in the gid://shopify/Blog/106700000000 form. That is deliberate. A bare 11 to 13 digit ID is just a number as far as Excel is concerned, so it gets rewritten as 1.067E+11 when you save the file and the real ID is gone. The gid form contains letters, so spreadsheets leave it alone. If you are typing IDs in yourself, use the gid form, or format the blog_id and article_id columns as Text before you paste.


Importing Recipes

  1. Go to the Tools page in Recipe Kit.
  2. Drag and drop your CSV file into the upload area (or click to browse). Files are limited to 50 MB and 10,000 rows. Split a larger collection into several files and import them one after another.
  3. A preview modal appears showing:
    • Matches: Recipes that match existing recipes in your store (by ID or title).
    • New Recipes: Recipes that will be created.
    • Invalid: Rows with errors that will be skipped.
  4. Review the summary. If there are invalid entries, expand the section to see the specific errors and row numbers.
  5. Configure import options (see below).
  6. Click Confirm Import to start.

After confirming, you'll see a progress bar with real-time updates. Processing happens in the background — you can navigate away and return to check progress later.

Import Options

During the preview step, you can toggle:

  • Update existing recipes (default: ON) — Matched recipes are updated with CSV data. When OFF, matched recipes are created as new recipes instead.
  • Create blog posts (default: OFF) — When enabled, recipes with a blog_id but no article_id will have a new Shopify blog article created automatically. Enabling this makes blog_id a required field.

Updating Existing Recipes

There are two ways recipes are matched to existing records:

  1. By recipe_id: If you provide a recipe ID in the recipe_id column, it will match that exact recipe.
  2. By title: If no recipe_id is provided, the system looks for an existing recipe with the same title.

To update recipes, export your current recipes first, modify the CSV, and re-import. The recipe_id column ensures accurate matching.

Cancelling an Import

While an import is in progress, click Stop Import to cancel. Recipes already processed will remain, but no further recipes will be imported.


Exporting Recipes

  1. Go to the Tools page.
  2. Under the export section, select a filter:
    • All recipes
    • Published only (status = 1)
    • Hidden only (status = 0)
  3. Click Export. A CSV file will download to your computer.

The exported CSV uses the exact same format as the import, so you can modify it and re-import to make bulk changes.

Blog and article IDs export in the gid://shopify/Blog/106700000000 form so they survive being opened and saved in Excel. Imports still accept plain numeric IDs, so older exports keep working.


Troubleshooting

"Missing recipe_title. A recipe title is required."

Every row must have a value in the recipe_title column. Check for empty rows or rows where the title column was accidentally left blank.

"Invalid status. Must be 0 (hidden) or 1 (published)."

The status column only accepts 0 (hidden) or 1 (published). A blank cell is treated as missing, so fill it in on every row.

"Missing blog_id (required when 'Create blog posts' is enabled)."

With "Create blog posts" turned on, every row needs a blog_id. Fill it in on every row, or turn the option off. A blank cell counts as missing.

"Invalid blog_id. Must be the gid:// form the CSV export writes."

The cell holds something that is not an ID. Use the gid form from an export (gid://shopify/Blog/106700000000), or the plain number from your blog's URL in Shopify admin (/admin/blogs/106700000000). The same applies to article_id.

"Invalid blog_id. '1.067E+11' is what a spreadsheet writes when it converts a long ID to a number."

Excel reads a long ID as a number and writes the shortened version back when you save, which throws the digits away. The original ID cannot be recovered from that value, so the row is rejected before the recipe is created. Two ways to fix it:

  • Download a fresh export. Blog and article IDs come out in the gid form, which spreadsheets do not touch.
  • Or format the blog_id and article_id columns as Text, then enter the IDs again.

Recipes imported before this check existed were saved without their blog post, and those rows warned with "Saved, but no blog post was created". Re-import them with a correct ID and "Update existing recipes" on.

"Invalid nutrition_data entry"

An entry in the nutrition_data column is missing its colon, or has nothing on the title side. Each entry must read Title:Value, and entries are separated by semicolons:

Calories:250;Fat:12;Protein:3

The error message quotes the entry it could not read, so search your CSV for that text.

"Only one || delimiter allowed per ingredient."

An ingredient entry contains more than one ||. Each ingredient can only have one URL link. Check for accidental double pipes in your ingredient text.

"Custom field 1: Both key and value must be provided together."

Custom fields require both a key and a value. If you provide custom_field_1_key, you must also provide custom_field_1_value (and vice versa). Leave both blank if you are not using the field.

Calories not importing, or a row failing on calories

The recipe_calories field only accepts whole numbers in the range -32,768 to 32,767. Text, commas and decimal points are ignored and the recipe imports with no calorie count. A number outside that range fails the row.

"Missing recipe_ingredients. At least one ingredient is required."

The recipe_ingredients column cannot be empty. Provide at least one ingredient.

"Missing recipe_directions. At least one direction is required."

The recipe_directions column cannot be empty. Provide at least one direction step.

Shopify product links not working after import

  • Make sure you're using the correct product handle. The handle is the URL-friendly slug from your product page URL (e.g., organic-flour from yourstore.com/products/organic-flour).
  • The product must exist in your Shopify store. If the handle doesn't match any product, the ingredient is imported without a product link.
  • Product lookups require API access to your store. If your store has API rate limits or connectivity issues, product linking may fail silently.
  • Check that the format is exactly shopify:handle after the || (e.g., 2 cups flour||shopify:organic-flour). Do not include the full URL.

Direction images not appearing after import

  • Ensure the image positions in direction_images align exactly with the positions in recipe_directions, including headings (which should have empty positions).
  • Image URLs must be publicly accessible.
  • The number of semicolons in direction_images should match the number of semicolons in recipe_directions.

Images not appearing after import

  • Verify the image URLs are publicly accessible (not behind a login or paywall).
  • Image uploads can fail silently — the recipe is still imported, just without the image. Re-upload images manually or re-import after fixing the URLs.
  • Images already on Shopify's CDN (cdn.shopify.com) are used directly without re-uploading.

Import seems stuck or slow

  • Recipes are processed with a small delay between each one to avoid overloading Shopify's API (roughly 150ms between recipes, 500ms between batches of 25).
  • Image uploads add additional time per recipe.
  • Large imports (thousands of recipes) can take a while. The progress bar updates in real-time.
  • If you navigate away, your import continues in the background. Return to the Tools page to see progress.

Semicolons in my ingredient/direction text

Semicolons (;) are the delimiter, so a literal one has to be escaped with a backslash:

Reduce by half\; stirring often;Season to taste

The escaped ; stays inside the item instead of starting a new one. If you would rather not escape anything, rephrase with a comma.

Semicolons that close an HTML entity (&, ', ’) are recognized automatically and never split an item, so exported recipes containing apostrophes and ampersands re-import unchanged.

The same rules apply to the equipment, directions and direction_images columns. The nutrition_data column does not support escaping, so avoid semicolons inside nutrition values.

Special characters or encoding issues

  • Save your CSV file with UTF-8 encoding.
  • If using Excel, choose "CSV UTF-8 (Comma delimited)" when saving.
  • Values containing commas, double quotes, or newlines should be wrapped in double quotes (most spreadsheet apps do this automatically).

Rows marked as "Invalid" in preview

Expand the invalid entries section in the preview modal to see the specific error for each row, including the row number. Fix the issues in your CSV and re-upload.

Exported CSV looks wrong when opened in Excel

  • Ensure you're opening it as a CSV (not pasting the content).
  • In Excel, use File > Open and select the CSV, or use the Data Import wizard to specify comma as the delimiter and UTF-8 as the encoding.

Import was cancelled but some recipes were already created

Cancelling an import stops future processing but does not roll back recipes that were already created or updated. You can delete unwanted recipes manually from the Recipes page.

What's Next?

  • Creating Recipes - Manual recipe creation
  • Recipe Status and Visibility - Publishing options
  • Linking Blog Posts to Recipes - Blog integration
Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.

Still need help? Contact Us Contact Us