Contents
On this page
Create Recipe - Plugixa Recipe
POST /plugixa-recipe/v1/recipes
Create a recipe with its ingredients, instructions, taxonomy links and nutrition in one request.
Needs the plugixa_recipe_edit capability, or WordPress’s edit_posts (Contributor and up). When the request moves the recipe into or out of published, it also needs plugixa_recipe_publish or WordPress’s publish_posts (Author and up). Moving between draft, pending and archived does not.
Note. Every required field below is required whatever the
status, a draft included: in 1.4.0 the plugin applies the full list to every status.
Request
POST https://your-site.example/wp-json/plugixa-recipe/v1/recipes
Request body
Content type: application/json
| Field | Type | Required | Description |
|---|---|---|---|
title |
string |
Yes | Plain text. Must not be blank. |
description |
string |
Yes | HTML; the tags allowed in post content are kept. Must contain some text. |
image_id |
integer |
Yes | Attachment id of the main image. |
category_ids |
integer[] |
Yes | At least one category id. Links the recipe to these categories, in this order. |
difficulty |
string |
Yes | easy, medium, hard or expert. |
servings |
integer |
Yes | At least 1. |
prep_time |
integer |
Yes | Minutes. 0 is accepted; empty is not. |
cook_time |
integer |
Yes | Minutes. 0 is accepted; empty is not. |
ingredients |
object[] |
Yes | At least one with a name. Fields as in Replace Ingredients. |
instructions |
object[]|string[] |
Yes | At least one step with text. Fields as in Replace Instructions; a bare string is a step. |
status |
string |
No | draft (the default), pending, published or archived, lower case exactly. |
slug |
string |
No | Made from the title when empty. Made unique by appending -2, -3, … |
excerpt, notes, tips |
string |
No | HTML, as description. |
rest_time |
integer |
No | Minutes. |
servings_unit |
string |
No | Defaults to servings. |
yield_amount, yield_unit |
string |
No | Free text, for example 12 and pancakes. |
cost |
string |
No | budget, moderate (the default), expensive or premium. The older low, medium and high are read as budget, moderate and expensive. |
season |
string |
No | spring, summer, autumn, winter, all, or empty. |
cuisine_ids, course_ids |
integer[] |
No | Links the recipe to these cuisines and courses. |
gallery |
integer[] |
No | Attachment ids of extra images. |
tags |
string[] |
No | Blanks and repeats are dropped. |
equipment |
string[] |
No | Equipment names. Or send equipment_items, a list of {name, link_url}, which wins when both are sent. |
video_url, source_url |
string |
No | URLs. |
source, author_name |
string |
No | Credit text. |
is_featured, is_editor_pick |
integer |
No | 1 or 0. |
display_order |
integer |
No | Manual position. |
is_vegetarian, is_vegan, is_gluten_free, is_dairy_free, is_keto, is_low_carb, is_nut_free, is_paleo, is_sugar_free, is_halal, is_kosher |
integer |
No | Diet flags, 1 or 0. |
nutrition |
object |
No | Stores a nutrition row. Fields as in Update Nutrition. |
seo_title, seo_description, seo_keywords |
string |
No | Plain text. |
enable_schema |
boolean |
No | Whether the recipe page carries structured data. |
language_code, translation_group |
string |
No | For multilingual sites. |
custom_fields |
object |
No | PRO Field key to value. Replaces every stored value. When sent, fields marked required must be filled. |
page_layout |
string |
No | PRO A layout name, or empty for the site default. |
An ingredient’s quantity is also stored as numbers (amount, amount_max) when it reads as one: 2, 1 1/2, ½ or a range such as 2-3. A recipe created as published gets published_at set to now. total_time is worked out for you.
{
"title": "Lemon Pancakes",
"description": "<p>Light, fluffy pancakes with lemon zest.</p>",
"image_id": 345,
"category_ids": [
3
],
"course_ids": [
8
],
"difficulty": "easy",
"cost": "budget",
"servings": 4,
"prep_time": 10,
"cook_time": 15,
"status": "published",
"is_vegetarian": 1,
"tags": [
"breakfast",
"brunch"
],
"equipment": [
"Frying pan"
],
"ingredients": [
{
"quantity": "2",
"unit": "cups",
"name": "flour"
},
{
"quantity": "1 1/2",
"unit": "cups",
"name": "milk",
"substitution": "oat milk",
"sort_order": 1
}
],
"instructions": [
"Whisk the flour, milk and lemon zest.",
{
"instruction_text": "Cook each pancake for 2 minutes a side.",
"timer_seconds": 240,
"sort_order": 1
}
]
}
Responses
200 OK. data is the new recipe, read back in full.
{
"success": true,
"message": "",
"data": {
"id": "12",
"language_code": "",
"translation_group": "",
"title": "Lemon Pancakes",
"slug": "lemon-pancakes",
"description": "<p>Light, fluffy pancakes with lemon zest.</p>",
"excerpt": "",
"notes": "",
"tips": "",
"image_id": "345",
"gallery": null,
"tags": [
"breakfast",
"brunch"
],
"equipment": [
"Frying pan"
],
"video_url": "",
"servings": "4",
"servings_unit": "servings",
"yield_amount": "",
"yield_unit": "",
"prep_time": "10",
"cook_time": "15",
"rest_time": null,
"total_time": "25",
"difficulty": "easy",
"cost": "budget",
"season": "",
"status": "published",
"is_featured": "0",
"is_editor_pick": "0",
"display_order": "0",
"source": "",
"source_url": "",
"author_name": "",
"view_count": "0",
"rating_average": "0.00",
"rating_count": "0",
"like_count": "0",
"enable_schema": "1",
"canonical_post_id": null,
"seo_score": null,
"seo_title": null,
"seo_description": null,
"seo_keywords": null,
"focus_keyword": null,
"is_vegetarian": "1",
"is_vegan": "0",
"is_gluten_free": "0",
"is_dairy_free": "0",
"is_keto": "0",
"is_low_carb": "0",
"is_nut_free": "0",
"is_paleo": "0",
"is_sugar_free": "0",
"is_halal": "0",
"is_kosher": "0",
"published_at": "2026-11-12 09:30:00",
"created_at": "2026-11-12 09:30:00",
"created_by": "1",
"submission_ip": null,
"modified_at": null,
"modified_by": null,
"image_url": "https://your-site.example/wp-content/uploads/2026/11/lemon-pancakes-1024x683.jpg",
"image_thumbnail": "https://your-site.example/wp-content/uploads/2026/11/lemon-pancakes-150x150.jpg",
"equipment_items": [
{
"name": "Frying pan",
"link_url": ""
}
],
"categories": [
{
"id": "3",
"language_code": "",
"translation_group": "",
"name": "Breakfast",
"slug": "breakfast",
"description": "",
"image_id": null,
"display_order": "0",
"created_at": "2026-11-01 08:00:00"
}
],
"cuisines": [],
"courses": [
{
"id": "8",
"name": "Breakfast",
"slug": "breakfast",
"description": null,
"display_order": "7"
}
],
"category_id": 3,
"category_ids": [
3
],
"cuisine_id": 0,
"course_id": 8,
"ingredients": [
{
"id": "101",
"recipe_id": "12",
"group_name": "",
"group_order": "0",
"sort_order": "0",
"quantity": "2",
"unit": "cups",
"name": "flour",
"notes": "",
"link_url": "",
"amount": "2.000",
"amount_max": null,
"is_optional": "0",
"substitution": ""
},
{
"id": "102",
"recipe_id": "12",
"group_name": "",
"group_order": "0",
"sort_order": "1",
"quantity": "1 1/2",
"unit": "cups",
"name": "milk",
"notes": "",
"link_url": "",
"amount": "1.500",
"amount_max": null,
"is_optional": "0",
"substitution": "oat milk"
}
],
"instructions": [
{
"id": "201",
"recipe_id": "12",
"group_name": "",
"group_order": "0",
"sort_order": "0",
"title": "",
"instruction_text": "Whisk the flour, milk and lemon zest.",
"image_id": null,
"video_url": "",
"duration": null,
"timer_seconds": null,
"tip": "",
"text": "Whisk the flour, milk and lemon zest."
},
{
"id": "202",
"recipe_id": "12",
"group_name": "",
"group_order": "0",
"sort_order": "1",
"title": "",
"instruction_text": "Cook each pancake for 2 minutes a side.",
"image_id": null,
"video_url": "",
"duration": "4",
"timer_seconds": "240",
"tip": "",
"text": "Cook each pancake for 2 minutes a side."
}
],
"nutrition": null
}
}
| Field | Type | Meaning |
|---|---|---|
data.id |
string |
The recipe’s id, as a numeric string |
data.status |
string |
draft, pending, published or archived |
data.slug |
string |
Unique among recipes |
data.prep_time, data.cook_time, data.rest_time |
string|null |
Minutes |
data.total_time |
string |
Minutes: prep + cook + rest, worked out by the database |
data.difficulty |
string |
easy, medium, hard or expert |
data.cost |
string |
budget, moderate, expensive or premium |
data.tags |
string[] |
Tags, decoded |
data.equipment |
string[] |
Equipment names |
data.equipment_items |
object[] |
The same equipment with each item’s optional link_url |
data.gallery |
string|null |
The stored gallery, as JSON text. gallery_ids and gallery_urls are added when it is set |
data.image_url, data.image_thumbnail |
string |
Only present when the recipe has an image |
data.categories, data.cuisines, data.courses |
object[] |
The linked terms, every column |
data.category_ids |
integer[] |
Ids of the linked categories |
data.category_id, data.cuisine_id, data.course_id |
integer |
The first linked term of each kind, 0 when none |
data.ingredients |
object[] |
In group order, then position. See List Ingredients |
data.instructions |
object[] |
In group order, then position, each with a text copy of instruction_text |
data.nutrition |
object|null |
The nutrition row, or null |
data.published_at |
string|null |
Site-time datetime the recipe first went live |
data.custom_fields |
object |
Pro, with Custom Fields on: field key to value |
data.page_layout |
string |
Pro, with Recipe Layouts on: the recipe’s own layout, empty for the site default |
The status is 200, not 201.
Errors
| Status | Code | When |
|---|---|---|
400 |
rest_error |
A required field is missing or empty. Checked in this order, and the first failure is reported: title, description, image, category, difficulty, servings, prep time, cook time, ingredient, instruction. data.errors names the field. |
400 |
rest_error |
difficulty, cost, season or status is not one of the allowed values. |
400 |
rest_error |
PRO A required custom field is empty, or page_layout names no layout. |
403 |
rest_forbidden |
The request would publish the recipe or take it down, and you lack the publish capability. |
500 |
rest_error |
The recipe row could not be written. |
Plus the errors every endpoint can return.
Examples
curl -s -X POST "https://your-site.example/wp-json/plugixa-recipe/v1/recipes" \
-u "admin:$WP_APP_PASSWORD" \
-H "Content-Type: application/json" \
-d '{"title":"Lemon Pancakes","description":"<p>Light, fluffy pancakes with lemon zest.</p>","image_id":345,"category_ids":[3],"difficulty":"easy","servings":4,"prep_time":10,"cook_time":15,"ingredients":[{"quantity":"2","unit":"cups","name":"flour"}],"instructions":["Whisk the flour, milk and lemon zest."]}'
<?php
$ch = curl_init('https://your-site.example/wp-json/plugixa-recipe/v1/recipes');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_USERPWD => 'admin:' . getenv('WP_APP_PASSWORD'),
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode([
'title' => 'Lemon Pancakes',
'description' => '<p>Light, fluffy pancakes with lemon zest.</p>',
'image_id' => 345,
'category_ids' => [3],
'difficulty' => 'easy',
'servings' => 4,
'prep_time' => 10,
'cook_time' => 15,
'ingredients' => [
[
'quantity' => '2',
'unit' => 'cups',
'name' => 'flour',
],
],
'instructions' => ['Whisk the flour, milk and lemon zest.'],
]),
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
const auth = 'Basic ' + btoa(`admin:${process.env.WP_APP_PASSWORD}`);
const response = await fetch('https://your-site.example/wp-json/plugixa-recipe/v1/recipes', {
method: 'POST',
headers: {
'Authorization': auth,
'Content-Type': 'application/json',
},
body: JSON.stringify({"title": "Lemon Pancakes", "description": "<p>Light, fluffy pancakes with lemon zest.</p>", "image_id": 345, "category_ids": [3], "difficulty": "easy", "servings": 4, "prep_time": 10, "cook_time": 15, "ingredients": [{"quantity": "2", "unit": "cups", "name": "flour"}], "instructions": ["Whisk the flour, milk and lemon zest."]}),
});
const { data } = await response.json();
Quick Links
Share