Search a Recipe

A method to search recipes.

POST https://api.whisk.com/recipe/v2/search

Request Body

NameTypeDescription

Search Request

object

{
  "data": [
    {
      "content": {
        "id": "97f77cceca5d11e7ae7e42010a9a0035",
        "name": "Quick coronation chicken sandwich",
        "description": "Use leftover roast chicken to make a delicious coronation chicken sandwich ready to pack in your lunchbox.",
        "images": [
          {
            "url": "http://cdnwp.audiencemedia.com/wp-content/uploads/2015/01/478091-1-eng-GB_coronation-chick-sandwich-470x540.jpg",
            "responsive": {
              "url": "https://lh3.googleusercontent.com/crjY_vyxK8kdBYYBd6VVtlGwIXuG3pn9DuCSWP4-_VtURbrYfpKPrYDMmrlCwc8kqSAsgCBtjhqU2C7PEjU0wMDh4FSK",
              "width": 470,
              "height": 540
            }
          },
          ...
        ],
        "source": {
          "name": "deliciousmagazine.co.uk",
          "displayName": "delicious. magazine",
          "sourceRecipeUrl": "http://www.deliciousmagazine.co.uk/recipes/quick-coronation-chicken-sandwich/",
          "license": "Fairuse",
          "image": {
            "url": "https://res.cloudinary.com/whisk/image/upload/v1401879186/content/publisher_logos/delicious-magazine-logo.png",
            "responsive": {
              "url": "https://res.cloudinary.com/whisk/image/upload/v1401879186/content/publisher_logos/delicious-magazine-logo.png",
              "width": 200,
              "height": 200
            }
          }
        },
        "author": {
          "name": "Author name",
          "image": {
            "url": "https://whisk-res.cloudinary.com/image/upload/v1523894700/custom_upload/ba4d7363cd46c736675d2cc08754f5bc.png",
            "responsive": {
              "url": "https://whisk-res.cloudinary.com/image/upload/v1523894700/custom_upload/ba4d7363cd46c736675d2cc08754f5bc.png",
              "width": 800,
              "height": 800
            }
          }
        },
        "numberOfServings": 1,
        "labels": {
          "mealType": [],
          "cuisine": [],
          "category": [
            {
              "name": "quick-and-easy",
              "displayName": "Quick and easy"
            }
          ]
        }
      },
      "matchedIngredients": [
        {
          "name": "meat"
        },
        {
          "name": "bread"
        }
      ]
    },
    {
      "content": {
        ...
      }
    }
  ],
  "paging": {
    "cursors": {
      "after": "eyJpZCI6ImNhZjVlOWY3Y2YxNzFkYjBmZTdkYjJmOTM4M2M0ZDIzIiwiaW5kZXgiOjF9"
    },
    "total": 1300
  }

Logic for A/B was temporarily disabled. It could be enabled again by request

Internal logic on whisk back-end was updated for this endpoint. Implicit profile functionality and A/B test were implemented.

For ST Cooking user_id for which embeddings were uploaded, search results will be generated by elastic Search according to corresponded embedding.

A/B test was added on the BE. Users splitted by murmurHash2 (64) into 2 groups:

  1. Provide results with implicit profile functionality

  2. Provide results without implicit profile functionality

So for first group embeddings should be added for only first group while call method = 'whisk.api.recipe.v2.RecipeAPI/SearchRecipes'

The Search API accepts a query object that looks similar to this:

{
  "query": "string",
  "language": "string",
  "country": "string",
  "min_ingredients_should_match": 0,
  "max_time_in_minutes": 0,
  "min_health_score": 0,
  "ordering": "ORDERING_INVALID",
  "has_instructions": true,
  "paging": {
    ...
  },
  "labels": [
    ...
  ],
  "exclude_ingredients": {
    ...
  },
  "include_ingredients": {
    ...
  },
  
  "glycemic_filter": {
    ...
  },
  "nutrition": [
    ...
  ],
  "fields": [
    ...
  ],
  "custom_labels": {
    ...
  },
  "apply_implicit_preferences": true
}

It is made up of the following attributes:

ATTRIBUTE

TYPE

DESCRIPTION

query

string

Search phrase

language

string

recipes should be in this language, it allows only ISO 639-1 language codes Default value: en

minIngredientsShouldMatch

int

if includeIngredients specified, number of minimum ingredients, which should be matched

maxTimeInMinutes

int

recipes should take this total time at maximum

minHealthScore

double

filter recipes with healthScore more than specified value

ordering

has_instructions

paging

labels

exclude_ingredients

include_ingredients

glycemic_filter

nutrition

fields

array

custom_labels

object

boolean

Flag to switch search results between search with embeddings and regular search

In addition to the base attributes, there are additional objects that contain more detailed information on the search parameters.

Paging

"paging": {
  "limit": 0,
  "cursors": {
    "after": "string",
    "before": "string"
  }
},

Labels

"labels": [
  {
    "label": {
      "diet": "DIET_INVALID",
      "category": "CATEGORY_INVALID",
      "avoidance": "AVOIDANCE_INVALID",
      "cuisine": "CUISINE_INVALID",
      "meal_type": "MEAL_TYPE_INVALID",
      "nutrition": "NUTRITION_INVALID",
      "feature": "string",
      "holiday": "HOLIDAY_INVALID",
      "seasonality": "SEASONALITY_INVALID"
    },
    "boost": true
  }
],

Exclude Ingredients

"exclude_ingredients": {
  "list": [
    "string"
  ]
},

Include Ingredients

"include_ingredients": {
  "list": [
    "string"
  ]
},

Glycemic Filter

"glycemic_filter": {
  "glycemic_index": 0,
  "glycemic_load_total": 0,
  "glycemic_load_serving": 0
},

Nutrition

Allow to use numeric values of

  • Energy - NUTRITION_ENERGY

  • Fat - NUTRITION_FAT

  • Proteine - NUTRITION_PROTEINE

  • Carbohydrate - NUTRITION_CARBOHYDRATE

Values can set by using gt,lt,gte,lte expressions, for example:

    "nutrition": [
        {
            "nutrition": "NUTRITION_ENERGY",
            "condition": "CONDITION_GTE",
            "value": 50.0000000000001
        },
        {
            "nutrition": "NUTRITION_ENERGY",
            "condition": "CONDITION_LTE",
            "value": 100.1
        }
    ]

Ordering

"ordering": "ORDERING_INVALID",

Fields

"fields": [
  "RECIPE_FIELD_INVALID"
],

Custom Labels

"custom_labels": {
  "everywhere": {
    "in": [
      {
        "group": "string",
        "label": "string"
      }
    ],
    "boost_in": [
      {
        "group": "string",
        "label": "string"
      }
    ]
  },
  "in_recipe": {
    "in": [
      {
        "group": "string",
        "label": "string"
      }
    ],
    "boost_in": [
      {
        "group": "string",
        "label": "string"
      }
    ]
  },
  "in_ingredients": {
    "in": [
      {
        "group": "string",
        "label": "string"
      }
    ],
    "boost_in": [
      {
        "group": "string",
        "label": "string"
      }
    ]
  },
  "in_instruction_steps": {
    "in": [
      {
        "group": "string",
        "label": "string"
      }
    ],
    "boost_in": [
      {
        "group": "string",
        "label": "string"
      }
    ]
  }
}

Recipes with Full license

"whisk_origin_recipes": true

Apply Implicit Preferences

Field manages search resultes. Two options available: Search results with user's embedding, for users who accepted this type of search in ST app; And regular search results.

Default value: true

Fallback if embedding is not available: regular search results

"apply_implicit_preferences": true

Sample Request

curl -X POST "https://api.whisk.com/recipe/v2/search" \
  -H "accept: application/json" \
  -H "Authorization: Token <Access-Token>" \
  -H "Content-Type: application/json" \
  -d "{ \"language\": \"en\", \"paging\" : {\"limit\": 2}}"

Use-case request examples

Boost recipes by certain device, technique, ingredient

Using recipe labels to search or boost recipes will now also take intents set for this recipe into account. I.e. if a recipe has intents for an oven and search is performed with "device": "DEVICE_OVEN", the recipe will be boosted.

curl -X POST "https://api.whisk.com/recipe/v2/search" \
  -H "accept: application/json" \
  -H "Authorization: Token <Access-Token>" \
  -H "Content-Type: application/json" \
  -d '{
  "language": "ko",
  "country": "KR",
  "labels": [
    {
      "label": {
        "device": "DEVICE_OVEN"
      },
      "boost": true
    },
    {
      "label": {
        "technique": "TECHNIQUE_GRILLING"
      },
      "boost": true
    }
  ],
  "include_ingredients": {
    "list": [
      "Beef Tenderloin"
    ]
  },
  "min_ingredients_should_match": 0
}'

Search recipe with certain device intents

Note: recipes AI identified to be suitable for cooking with a device will also be returned, currently there's no way to search recipes strictly with intents defined

You may find the complete list of devices supported in search on API definition page under the section request body -> model -> labels -> device or in whisk.api.shared.v1.Device model

curl -X POST "https://api.whisk.com/recipe/v2/search" \
  -H "accept: application/json" \
  -H "Authorization: Token <Access-Token>" \
  -H "Content-Type: application/json" \
  -d '{
  "language": "ko",
  "country": "KR",
  "labels": [
    {
      "label": {
        "device": "DEVICE_PRESSURE_COOKER"
      },
      "boost": false
    }
  ],
  "fields": [
    " RECIPE_FIELD_INSTRUCTIONS", "RECIPE_FIELD_INSTRUCTION_INTENTS"
  ],
}'

Note: the instructions.steps.intents field is deprecated and will not be extended.

You will find intents in instructions.steps.instruction_intents.attributes field. Each intent comes as an object with following fields

  • equipment_action - describes what equipment and in what mode is going to be used (utensils like frying pan does not have modes or attributes at all)

  • attributes - the conditions to be used when cooking, the list may be empty which usually means until user stops the program on her own

The complete list of supported equipment, modes and attributes can be found inside whisk.api.recipe.v2.Intents model

e.g.

...
    "instructions": {
      "steps": [
        {
          "text": "After chicken is 'well-boiled' (the oil should be floating on top of the frying pan liquid), add the garam masala and turmeric and turn off the heat. Season with salt to taste. Stir all together and serve.",
          "intents": [
            {
              "name": "AirFryer.PreHeating",
              "options": [
                {
                  "key": "Temperature",
                  "value": 200,
                  "unit": "celsius"
                },
                {
                  "key": "Duration",
                  "value": 10,
                  "unit": "minute"
                }
              ]
            }
          ],
          "instruction_intents": {
            "intents": [
              {
                "attributes": [
                  {
                    "temperature": {
                      "value": {
                        "plain_value": 200
                      },
                      "unit": "UNIT_CELSIUS"
                    }
                  },
                  {
                    "duration": {
                      "value": {
                        "plain_value": 600
                      },
                      "unit": "UNIT_SECONDS"
                    }
                  }
                ],
                "equipment_action": {
                  "air_fryer": {
                    "mode": "MODE_HEAT"
                  }
                }
              },
              {
                "attributes": [
                  {
                    "duration": {
                      "value": {
                        "plain_value": 600
                      },
                      "unit": "UNIT_SECONDS"
                    }
                  },
                  {
                    "gas": {
                      "value": {
                        "plain_value": 3
                      },
                      "unit": "UNIT_LEVEL"
                    }
                  }
                ],
                "equipment_action": {
                  "oven": {
                    "mode": "MODE_GAS"
                  }
                }
              },
              {
                "equipment_action": {
                  "microwave": {
                    "mode": "MODE_DEFROST"
                  }
                }
              },
              {
                "attributes": [
                  {
                    "speed": {
                      "value": "LEVEL_MEDIUM",
                      "unit": "UNIT_LEVEL"
                    }
                  },
                  {
                    "duration": {
                      "value": {
                        "plain_value": 900
                      },
                      "unit": "UNIT_SECONDS"
                    }
                  }
                ],
                "equipment_action": {
                  "food_processor": {
                    "mode": "MODE_PREPARE"
                  }
                }
              },
              {
                "attributes": [
                  {
                    "duration": {
                      "value": {
                        "plain_value": 600
                      },
                      "unit": "UNIT_SECONDS"
                    }
                  }
                ],
                "equipment_action": {
                  "pressure_cooker": {
                    "mode": "MODE_MULTIGRAIN"
                  }
                }
              },
              {
                "attributes": [
                  {
                    "duration": {
                      "value": {
                        "plain_value": 600
                      },
                      "unit": "UNIT_SECONDS"
                    }
                  }
                ],
                "equipment_action": {
                  "slow_cooker": {
                    "mode": "MODE_KEEPWARM"
                  }
                }
              }
            ]
          }
        }
      ]
    },
...

Last updated