Python Requests Library & FastAPI
Last updated
Last updated
First, you’ll need to have the necessary software; make sure you have and installed on your machine. Then, head over to the command line and install the python requests module with pip:
Now you’re ready to start using Python Requests to interact with a REST API, make sure you import the Requests library into any scripts you want to use it in:
How Request Data With GET
The GET method is used to access data for a specific resource from a REST API; Python Requests includes a function to do exactly this.
The response object contains all the data sent from the server in response to your GET request, including headers and the data payload. When this code example prints the response object to the console it simply returns the name of the object’s class and the status code the request returned (more on status codes later).
While this information might be useful, you’re most likely interested in the content of the request itself, which can be accessed in a few ways:
How to Use Query Parameters
Queries can be used to filter the data that an API returns, and these are added as query parameters that are appended to the endpoint URL. With Python Requests, this is handled via the params argument, which accepts a dictionary object; let’s see what that looks like when we use the Open Notify API to GET an estimate for when the ISS will fly over a specified point:
The print command would return something that looks like this:
How to Create and Modify Data With POST and PUT
In a similar manner as the query parameters, you can use the data argument to add the associated data for PUT and POST method requests.
How to Access REST Headers
You can also retrieve metadata from the response via headers. For example, to view the date of the response, just specify that with the `headers` property:
For open APIs that covers the basics. However, many APIs can’t be used by just anyone. For those, let’s go over how to authenticate to REST APIs.
So far you’ve seen how to interact with open REST APIs that don’t require any authorization. However, many REST APIs require you to authenticate to them before you can access specific endpoints, particularly if they deal with sensitive data.
There are a few common authentication methods for REST APIs that can be handled with Python Requests. The simplest way is to pass your username and password to the appropriate endpoint as HTTP Basic Auth; this is equivalent to typing your username and password into a website.
Once you have an access token, you can provide it as a bearer token in the request header: this is the most secure way to authenticate to a REST API with an access token:
Use Sessions to Manage Access Tokens
Session objects come in handy when working with Python Requests as a tool to persist parameters that are needed for making multiple requests within a single session, like access tokens. Also, managing session cookies can provide a nice performance increase because you don’t need to open a new connection for every request.
API calls don’t always go as planned, and there’s a multitude of reasons why API requests might fail that could be the fault of either the server or the client. If you’re going to use a REST API, you need to understand how to handle the errors they output when things go wrong to make your code more robust. This section covers everything you need to know about handling HTTP errors with Python Requests.
The Basics of HTTP Status Codes
Before we dive into the specifics of Python Requests, we first need to take a step back and understand what HTTP status codes are and how they relate to errors you might encounter.
All status codes fall into one of five categories.
1xx Informational – Indicates that a request has been received and that the client should continue to make the requests for the data payload. You likely won’t need to worry about these status codes while working with Python Requests.
2xx Successful – Indicates that a requested action has been received, understood, and accepted. You can use these codes to verify the existence of data before attempting to act on it.
3xx Redirection – Indicates that the client must make an additional action to complete the request like accessing the resource via a proxy or a different endpoint. You may need to make additional requests or modify your requests to deal with these codes.
4xx Client Error – Indicates problems with the client, such as a lack of authorization, forbidden access, disallowed methods, or attempts to access nonexistent resources. This usually indicates configuration errors on the client application.
5xx Server Error – Indicates problems with the server that provides the API. There are a large variety of server errors and they often require the API provider to resolve.
How to Check for HTTP Errors With Python Requests
The response objects has a status_code attribute that can be used to check for any errors the API might have reported. The next example shows how to use this attribute to check for successful and 404 not found HTTP status codes, and you can use this same format for all HTTP status codes.
To see this in action, try removing the last letter from the URL endpoint, the API should return a 404 status code.
If you want requests to raise an exception for all error codes (4xx and 5xx), you can use the raise_for_status() function and catch specific errors using Requests built-in exceptions. This next example accomplishes the same thing as the previous code example.
TooManyRedirects
Something that is often indicated by 3xx HTTP status codes is the requirement to redirect to a different location for the resource you’re requesting. This can sometimes result in a situation where you end up with an infinite redirect loop. The Python Requests module has the TooManyRedirects error that you can use to handle this problem. To resolve this problem, it’s likely the URL you’re using to access the resource is wrong and needs to be changed.
You can optionally use the request options to set the maximum number of redirects:
Or disable redirecting completely within your request options:
ConnectionError
So far, we’ve only looked at errors that come from an active server. What happens if you don’t receive a response from the server at all? Connection errors can occur for many different reasons, including a DNS failure, refused connection, internet connectivity issues or latency somewhere in the network. Python Requests offers the ConnectionError exception that indicates when your client is unable to connect to the server.
This type of error might be temporary, or permanent. In the former scenario, you should retry the request again to see if there is a different result. In the latter scenario, you should make sure you’re able to deal with a prolonged inability to access data from the API, and it might require you to investigate your own connectivity issues.
Timeout
Timeout errors occur when you’re able to connect to the API server, but it doesn’t complete the request within the allotted amount of time. Similar to the other errors we’ve looked at, Python Requests can handle this error with a Timeout exception:
In this example, the timeout was set as a fraction of a second via the request options. Most APIs are unable to respond this quickly, so the code will produce a timeout exception. You can avoid this error by setting longer timeouts for your script, optimizing your requests to be smaller, or setting up a retry loop for the request. This can also sometimes indicate a problem with the API provider. One final solution is to incorporate asynchronous API calls to prevent your code from stopping while it waits for larger responses.
As we’ve seen, the Requests module elegantly handles common API request errors by utilizing exception handling in Python. If we put all of the errors we’ve talked about together, we have a rather seamless way to handle any HTTP request error that comes our way:
So far, we have learned how to use APIs to get data from external sources. So, we have been on the client side asking for data. We were not particularly interested in how our requests were being processed. We were just interested in the response Json data we would be receiving. But from now on, we will switch roles and be on the server side. We will process the incoming API requests on our back-end code and send back a response object to the client side. We will not be consuming APIs, but we will be creating them. For that, we will be using a Python library called FastAPI.
FastAPI is a modern, fast (high-performance), web framework for building APIs with Python 3.7+ based on standard Python type hints. It is built on top of Starlette and pydantic, and is one of the fastest Python web frameworks available.
Here are some key features of FastAPI:
Fast: Very high performance, on par with NodeJS and Go (thanks to Starlette and Pydantic). One of the fastest Python frameworks available.
Fast to code: Increase the speed to develop features significantly.
Fewer bugs: Reduce about 40% of human (developer) induced errors.
Fewer lines of code: Achieve more with fewer lines of code.
Fewer files: A single file for a small project.
Auto-validation of request parameters using Python 3.6+ type hints.
Automatic generation of OpenAPI documentation (including example requests and responses) using pydantic models and Python 3.6+ type hints.
Automatic generation of Swagger UI from the OpenAPI documentation.
Async support with async/await syntax.
WebSockets support.
GraphQL support with automatic schema generation.
Dependency injection and easy integration with other libraries.
To use FastAPI, you will need to install it first. You can do this using pip
, the Python package manager:
Once you have FastAPI installed, you can start using it to build your API. Here are the steps you can follow to start a new FastAPI project:
Create a new directory for your project and navigate to it.
Create a new virtual environment for your project. This is optional, but recommended, as it will allow you to isolate the packages you install for this project from other projects on your system. You can do this using python -m venv env
, where env
is the name of your virtual environment.
Activate the virtual environment by running source env/bin/activate
on Linux or macOS, or env\Scripts\activate.bat
on Windows.
Install FastAPI and any other dependencies your project needs using pip
. For example, to install FastAPI, you can run pip install fastapi
.
Create a new file for your API, such as main.py
.
Import FastAPI and create a new instance of the FastAPI
class.
Define your API endpoints using FastAPI's routing decorators (e.g. @app.get()
, @app.post()
, etc.).
Run the API using uvicorn.run()
. For example: uvicorn.run(app, host="0.0.0.0", port=8000)
. This will start the API on port 8000 of your local machine.
Here's an example of what your main file might look like:
Alternatively, you can run the server from your terminal with the command:
The command uvicorn main:app --reload
refers to:
main
: the file main.py
(the Python "module").
app
: the object created inside of main.py
with the line app = FastAPI()
.
--reload
: make the server restart after code changes. Only do this for development.
You will see the JSON response as:
You already created an API that:
Receives HTTP requests in the paths /
and /items/{item_id}
.
Both paths take GET
operations (also known as HTTP methods).
The path /items/{item_id}
has a path parameter item_id
that should be an int
.
The path /items/{item_id}
has an optional str
query parameter q
.
Now modify the file main.py
to receive a body from a PUT
request.
Declare the body using standard Python types, thanks to Pydantic.
The server should reload automatically (because you added --reload
to the uvicorn
command above).
The interactive API documentation will be automatically updated, including the new body:
Click on the button "Try it out", it allows you to fill the parameters and directly interact with the API:
Then click on the "Execute" button, the user interface will communicate with your API, send the parameters, get the results and show them on the screen:
In summary, you declare once the types of parameters, body, etc. as function parameters.
You do that with standard modern Python types.
You don't have to learn a new syntax, the methods or classes of a specific library, etc.
Just standard Python 3.7+.
For example, for an int
:
or for a more complex Item
model:
...and with that single declaration you get:
Editor support, including:
Completion.
Type checks.
Validation of data:
Automatic and clear errors when the data is invalid.
Validation even for deeply nested JSON objects.
Here is an hour-long video which covers all the basics of FastAPI library, make sure to carefully watch and code-along:
Once you finish watching/coding-along the video above, check out this video as well. This one will help you consolidate the things you have learned in the first video and will teach you some more advanced concepts (as always, try to code along the video):
A more secure method is to get an access token that acts as an equivalent to a username/password combination; the method to get an access token varies widely from API to API, but the most common framework for API authentication is . Here at Nylas, we use to grant an access token for user accounts that is restricted to scopes that define the specific data and functionality that can be accessed. This process is demonstrated in the
There are quite a few other methods to authenticate to a REST API, including digest, , , and AuthBase. The use of these depends on the architecture decisions of the REST API producer.
To start the API, simply run the main file using Python: python main.py
. This will start the API and make it available at .
Open your browser at .
Now go to .
You will see the automatic interactive API documentation (provided by ):
Now go to .
For a more complete example including more features, see the .