Understanding and Using APIs · Topic 7 of 7
Calling a REST API with the Requests Library
Write a Python script that calls a REST API with the requests library, using get, post, headers, params, json, timeouts, status checks and a Session.
- 4 min
- The function name matches the method:
get,post,put,patch,delete. headersandparamstake dictionaries, andparamsbuilds the query string.json=sends a dictionary as JSON, andresponse.json()parses a JSON reply.
What it is
The requests library is the standard third party Python package for sending HTTP requests.
It turns the parts of a request into function arguments and the parts of a response into attributes. A few lines of code replace a lot of low level work.
You install it with pip install requests, then import it in your script.
How it works
You call a function named after the method, then read the Response object that it returns.
The basic functions
| Function | HTTP method |
|---|---|
requests.get() | GET |
requests.post() | POST |
requests.put() | PUT |
requests.patch() | PATCH |
requests.delete() | DELETE |
Useful arguments
| Argument | Purpose |
|---|---|
headers | A dictionary of request headers |
params | A dictionary that becomes the query string |
json | A dictionary sent as a JSON body, with Content-Type set for you |
data | A form encoded or raw body |
auth | A tuple of username and password for basic authentication |
timeout | Seconds to wait before giving up |
verify | Whether to check the server certificate. It defaults to True |
The Response object
| Attribute or method | Meaning |
|---|---|
status_code | The numeric status, such as 200 |
ok | True when the status is below 400 |
headers | The response headers, as a dictionary like object |
text | The body as a string |
json() | The body parsed as JSON |
raise_for_status() | Raises an exception for a 4xx or 5xx code |
A complete script
This script lists devices from a fictional API, with an API key held in an environment variable.
import os
import requests
BASE_URL = os.environ["API_BASE"]
API_KEY = os.environ["API_KEY"]
headers = {
"Authorization": "Bearer " + API_KEY,
"Accept": "application/json",
}
params = {"limit": 20}
try:
response = requests.get(
BASE_URL + "/v1/devices",
headers=headers,
params=params,
timeout=10,
)
response.raise_for_status()
except requests.exceptions.RequestException as err:
print("Request failed:", err)
else:
for device in response.json()["items"]:
print(device["name"], device["status"])
The try block catches network problems and bad status codes. The else block runs only when the call worked.
Creating a resource with POST
payload = {"name": "edge-sw1", "site": "London"}
response = requests.post(
BASE_URL + "/v1/devices",
headers=headers,
json=payload,
timeout=10,
)
print(response.status_code)
print(response.json())
Because json=payload is used, there is no need to call json.dumps() yourself.
Updating and deleting
requests.put(BASE_URL + "/v1/devices/42", headers=headers, json=payload)
requests.delete(BASE_URL + "/v1/devices/42", headers=headers)
A successful DELETE often returns 204 with no body, so do not call .json() on it.
Sessions
A requests.Session object keeps settings and the connection across calls. This saves repeating the same headers.
session = requests.Session()
session.headers.update(headers)
devices = session.get(BASE_URL + "/v1/devices", timeout=10)
sites = session.get(BASE_URL + "/v1/sites", timeout=10)
Certificates and the verify argument
By default, requests checks the server certificate. Lab devices often use self signed certificates, which makes the call fail.
Setting verify=False turns the check off and works in a lab. It removes protection against impostors, so avoid it in production. A better fix is to point verify at the right certificate file.
Common error types
requests.exceptions.ConnectionErrorwhen the server cannot be reached.requests.exceptions.Timeoutwhen the server did not answer in time.requests.exceptions.HTTPErrorwhenraise_for_status()sees a 4xx or 5xx code.
Always set a timeout. Without one, a call to an unresponsive server can wait for a very long time.
What to remember for the exam
Seven facts cover this topic.
- The function name matches the method:
get,post,put,patch,delete. headersandparamstake dictionaries, andparamsbuilds the query string.json=sends a dictionary as JSON, andresponse.json()parses a JSON reply.status_codeholds the code andraise_for_status()raises on 4xx and 5xx.auth=(user, password)gives basic authentication.timeoutstops a call waiting forever.- A
Sessionreuses headers and the connection across calls.
Common mistakes
Four mistakes are typical.
Calling .json() on an empty body. A 204 response has no body, so parsing it raises an error.
Forgetting the timeout. The script may hang.
Using data= when the API wants JSON. Use json= so that the body and Content-Type are correct.
Leaving verify=False in production code. It disables certificate checking.
A response with status 404 or 500 does not raise an exception by itself. Check status_code or call raise_for_status().
Check yourself
Work through these three questions.
Which argument sends a Python dictionary as a JSON body?
The
jsonargument, for examplerequests.post(url, json=payload).How can you make a script stop on a 4xx or 5xx reply?
Call
response.raise_for_status(), which raises an exception for those codes.Why should every call have a timeout?
Without a timeout, a call to an unresponsive server can wait for a very long time.
Topic 7 of 7 in Understanding and Using APIs
Test yourself on Understanding and Using APIs
The full Cisco CCNA 200-901 DEVASC question bank has 808 questions, with timed practice exams and an explanation for every answer.
This study guide is independent and is not affiliated with, endorsed by, or sponsored by Cisco. Vendor names, certification names and exam codes are trademarks of their respective owners and are used for identification only.