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 Settings page of your RecipeKit 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 Settings 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.
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 (number). |
| 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 | Shopify blog ID. Required when "Create blog posts" is enabled. |
| article_id | No | Existing Shopify article ID to link to. |
| 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 key:value pairs |
| nutrition_template | No | Nutrition display template name (e.g., "Standard"). |
| 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. |
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 RecipeKit 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, RecipeKit 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 (;).
dessert;cookies;chocolate;baking;holiday
Nutrition Data
Nutrition data uses colon-separated key:value pairs, with entries separated by semicolons.
Format: Title:Value;Title:Value
Calories:250;Carbs:30;Fat:12;Protein:3;Sugar:18;Sodium:200
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 |
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.
Importing Recipes
- Go to the Settings page in RecipeKit.
- Drag and drop your CSV file into the upload area (or click to browse).
- 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.
- Review the summary. If there are invalid entries, expand the section to see the specific errors and row numbers.
- Configure import options (see below).
- 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:
- By recipe_id: If you provide a recipe ID in the recipe_id column, it will match that exact recipe.
- 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
- Go to the Settings page.
- Under the export section, select a filter:
- All recipes
- Published only (status = 1)
- Hidden only (status = 0)
- 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.
Troubleshooting
"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.
"status must be 0 or 1"
The status column only accepts 0 (hidden) or 1 (published). Any other value will cause the row to be marked invalid.
"blog_id is required when blog post creation is enabled"
If you enabled "Create blog posts" during import, every row must have a valid blog_id. Either provide blog IDs for all rows or disable the "Create blog posts" option.
"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 key provided without value" / "Custom field value provided without key"
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).
"recipe_calories must be a valid number"
The recipe_calories field only accepts whole numbers in the range -32,768 to 32,767. Remove any text, commas, or decimal points.
"recipe_ingredients must have at least one ingredient"
The recipe_ingredients column cannot be empty. Provide at least one ingredient.
"recipe_directions must have at least one direction"
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 Settings page to see progress.
Semicolons in my ingredient/direction text
Since semicolons (;) are used as the delimiter, you cannot use literal semicolons within ingredient or direction text. Rephrase the text to avoid semicolons, or use a comma instead.
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