REST APIs, requests.get, tokens, query parameters & JSON
Ask the inventory API
Lesson 16 of 20 · 14 min · Module 5: APIs and the source of truth
Large networks keep their inventory in a source of truth such as NetBox: one place that says which devices exist, where they are and how to reach them. Scripts ask it over a REST API instead of reading a spreadsheet.
Start this lesson →Free. No installation: you write and run the code in your browser, and it is checked automatically.
By the end, you’ll be able to…
- Call a REST API with requests
- Authenticate with a token header
- Read nested JSON fields
The idea
requests.get(url, headers=..., params=...) sends an HTTP GET. NetBox expects the header Authorization: Token <token>; without it the answer is 403 Forbidden. params={"site": "bucharest"} becomes ?site=bucharest and filters on the server. response.raise_for_status() raises an error for 4xx and 5xx answers, and response.json() returns the body as Python data: a list endpoint answers with count, next, previous and results. The practice API at https://inventory.lab works like NetBox; its address and a token are in INVENTORY_URL and INVENTORY_TOKEN.
import os
import requests
URL = os.environ["INVENTORY_URL"]
HEADERS = {"Authorization": f"Token {os.environ['INVENTORY_TOKEN']}",
"Accept": "application/json"}
response = requests.get(f"{URL}/api/dcim/sites/", headers=HEADERS)
response.raise_for_status()
print([site["name"] for site in response.json()["results"]])
01No token, no data
The API answers, but refuses. The status code and the JSON body say why.
import os
import requests
URL = os.environ["INVENTORY_URL"]
response = requests.get(f"{URL}/api/dcim/devices/")
print(response.status_code)
print(response.json())403
{'detail': 'Authentication credentials were not provided.'}02With the token
count is the number of matching devices; results holds one page of them, and next is the link to the following page.
HEADERS = {"Authorization": f"Token {os.environ['INVENTORY_TOKEN']}",
"Accept": "application/json"}
data = requests.get(f"{URL}/api/dcim/devices/", headers=HEADERS).json()
print(data["count"], len(data["results"]))
print(data["next"])6 5
https://inventory.lab/api/dcim/devices/?limit=5&offset=503Nested fields
Related objects come as small dictionaries. Each step of the path is one more key.
device = data["results"][0]
print(device["name"], device["site"]["slug"], device["platform"]["slug"])
print(device["primary_ip4"]["address"])
print(device["status"])core1 bucharest cisco_ios
10.0.0.1/24
{'value': 'active', 'label': 'Active'}Your turn
/api/dcim/devices/ for the devices in the Bucharest site (filter with site=bucharest). Build bucharest: a list of (name, address) tuples in the order the API returns them, where the address is the primary IPv4 address without the prefix length (10.0.0.1, not 10.0.0.1/24). Read the URL and the token from the environment.What the checks look for
- bucharest lists the four Bucharest devices
- Sent the token and got 200 OK
- Filtered on the server with site=bucharest
- The token is not written in the code
Take this with you
Ask the source of truth: GET with a token, check the status, read the JSON.
Go a little further
Also ask for role=access, and print the names that appear in both answers.