Learning and Consume REST APIs using Python
There’s an amazing amount of data available on the Web. Many web services, like YouTube and GitHub, make their data accessible to third-party applications through an application programming interface (API). One of the most popular ways to build APIs is the REST architecture style. Python provides some great tools not only to get data from REST APIs but also to build your own Python REST APIs.
In this tutorial, you will learn:
l What REST architecture is
l How REST APIs provide access to web data
l How to consume data from REST APIs using the requests library
By using Python and REST APIs, you can retrieve, parse, update, and manipulate the data provided by any web service you’re interested in.
REST Architecture
REST stands for representational state transfer and is a software architecture style that defines a pattern for client and server communications over a network. REST provides a set of constraints for software architecture to promote performance, scalability, simplicity, and reliability in the system.
REST defines the following architectural constraints:
u Stateless: The server won’t maintain any state between requests from the client.
u Client-server: The client and server must be decoupled from each other, allowing each to develop independently.
u Cacheable: The data retrieved from the server should be cacheable either by the client or by the server.
u Uniform interface: The server will provide a uniform interface for accessing resources without defining their representation.
u Layered system: The client may access the resources on the server indirectly through other layers such as a proxy or load balancer.
u Code on demand (optional): The server may transfer code to the client that it can run, such as JavaScript for a single-page application.
Note, REST is not a specification but a set of guidelines on how to architect a network-connected software system.
REST APIs and Web Services
A REST web service is any web service that adheres to REST architecture constraints. These web services expose their data to the outside world through an API. REST APIs provide access to web service data through public web URLs.
For example, here’s one of the URLs for GitHub’s REST API:
https://api.github.com/users/<username>
This URL allows you to access information about a specific GitHub user. You access data from a REST API by sending an HTTP request to a specific URL and processing the response.
HTTP Methods
REST APIs listen for HTTP methods like GET, POST, and DELETE to know which operations to perform on the web service’s resources. A resource is any data available in the web service that can be accessed and manipulated with HTTP requests to the REST API. The HTTP method tells the API which action to perform on the resource.
While there are many HTTP methods, the five methods listed below are the most commonly used with REST APIs:
HTTP method | Description |
GET | Retrieve an existing resource. |
POST | Create a new resource. |
PUT | Update an existing resource. |
PATCH | Partially update an existing resource. |
DELETE | Delete a resource. |
A REST API client application can use these five HTTP methods to manage the state of resources in the web service.
Status Codes
Once a REST API receives and processes an HTTP request, it will return an HTTP response. Included in this response is an HTTP status code. This code provides information about the results of the request. An application sending requests to the API can check the status code and perform actions based on the result. These actions could include handling errors or displaying a success message to a user.
Below is a list of the most common status codes returned by REST APIs:
Code | Meaning | Description |
200 | OK | The requested action was successful. |
201 | Created | A new resource was created. |
202 | Accepted | The request was received, but no modification has been made yet. |
204 | No Content | The request was successful, but the response has no content. |
400 | Bad Request | The request was malformed. |
401 | Unauthorized | The client is not authorized to perform the requested action. |
404 | Not Found | The requested resource was not found. |
415 | Unsupported Media Type | The request data format is not supported by the server. |
422 | Unprocessable Entity | The request data was properly formatted but contained invalid or missing data. |
500 | Internal Server Error | The server threw an error when processing the request. |
These ten status codes represent only a small subset of the available HTTP status codes. Status codes are numbered based on the category of the result:
Code range | Category |
2xx | Successful operation |
3xx | Redirection |
4xx | Client error |
5xx | Server error |
HTTP status codes come in handy when working with REST APIs as you’ll often need to perform different logic based on the results of the request.
API Endpoints
A REST API exposes a set of public URLs that client applications use to access the resources of a web service. These URLs, in the context of an API, are called endpoints.
To help clarify this, take a look at the table below. In this table, you’ll see API endpoints for a hypothetical CRM system. These endpoints are for a customer resource that represents potential customers in the system:
HTTP method | API endpoint | Description |
GET | /customers | Get a list of customers. |
GET | /customers/<customer_id> | Get a single customer. |
POST | /customers | Create a new customer. |
PUT | /customers/<customer_id> | Update a customer. |
PATCH | /customers/<customer_id> | Partially update a customer. |
DELETE | /customers/<customer_id> | Delete a customer. |
Each of the endpoints above performs a different action based on the HTTP method.
Note: The base URL for the endpoints has been omitted for brevity. In reality, you’ll need the full URL path to access an API endpoint:
https://api.example.com/customers
This is the full URL you’d use to access this endpoint. The base URL is everything besides /customers.
You’ll note that some endpoints have <customer_id> at the end. This notation means you need to append a numeric customer_id to the URL to tell the REST API which customer you’d like to work with.
The endpoints listed above represent only one resource in the system. Production-ready REST APIs often have tens or even hundreds of different endpoints to manage the resources in the web service.
REST and Python: Consuming APIs
To write code that interacts with REST APIs, most Python developers turn to requests to send HTTP requests. This library abstracts away the complexities of making HTTP requests. It’s one of the few projects worth treating as if it’s part of the standard library.
To start using requests, you need to install it first. You can use pip to install it:
$ python -m pip install requests
Now that you’ve got requests installed, you can start sending HTTP requests.
GET
GET is one of the most common HTTP methods you’ll use when working with REST APIs. This method allows you to retrieve resources from a given API. GET is a read-only operation, so you shouldn’t use it to modify an existing resource.
To test out GET and the other methods in this section, you’ll use a service called JSONPlaceholder. This free service provides fake API endpoints that send back responses that requests can process.
To try this out, start up the Python REPL and run the following commands to send a GET request to a JSONPlaceholder endpoint:
>>> import requests>>> api_url = "https://jsonplaceholder.typicode.com/todos/1">>> response = requests.get(api_url)>>> response.json(){'userId': 1, 'id': 1, 'title': 'delectus aut autem', 'completed': False}
This code calls requests.get() to send a GET request to /todos/1, which responds with the todo item with the ID 1. Then you can call .json() on the response object to view the data that came back from the API.
The response data is formatted as JSON, a key-value store similar to a Python dictionary. It’s a very popular data format and the de facto interchange format for most REST APIs.
Beyond viewing the JSON data from the API, you can also view other things about the response:
>>> response.status_code200
>>> response.headers["Content-Type"]'application/json; charset=utf-8'
Here, you access response.status_code to see the HTTP status code. You can also view the response’s HTTP headers with response.headers. This dictionary contains metadata about the response, such as the Content-Type of the response.
POST
Now, take a look at how you use requests to POST data to a REST API to create a new resource. You’ll use JSONPlaceholder again, but this time you’ll include JSON data in the request. Here’s the data that you’ll send:
{ "userId": 1, "title": "Buy milk", "completed": false}
This JSON contains information for a new todo item. Back in the Python REPL, run the following code to create the new todo:
>>> import requests>>> api_url = "https://jsonplaceholder.typicode.com/todos">>> todo = {"userId": 1, "title": "Buy milk", "completed": False}>>> response = requests.post(api_url, json=todo)>>> response.json(){'userId': 1, 'title': 'Buy milk', 'completed': False, 'id': 201}
>>> response.status_code201
Here, you call requests.post() to create a new todo in the system.
First, you create a dictionary containing the data for your todo. Then you pass this dictionary to the json keyword argument of requests.post(). When you do this, requests.post() automatically sets the request’s HTTP header Content-Type to application/json. It also serializes todo into a JSON string, which it appends to the body of the request.
If you don’t use the json keyword argument to supply the JSON data, then you need to set Content-Type accordingly and serialize the JSON manually. Here’s an equivalent version to the previous code:
>>> import requests>>> import json>>> api_url = "https://jsonplaceholder.typicode.com/todos">>> todo = {"userId": 1, "title": "Buy milk", "completed": False}>>> headers = {"Content-Type":"application/json"}>>> response = requests.post(api_url, data=json.dumps(todo), headers=headers)>>> response.json(){'userId': 1, 'title': 'Buy milk', 'completed': False, 'id': 201}
>>> response.status_code201
In this code, you add a headers dictionary that contains a single header Content-Type set to application/json. This tells the REST API that you’re sending JSON data with the request.
You then call requests.post(), but instead of passing todo to the json argument, you first call json.dumps(todo) to serialize it. After it’s serialized, you pass it to the data keyword argument. The data argument tells requests what data to include in the request. You also pass the headers dictionary to requests.post() to set the HTTP headers manually.
When you call requests.post() like this, it has the same effect as the previous code but gives you more control over the request.
Note: json.dumps() comes from the json package in the standard library. This package provides useful methods for working with JSON in Python.
Once the API responds, you call response.json() to view the JSON. The JSON includes a generated id for the new todo. The 201 status code tells you that a new resource was created.
PUT
Beyond GET and POST, requests provides support for all the other HTTP methods you would use with a REST API. The following code sends a PUT request to update an existing todo with new data. Any data sent with a PUT request will completely replace the existing values of the todo.
You’ll use the same JSONPlaceholder endpoint you used with GET and POST, but this time you’ll append 10 to the end of the URL. This tells the REST API which todo you’d like to update:
>>> import requests>>> api_url = "https://jsonplaceholder.typicode.com/todos/10">>> response = requests.get(api_url)>>> response.json(){'userId': 1, 'id': 10, 'title': 'illo est ... aut', 'completed': True}
>>> todo = {"userId": 1, "title": "Wash car", "completed": True}>>> response = requests.put(api_url, json=todo)>>> response.json(){'userId': 1, 'title': 'Wash car', 'completed': True, 'id': 10}
>>> response.status_code200
Here, you first call requests.get() to view the contents of the existing todo. Next, you call requests.put() with new JSON data to replace the existing to-do’s values. You can see the new values when you call response.json(). Successful PUT requests will always return 200 instead of 201 because you aren’t creating a new resource but just updating an existing one.
PATCH
Next up, you’ll use requests.patch() to modify the value of a specific field on an existing todo. PATCH differs from PUT in that it doesn’t completely replace the existing resource. It only modifies the values set in the JSON sent with the request.
You’ll use the same todo from the last example to try out requests.patch(). Here are the current values:
{'userId': 1, 'title': 'Wash car', 'completed': True, 'id': 10}
Now you can update the title with a new value:
>>> import requests>>> api_url = "https://jsonplaceholder.typicode.com/todos/10">>> todo = {"title": "Mow lawn"}>>> response = requests.patch(api_url, json=todo)>>> response.json(){'userId': 1, 'id': 10, 'title': 'Mow lawn', 'completed': True}
>>> response.status_code200
When you call response.json(), you can see that title was updated to Mow lawn.
DELETE
Last but not least, if you want to completely remove a resource, then you use DELETE. Here’s the code to remove a todo:
>>> import requests>>> api_url = "https://jsonplaceholder.typicode.com/todos/10">>> response = requests.delete(api_url)>>> response.json(){}
>>> response.status_code200
You call requests.delete() with an API URL that contains the ID for the todo you would like to remove. This sends a DELETE request to the REST API, which then removes the matching resource. After deleting the resource, the API sends back an empty JSON object indicating that the resource has been deleted.
The requests library is an awesome tool for working with REST APIs and an indispensable part of your Python tool belt. In the next section, you’ll change gears and consider what it takes to build a REST API.
No comments: