Flask API Tutorials

How to Create a Food Website (using the Spoonacular API) [Python & Flask]

Nutrition is an essential aspect of human life but many people have problems with it.

Some don’t have enough time and passion for eating healthy food, while others like to cook the same old dishes.

Today we will solve this issue, improve our diet, and learn how to create a beautiful web application for recipe search using APIs.

How to Create a Food Website with an API

  1. Choose the right Recipe API
  2. Add a Search Functionality with the Recipe API
  3. Create a Search Results Page
  4. Create a Detailed Recipe Page

If you’re looking to start a food blog, this link may be helpful. (Don’t forget to SEO your food blog, like building backlinks and promotion.)

Spoonacular’s Recipe, Food, & Nutrition API

For our purposes, we’ll be using the Spoonacular API as the targeted data source.

It is a comprehensive database for food information and data. 

You can find and share enormous amounts of recipes in real-life.

This API includes over 365,000 recipes, which includes over 80,000 ingredients, making it one of the best recipe APIs that includes full access to recipes and cooking instructions.

Also, the API allows for searching the recipes and cooking procedures via natural language. For example, you can search for something like “vegan desserts”, and you should receive some comprehensive search results.

Spoonacular has powerful calculation features for calories, pricing, and many other aspects. A potential user can even see a visualization of different meals of different cuisines.

Finally, there is a space for custom diets.

You can submit your particular diet, or follow the rules of some specific diet template.

This is only the tip of the iceberg – there are even associated chatbots and an ingredients autocompleting system!

How to use and get an API key to the Spoonacular API

In order to use the Spoonacular API, we will be using the RapidAPI platform.

Simply, sign up for a free RapidAPI user account and subscribe to the API.  (hint: there’s a Basic Plan that allows you to do 500 free requests/day).

We’ll be coding in Python for this project, and we will create a Flask application with a bright demonstration of the nutrition API power. So be sure to have Python 3.7 on your machine.

Recipe API Overview

 Here are some of the top API endpoints:

The medial side responds to the headers and parameters within the chosen endpoint.

Here, you can check your unique RapidAPI id and host of the tested product.

It is a very informative way to meet a user with a request functionality.

Each input field has a description; you can change and test different combinations of them.

If you would like to see the results of the previously mentioned tests, look to the right side, you can find code snippets for all supported programming languages

Now let’s review a practical example.

How to Build a Food App or Website using the Spoonacular API

“What’s in your fridge” website

Once in awhile, we’ll peek into our fridge and make some strange food combinations.

Let’s create an app that helps us find recipes given the ingredients we have available.

1. Create a Search Page

First, let’s create a starting point for our users with a search page. If the user chooses not to type anything, the system still will back a few random propositions.

After that, the user can choose and open a detailed view of a single recipe. Each page should include dynamic information and use multiple API endpoints. The full list of them is as follows:

  • GET random joke – just a funny way to say hello;
  • GET recipes by ingredients’ enumeration – the basic type of recipe search. Ingredients are typed with comma-separator;
  • GET random recipes – in case of user’s interest in something new;
  • GET recipe information by ID – detailed text info with a recipe image;
  • VISUALIZE recipe ingredients by ID – default HTML-widget from Spoonacular for the ingredients demonstration;
  • VISUALIZE recipe equipment by ID – one more widget, but for the equipment.

As mentioned before, we will make a web application using Python, so we will need a framework. Luckily, Python has a swift and very lightweight solution called Flask – a microframework, which is also quite an active programming tool. Don’t forget to install it into your Python environment.

pip install flask

The next required library – requests. It is one of the possible solutions for HTTP requests handling. 

pip install requests

Now you can create a folder for a web-app and .py file. Let’s use Bootstrap for a prettified HTML layout – an open-sourced library for many CSS templates.

Next, we will create a templates subfolder with all the layout snippets. Each of them will be executed after the backside data processing. You need to have a method-handler per each unique data action. This method should render a template with a dynamic response creating.

Next, let’s use Jinja2 – a method of connecting Python code with HTML templates. It’s a syntax language for Python commands using mark-up. Okay, let’s now code something!

First, let’s create a base.html file that will be a basic template for all of the next steps.

<!doctype html>
<html lang="en">
  <head>
  <!-- Required meta tags -->
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <!-- Bootstrap CSS -->
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossorigin="anonymous">

  <!-- Optional JavaScript -->
  <!-- jQuery first, then Popper.js, then Bootstrap JS -->
  <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" crossorigin="anonymous"></script>
  <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script>

  <title>{% block title %} Base page {% endblock %}</title>
  </head>
  <body>
  {% block body %}
     Body block
  {% endblock %}
  </body>
</html>

Here we include all the required style files and create blocks for the dynamic data.

Now we can try to work with the .py file.

Paste the following lines in it:

from flask import Flask, render_template, request

import requests
app = Flask(__name__)

url = "https://spoonacular-recipe-food-nutrition-v1.p.rapidapi.com/"

headers = {
  'x-rapidapi-host': "spoonacular-recipe-food-nutrition-v1.p.rapidapi.com",
  'x-rapidapi-key': "<YOUR_RAPID_API_KEY>",
  }

random_joke = "food/jokes/random"
find = "recipes/findByIngredients"
randomFind = "recipes/random"

@app.route('/')
def search_page():
  joke_response = str(requests.request("GET", url + random_joke, headers=headers).json()['text'])
  return render_template('search.html', joke=joke_response)

if __name__ == '__main__':
  app.run()

Now we’ll create some global variables for the endpoints.

Let’s also route the home page with the search.

After receiving the joke, we pass it to the template.

Now create and modify search.html in the following way:

{% extends 'base.html' %}
{% block title %} Recipes searcher {% endblock %}
{% block body %}

<center>
  <h2 style="padding-top: 3%;">
     It's time for cooking! What do you have in the fridge? <br>
     <small class="text-muted"> Let's warm up with a joke: {{ joke }} </small>
  </h2>
  <br><br>
  <form action="/recipes">
     <div class="input-group mb-3" style="max-width: 50%;">
         <input type="text" class="form-control" placeholder="Apple, flour, sugar" aria-label="Ingridients" aria-describedby="button-addon2" name="ingridients">
         <div class="input-group-append">
             <button class="btn btn-outline-secondary" type="submit" id="button-addon2">Search</button>
         </div>         
     </div>
  </form>
</center>
{% endblock %}

Pretty simple layout creation. But let’s try to execute our project by running the .py file.

2. Create a Recipe Search Results Page

Now let’s display the search results.

Let’s display them in a list, so we will need one more method-handler + template.

We can start with the first one.

Paste the following code under the search_page method within the .py file.

@app.route('/recipes')
def get_recipes():
  if (str(request.args['ingridients']).strip() != ""):
     # If there is a list of ingridients -> list
     querystring = {"number":"5","ranking":"1","ignorePantry":"false","ingredients":request.args['ingridients']}
     response = requests.request("GET", url + find, headers=headers, params=querystring).json()
     return render_template('recipes.html', recipes=response)
  else:
     # Random recipes
     querystring = {"number":"5"}
     response = requests.request("GET", url + randomFind, headers=headers, params=querystring).json()
     print(response)
     return render_template('recipes.html', recipes=response['recipes'])

Here we will request two different endpoints depending on the search results.

But don’t forget about the templates.

Create a recipes.html file and paste the following lines:

{% extends 'base.html' %}
{% block title %} Recipes searcher {% endblock %}
{% block body %}
<h2 style="text-align: center"> <a href="/" style="text-decoration: none; color:red"> Recipes for you: </a></h2>
<div style="margin-left:35%;">
  <ul class="list-unstyled">
     {% for recipe in recipes %}
         <li class="media">
             <img src="{{recipe['image']}}" class="align-self-center mr-3" alt="..." width="15%" height="15%">
             <div class="media-body">
                 <h5 class="mt-0 mb-1"><a href="/recipe?id={{ recipe['id'] }}">{{ recipe['title'] }}</a></h5>
                 {% if 'likes' not in recipe%}
                     How many minutes for preparation? {{recipe['preparationMinutes']}} <br>
                     How many minutes for cooking? {{recipe['cookingMinutes']}}  <br>
                     How many likes has this recipe? {{recipe['aggregateLikes']}}  <br>
                 {% else %}
                     How many your ingridients? {{recipe['usedIngredientCount']}} <br>
                     How many missed ingridients? {{recipe['missedIngredientCount']}}  <br>
                     How many likes has this recipe? {{recipe['likes']}}  <br>
                 {% endif %}
             </div>
         </li> <br>
     {% endfor %}
  </ul>
</div >
{% endblock %}

Again, there is a clear separation of two different ways of generating recipes. 

Now let’s test it. Let’s try searching for something:

Here are the results for “pork, lemon, honey”.

The user can compare the original recipe and the pointed products.

But how will our app work without any search?

When no search is performed, five random dishes are displayed, including the amount of time required for preparing and cooking.

3. Create detailed Recipe Pages

Now, let’s get a detailed display of the recipes. Let’s start with a usual method in the .py file.

@app.route('/recipe')
def get_recipe():
  recipe_id = request.args['id']
  recipe_info_endpoint = "recipes/{0}/information".format(recipe_id)
  ingedientsWidget = "recipes/{0}/ingredientWidget".format(recipe_id)
  equipmentWidget = "recipes/{0}/equipmentWidget".format(recipe_id)

  recipe_info = requests.request("GET", url + recipe_info_endpoint, headers=headers).json()
    
  recipe_headers = {
     'x-rapidapi-host': "spoonacular-recipe-food-nutrition-v1.p.rapidapi.com",
     'x-rapidapi-key': "<YOUR_RAPID_API_KEY>",
     'accept': "text/html"
  }
  querystring = {"defaultCss":"true", "showBacklink":"false"}

  recipe_info['inregdientsWidget'] = requests.request("GET", url + ingedientsWidget, headers=recipe_headers, params=querystring).text
  recipe_info['equipmentWidget'] = requests.request("GET", url + equipmentWidget, headers=recipe_headers, params=querystring).text
    
  return render_template('recipe.html', recipe=recipe_info)

Here you’ll see some interesting new lines of code.

First, let’s execute three requests, instead of one.

Second, we will create sub-headers to satisfy the accept header requirements for the content type.

Finally, we will include a default CSS for the widget integration.

Create a recipe.html template and paste the following snippet:

{% extends 'base.html' %}
{% block title %} {{recipe['title']}} {% endblock %}
{% block body %}

<div style="max-width: 70%; margin: auto; padding-top:3%">
  <div class="media">
     <img src="{{recipe['image']}}" class="mr-3" alt="..." width="20%" height="20%">
     <div class="media-body">
       <h5 class="mt-0">{{recipe['title']}}</h5>
       {{recipe['instructions']}}
     </div>
  </div><br>
  {{recipe['inregdientsWidget'] | safe }}<br>
  {{recipe['equipmentWidget'] | safe}}
</div>
{% endblock %}

Voila! The app is completed.

Of course, it isn’t ideal yet, but even now it has many interesting aspects to surf.

The most important thing: “| safe” keyword.

This code indicates HTML markup, so all tags and scripts will be executed. Rerun your project and visit some recipe pages.

Conclusion

Today we have introduced the Spoonacular API.

It is a powerful and multi-functional solution for food nutrition.

We created a visible and robust web application.

The basis of this app was made via RapidAPI endpoints.

Our project can propose fresh recipes depending on the contents of your fridge. 

 

 

5/5 - (5 votes)

View Comments

  • Hi,

    does it work the same way for Wagtail ? I mean the way to install the API :)

    Have a good day :)

  • Looks good! Is there a basic folder with all the files to make this usable on a localhost...no way for me to code such a use of an api; plus I can't afford for someone to write such code.

  • This post is really amazing and very informative

    your blog inspiration for beginners

    Thank for sharing your knowledge

Share
Published by

Recent Posts

Power Up Your Enterprise Hub: New March Release Boosts Admin Capabilities and Streamlines Integrations

We're thrilled to announce the latest update to the Rapid Enterprise API Hub (version 2024.3)!…

2 weeks ago

Unveiling User Intent: How Search Term Insights Can Empower Your Enterprise API Hub

Are you curious about what your API consumers are searching for? Is your Hub effectively…

2 weeks ago

Rapid Enterprise API Hub Levels Up Custom Branding, Monetization, and Management in February Release

The RapidAPI team is excited to announce the February 2024 update (version 2024.2) for the…

4 weeks ago

Supercharge Your Enterprise Hub with January’s Release: Search Insights, Login Flexibility, and More!

This January's release brings exciting features and improvements designed to empower you and your developers.…

3 months ago

Enhanced Functionality and Improved User Experience with the Rapid API Enterprise Hub November 2023 Release

Rapid API is committed to providing its users with the best possible experience, and the…

5 months ago

The Power of Supporting Multiple API Gateways in an API Marketplace Platform

In today's fast-paced digital world, APIs (Application Programming Interfaces) have become the backbone of modern…

6 months ago