Data behind an API endpoint can be secured or not. Anyone can access the data using the API address if it is not secured. On the other hand, for secured APIs, the user must be authenticated before being authorized (allowed access).
In the Python requests module, authentication credentials required by secured APIs are passed using headers. There are various methods of authentication that can be implemented efficiently on the requests module.
This article will discuss three of them: basic authentication, bearer or token, digest authentication, and OAuth 1 Authentication. Before that, however, let’s go through unsecured APIs.
Note: For security and privacy reasons, passwords, usernames, API keys, tokens, and any other personal information shared here is not accurate. They are just dummies I generated for demonstration purposes. Be careful not to share your private information as well.
Unsecured APIs (Example: open.er API)
These are APIs that require no authentication. As an example, let’s make a request to https://open.er-api.com/v6/latest/USD. This API contains live exchange rates for most of the currencies in the world. It uses USD as the base currency.
1 2 3 4 5 6 7 |
# import requests package import requests # Making a get request response = requests.get('https://open.er-api.com/v6/latest/USD') # print request object print(response.json()) |
Output (truncated):
{... 'time_last_update_unix': 1657152152, 'time_last_update_utc': 'Thu, 07 Jul 2022 00:02:32 +0000', 'time_next_update_unix': 1657240382, 'time_next_update_utc': 'Fri, 08 Jul 2022 00:33:02 +0000', 'time_eol_unix': 0, 'base_code': 'USD', 'rates': {'USD': 1, 'AED': 3.67, 'AFN': 87.55…}}
Basic Authentication (Example: Bitbucket API)
Basic authentication is the simplest authentication method. In this authentication method, you need to provide the username and password used to log in to the website providing the API.
These credentials are passed to the requests package through the headers. The header must start with the word “Basic” followed by username:password, which should be Base64 encoded. Based on the API usage guidelines, authentication may sometimes need a token instead of a login password.
The general syntax for implementing Basic Authentication using Python requests is given by:
1 2 3 4 |
import requests from requests.auth import HTTPBasicAuth requests.get(<API_address>, auth=HTTPBasicAuth(<user>, <password/token>)) |
Or, simply
1 2 3 |
import requests requests.get(<API_address>, auth=(<user>,<password/token>)) |
Example 1 – (Postman – using username and password)
For the purpose of demonstration, let’s use the postman API endpoint exposed by the site.
1 2 3 4 5 6 7 8 9 |
import requests url = "https://postman-echo.com/basic-auth" username = "postman" password = "password" response = requests.get(url, auth=(username, password)) print(response.status_code) print(response.json()) |
Output:
200 {'authenticated': True}
Example 2 (BitBucket – using a token)
Bitbucket, a code repo hosting site like GitHub, has an API that allows basic authentication. In their case, they need a token instead of a password and the username, not an email. The API token can be generated in the user profile under Manage Account > Security.
1 2 3 4 5 6 |
import requests from requests.auth import HTTPBasicAuth url = "https://api.bitbucket.org/2.0/repositories/kipronokoech" a = requests.get(url, auth=HTTPBasicAuth('kipronokoech', 'crbRc6Tqriq6De3683D')) print(a.json()) |
The output is information about my code repositories in Bitbucket. The content shown depends on the permissions granted to the handler of the API token set during its generation.
To make sure that the authentication was successful, the requests.get() function was able to fetch my private repositories.
Bearer/Token authentication (Example GitHub API)
This is a two-step authentication method that requires the user to not only possess the username-password login details but also be able to generate a token. Unlike the token-based verification described above (for Bitbucket), the token given in this authentication method expires after some time.
To pass your token to the API using requests, you should include it as a header called auth for Authorization. The value of this field should be in the form of Bearer {TOKEN} or Token {TOKEN}
Here is the general syntax of the request code when calling an API with token authentication.
1 2 3 |
import requests requests.get(<URL>, headers={'Authorization': 'Token/Bearer {ISSUED_TOKEN}'}) |
As an example, let’s call GitHub API using Bearer authentication. You can generate personal access token on GitHub using this link.
1 2 3 4 |
import requests r = requests.get('https://api.github.com/user', headers={'Authorization': 'Token Gsp_etrqRurrqSre9473289rv65BXdhafsdDB'}) print(r.status_code) |
Output:
200
Status 200 means that verification was successful and the data was fetched. r.text shows the retrieved data – my user information. Please note the space between the word “Token” and the actual token string.
If the wrong token is issued, as shown below, the request will exit with status 401, and the retrieved data shows that the token is invalid.
1 2 3 4 5 |
import requests r = requests.get('https://api.github.com/user', headers={'Authorization': 'Token Gsp_etrqR'}) print(r.status_code) print(r.text) |
Output:
401 {"message":"Bad credentials","documentation_url":"https://docs.github.com/rest"}
Digest Authentication (Example: Test site)
The only difference between this and the basic authentication method is that it does not transmit the username and password in plaintext.
In addition to the username, realm, password, and URI request, the server provides the client with a one-time-use number (a nonce). The client hashes each of those fields to create a hash key using the MD5 algorithm. It tries to authenticate by sending this hash key, the username, and the realm to the server.
In Python, Digest authentication can be implemented easily on the request module using the following code test:
1 2 3 4 5 6 7 8 9 |
import requests from requests.auth import HTTPDigestAuth url = 'https://httpbin.org/digest-auth/auth/kiprono/pass12' a = requests.get(url, auth=HTTPDigestAuth('user', 'pass1')) print(a) b = requests.get(url, auth=HTTPDigestAuth('user', 'pass12')) print(b) |
Output:
<Response [401]> <Response [200]>
This is an experimental test site. We sent two requests; the first failed because the site was set to accept “pass12” as the passcode, not “pass1”.
OAuth 1 Authentication (Example: Twitter API)
This authentication method is the most commonly used for web APIs. Users of the Python Requests package can quickly submit OAuth 1 authorized requests thanks to the requests-oauthlib library (if it is not installed on your machine you can install it using pip or conda).
Here is an example usage of the OAuth1 authentication method to access Twitter API (generate keys here). The general syntax is:
1 2 3 4 5 6 7 8 |
import requests from requests_oauthlib import OAuth1 url = 'https://api.twitter.com/1.1/account/verify_credentials.json' auth = OAuth1('YOUR_APP_KEY', 'YOUR_APP_SECRET', 'USER_OAUTH_TOKEN', 'USER_OAUTH_TOKEN_SECRET') requests.get(url, auth=auth) |
Conclusion
When accessing any API, make sure that you read its usage guidelines. The authentication methods discussed here are neither exhaustive nor are the formats of headers. Some APIs provide custom headers that the requests module can still use.