If you are reading this article, you are probably familiar with the possibilities that open up when you use an API (Application Programming Interface).
By using one of the many public APIs out there, you can expand the functionality of your application or enrich it with the necessary data.
But what if you have created your own unique functionality that you want to share with the community?
The answer is to create your own API.
Related: How to Use an API with Python
While this may seem like a daunting task at first glance, it’s actually quite easy.
We’ll break down how to do this with Python in the steps below.
Requirements
To create an API, we will use:
- Python 3 programming language and
- Flask – a simple and easy-to-use framework for creating web applications.
We will also need Flask-RESTful, an extension for Flask which enables rapid development of REST API with minimal setup. Installation is carried out by the command:
pip install flask-restful
Before We Get Started
We are about to develop a RESTful API that implements the basic CRUD functionality.
To fully understand the problem, let’s dive into these 2 terms mentioned above:
What is REST?
REST API (Representational state transfer) is an API that uses HTTP requests for communication.
REST APIs must follow certain constraints. Here are a few:
- Client-server architecture – the client handles the user interface, and the server handles the backend and data storage. Client and server are independent, and each of them can be replaced separately.
- Stateless – no data from the client is stored on the server-side. The session state is stored on the client-side.
- Cacheable – clients can cache server responses to improve performance.
What is CRUD?
CRUD is a programming concept that identifies four basic actions (create, read, update, and delete) that can be performed on the data.
In the REST API, Types of Requests or Request Methods are responsible for these actions:
- POST: Create action. Adds new data to the server. Using this type of request, we can, for example, add a new post to a blog.
- GET: Read action. Retrieve information. This is the most common type of request. Using it we can, for example, get a list of blog posts.
- PUT: Update action. Changes existing information. For example, using this type of request, it would be possible to change the text of the existing article.
- DELETE: Delete action. Deletes existing information, such as a blog post, that we are no longer interested in.
Now that we understand the basic terms, we can begin to create our API.
Implementation
Let’s create a repository of quotes about artificial intelligence (AI).
AI is one of the fastest-growing branches of human activity today, and Python is a popular tool for working with AI.
With this API, any Python developer can quickly retrieve a statement about artificial intelligence and be inspired for new achievements.
If the developer has his/her own worthwhile thoughts on this topic, then he/she will be able to add them to the repository.
Let’s start with importing the required modules and setting up the Flask application:
from flask import Flask from flask_restful import Api, Resource, reqparse import random app = Flask(__name__) api = Api(app)
In the indicated code snippet, Flask
, Api
and Resource
are the classes that we will need to work with.
Reqparse
is Flask-RESTful request parsing interface. We will also need the random
module to display a random quote.
Next, we will create a repository of quotes about AI.
Each entry in the repository will contain:
- a numerical id,
- the name of the author of the quote,
- and the quote itself.
Since this is a training example, we will store all entries in the Python list. In a real-world application, we would probably use a database instead.
ai_quotes = [ { "id": 0, "author": "Kevin Kelly", "quote": "The business plans of the next 10,000 startups are easy to forecast: " + "Take X and add AI." }, { "id": 1, "author": "Stephen Hawking", "quote": "The development of full artificial intelligence could " + "spell the end of the human race…. " + "It would take off on its own, and re-design " + "itself at an ever increasing rate. " + "Humans, who are limited by slow biological evolution, " + "couldn't compete, and would be superseded." }, { "id": 2, "author": "Claude Shannon", "quote": "I visualize a time when we will be to robots what " + "dogs are to humans, " + "and I’m rooting for the machines." }, { "id": 3, "author": "Elon Musk", "quote": "The pace of progress in artificial intelligence " + "(I’m not referring to narrow AI) " + "is incredibly fast. Unless you have direct " + "exposure to groups like Deepmind, " + "you have no idea how fast—it is growing " + "at a pace close to exponential. " + "The risk of something seriously dangerous " + "happening is in the five-year timeframe." + "10 years at most." }, { "id": 4, "author": "Geoffrey Hinton", "quote": "I have always been convinced that the only way " + "to get artificial intelligence to work " + "is to do the computation in a way similar to the human brain. " + "That is the goal I have been pursuing. We are making progress, " + "though we still have lots to learn about " + "how the brain actually works." }, { "id": 5, "author": "Pedro Domingos", "quote": "People worry that computers will " + "get too smart and take over the world, " + "but the real problem is that they're too stupid " + "and they've already taken over the world." }, { "id": 6, "author": "Alan Turing", "quote": "It seems probable that once the machine thinking " + "method had started, it would not take long " + "to outstrip our feeble powers… " + "They would be able to converse " + "with each other to sharpen their wits. " + "At some stage therefore, we should " + "have to expect the machines to take control." }, { "id": 7, "author": "Ray Kurzweil", "quote": "Artificial intelligence will reach " + "human levels by around 2029. " + "Follow that out further to, say, 2045, " + "we will have multiplied the intelligence, " + "the human biological machine intelligence " + "of our civilization a billion-fold." }, { "id": 8, "author": "Sebastian Thrun", "quote": "Nobody phrases it this way, but I think " + "that artificial intelligence " + "is almost a humanities discipline. It's really an attempt " + "to understand human intelligence and human cognition." }, { "id": 9, "author": "Andrew Ng", "quote": "We're making this analogy that AI is the new electricity." + "Electricity transformed industries: agriculture, " + "transportation, communication, manufacturing." } ]
Now let’s create the Quote
resource class.
It will determine the operation of the endpoints of our API. Inside the class, we will define four methods (remember: get, post, put, delete).
Let’s start with the GET method.
With this method, we can get a specific quote by specifying its id or get a random quote if the id is not specified:
class Quote(Resource): def get(self, id=0): if id == 0: return random.choice(ai_quotes), 200 for quote in ai_quotes: if(quote["id"] == id): return quote, 200 return "Quote not found", 404
The GET method returns a random quote if id contains a default value (that is, id was not specified when the method was called).
If the id is specified, then the method iterates through all the records in the quotes repository and returns the result that matches the specified id.
If the record cannot be found, the method returns the corresponding message (“Quote not found, 404”).
Note that the method returns a HTTP Status Code 200 to each response if the request is successful, 404 if the record is not found.
Next, let’s create a POST method to add a new quote to the repository.
This method will receive the identifier of the new quote at the input. Also, POST will use reqparse
to parse the parameters that will go in the body of the request (author and quote text).
def post(self, id): parser = reqparse.RequestParser() parser.add_argument("author") parser.add_argument("quote") params = parser.parse_args() for quote in ai_quotes: if(id == quote["id"]): return f"Quote with id {id} already exists", 400 quote = { "id": int(id), "author": params["author"], "quote": params["quote"] } ai_quotes.append(quote) return quote, 201
In the code snippet above, the POST method accepts id of the quote. Then usingreqparse
, it gets author and quote from the request and stores them in params dictionary.
If a quote with the specified id already exists, then the method returns the corresponding message and a 400 status code.
If a quote with the specified id has not yet been created, the method creates a new record with the specified id and author and quote parameters. Then it adds the record to ai_quotes
list, and then returns a record with a new quote and a 201 status code.
Now we will create the PUT method to modify the contents of an existing quote in the repository:
def put(self, id): parser = reqparse.RequestParser() parser.add_argument("author") parser.add_argument("quote") params = parser.parse_args() for quote in ai_quotes: if(id == quote["id"]): quote["author"] = params["author"] quote["quote"] = params["quote"] return quote, 200 quote = { "id": id, "author": params["author"], "quote": params["quote"] } ai_quotes.append(quote) return quote, 201
Similarly, with the previous example, the PUT method takes id at the input and then parses the quote parameters using reqparse
.
If a quote with the specified id exists, the method will update it with parsed parameters and return an updated quote with the 200 Status Code.
If there is no quote with the specified id yet, a new record will be created and returned with 201 Status Code.
Finally, let’s create a DELETE method to remove a quote that no longer seems inspirational to us:
def delete(self, id): global ai_quotes ai_quotes = [qoute for qoute in ai_quotes if qoute["id"] != id] return f"Quote with id {id} is deleted.", 200
The DELETE method receives the quote id at the input and updates the ai_quotes
list through global scope using list comprehension.
Now that we have created all the methods in our Quote resource, all we need to do is to add a resource to our API, specify its routes, and run our Flask application:
api.add_resource(Quote, "/ai-quotes", "/ai-quotes/", "/ai-quotes/<int:id>") if __name__ == '__main__': app.run(debug=True)
Our REST API Service is now ready!
Now we can save our code in the app.py
file and run it in the console with the command:
python3 app.py
If everything is in order, then we should see something like the following lines:
* Debug mode: on * Running on https://127.0.0.1:5000/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger PIN: XXXXXXX
Testing the API
Now that we’ve created our API, let’s test it!
We can test our API service using the curl console utility or Insomnia REST client or by publishing it on RapidAPI.
Publishing your API
RapidAPI is the world’s largest marketplace with over 10,000 APIs (and over 1,000,000 developers).
Not only does RapidAPI provide a single interface for working with third-party APIs, but it also provides the ability to quickly and seamlessly publish your own API.
To publish our API on RapidAPI, we need to first publish it on some server on the Internet.
In our case, let’s use Heroku. Publishing an API on Heroku is quite simple (learn more here).
How to Publish your API on Heroku
1. Install Heroku
First, we’ll need to register for a Heroku account and install the Heroku Command Line Interface (CLI) with the following command (works with Ubuntu 16+):
sudo snap install heroku --classic
Then log in to the Heroku CLI:
heroku login
2. Add the Necessary Files
Now, let’s add the necessary files for publication to the folder within our app:
- requirements.txt with a list of necessary Python modules
- Procfile, which explicitly declares what commands should be executed to start our app
- .gitignore, to exclude files we don’t need on the server
Our requirements.txt file will contain the following lines:
flask flask-restful gunicorn
Please note that we have added gunicorn
(Python WSGI HTTP Server) to our list, since it is necessary to run our app on the server.
Procfile will contain:
web: gunicorn app:app
and .gitignore will contain the following:
*.pyc __pycache__/
Now that we’ve created the necessary files, let’s initialize a git repository and commit:
git init git add . git commit -m "First API commit"
3. Create a new Heroku application
heroku create
And then finally push our master branch to the Heroku remote repository:
git push heroku master
Now we can start and open our API Service using the commands:
heroku ps:scale web=1 heroku open
Our API will now be accessible at: https://your-random-heroku-name.herokuapp.com/ai-quotes/
How to Add Your Python API to RapidAPI’s Marketplace
Now that our API service is published on Heroku, we can now add it to RapidAPI. You can find detailed documentation here for getting started:
1. Create a RapidAPI Account
First, sign up for a free RapidAPI account. You can do so with Facebook, Google, or even your Github account.
2. Add your API through the Provider Dashboard
Head on over to https://rapidapi.com/provider and click on “Add New API”.
From here, you’ll need to enter general information about your API.
After clicking ‘Add API’, the API overview definition page will appear. Here we can add some more information about our API.
Now, we can either manually enter your API’s endpoints or upload a swagger file using OpenAPI.
Let’s define the endpoints of our API on the Endpoints page. In our case, endpoints match the CRUD concept (get, post, put, delete).
Create a GET AI Quote endpoint that will return a random quote (if the id parameter has a default value) or a quote corresponding to a specific id.
To create an endpoint, click the “Create Endpoint” button.
Repeat this process for all other API endpoints.
And that’s it! Congratulations, you’ve published your API!
If all goes well, you’ll have an API page that looks like this:
Conclusion
In this article, we studied the process of creating our own RESTful API Service in Python, the process of publishing the API on the Heroku cloud platform, and then adding it to RapidAPI Catalog.
While we explored the basic principles of developing an API, we did not dive into the issues of security, fault tolerance, and scalability of the API.
In real (production) conditions, these issues should definitely be taken into account.
Raman Rayat says
Hey, I am facing one error, can you please take a look
Traceback (most recent call last):
File “D:\app.py”, line 102, in
class Quote(Resource):
File “D:\app.py”, line 158, in Quote
api.add_resource(Quote, “/ai-quotes”, “/ai-quotes/”, “/ai-quotes/”)
NameError: name ‘Quote’ is not defined
Richard Quiros says
you changed class name put the name of your class
class “Your className”(Resource):
code PY……
##DIRECTIONS
api.add_resource (“Your className”, “/ANYTHING”, “/ANYTHING/”, “/ANYTHING/”)
veryseriouscontributor says
hmm flask-restful seems deprecated and no longer maintained. What do you think about updating your libs?