LAB 2 — Intro to HTTP & APIs with Python
Readings and Videos to Prepare
These readings and videos will introduce you to important concepts and details which will help you complete this lab activity:
| You do not need to follow-along or complete any activities from these resources but you should watch/read them to prepare for the remainder of this lab. |
Learning Objectives
By the end of this lab, you will:
-
Understand HTTP requests, status codes, and responses
-
Use Python’s
requestslibrary to call REST APIs -
Parse JSON responses into Python dictionaries
-
Understand YAML structure and how it maps to Python data types
-
Read and write YAML files using Python
-
Write functions that interact with APIs
-
Use HTTP POST requests to send data to APIs
-
Handle basic errors from API responses
-
Save API data to files for later analysis
Purpose
This lab bridges Python Essentials 1 knowledge (functions, dictionaries, I/O, modules) to real-world API use. Students use Python’s requests library to make HTTP GET and POST requests, parse JSON into dictionaries, handle errors, and write results to files.
Why This Lab Matters
Everything in later labs—RESTCONF, NETCONF, API-driven automation—assumes you understand:
-
What an HTTP request is
-
How to talk to a server over the network
-
What JSON is and how to parse it
-
How to handle responses in Python
This lab builds that foundation in a simple, safe environment using public test APIs before we touch actual network equipment.
Prerequisites
You should be comfortable with:
-
Basic Python syntax (variables, data types, loops, conditionals)
-
Functions and return values
-
Dictionaries and how to access their values
-
Basic file I/O
You will need:
-
Python 3.7 or later
-
The
requestslibrary (install with:pip install requests) -
The
PyYAMLlibrary (install with:pip install pyyaml)
Background: REST APIs, HTTP, JSON, and YAML
REST APIs and YAML are two of the most common building blocks in modern automation. Nearly every cloud platform, SaaS product, and network device exposes a REST API, and many automation tools use YAML for configuration and inventory. Learning these formats now makes every later lab easier and mirrors real-world workflows.
Before beginning the lab, understand these concepts:
- HTTP (Hypertext Transfer Protocol)
-
The protocol used to send requests and receive responses over the internet. Common HTTP methods are:
-
GET: Retrieve data from a server -
POST: Send data to a server to create a resource -
PUT/PATCH: Modify existing resources -
DELETE: Remove a resource
-
- Status Codes
-
Numbers in the HTTP response indicating success or failure:
-
200-299: Success (200 = OK, 201 = Created) -
400-499: Client error (404 = Not Found, 401 = Unauthorized) -
500-599: Server error
-
- REST API
-
A web service that uses HTTP methods to allow programs to request and send data. APIs communicate using endpoints (URLs) and return structured data. REST is popular because it is simple, language-agnostic, and works over standard HTTP, so almost any programming language or tool can call it.
In general programming, REST APIs are how applications integrate with services like maps, payments, messaging, and authentication. In systems and network administration, REST APIs power:
-
Cloud management (AWS, Azure, Google Cloud)
-
Monitoring platforms (metrics, alerts, dashboards)
-
Network device configuration (RESTCONF, vendor APIs)
-
Infrastructure-as-code pipelines and automation tools
-
- JSON (JavaScript Object Notation)
-
A text format for structured data. Similar to Python dictionaries. JSON is the most common data format returned by REST APIs because it is easy for machines to parse and easy for humans to read:
{ "name": "John", "age": 30, "courses": ["ITC 2310", "ITC 2300"] }
In Python, requests.json() converts JSON strings into dictionaries you can work with. This lets you write code that reads and updates specific fields, not just raw text.
Structured vs. Unstructured Data (Why APIs Matter): In Lab 1, you used SSH/Netmiko and parsed CLI output meant for humans. That output is unstructured text, which means your code must guess where each field starts and ends. In contrast, REST APIs return structured data (JSON), where fields are clearly labeled and consistent.
Structured data advantages:
-
Reliable parsing:
data["interface"]["name"]is safer than splitting a line of text by spaces -
Resilient to formatting changes: API fields stay consistent across software updates
-
Cleaner automation logic: Code focuses on data, not text formatting
-
Easier validation: You can check exact fields before/after changes
This is a key reason modern automation prefers APIs for configuration tasks, while CLI automation remains useful for operational commands that have no API equivalents.
- YAML (YAML Ain’t Markup Language)
-
A human-friendly text format for structured data. YAML is popular because it is compact, supports comments, and is easy to edit by hand. YAML maps cleanly to Python dictionaries and lists:
name: John age: 30 courses: - ITC 2310 - ITC 2300
In Python, the yaml module reads YAML into dictionaries/lists and can write dictionaries/lists back to YAML. In systems and network automation, YAML is widely used for:
-
Inventory files (devices, IPs, roles, credentials)
-
Configuration variables for templates
-
Pipeline definitions (CI/CD workflows)
-
Tool configuration files (Ansible, Salt, Kubernetes)
Because YAML and REST APIs are so common, most real automation scripts combine both: YAML for local configuration and REST APIs for remote device/service interaction.
Section 1 — First API GET Request
Create api_get_basic.py:
# Import the requests library - this allows us to make HTTP requests to web servers
import requests
# Import the json library - this helps us format JSON data nicely for display
import json
# Store the URL of the API endpoint we want to request. This is a public test API.
url = "https://jsonplaceholder.typicode.com/posts/1"
# Make an HTTP GET request to the URL and store the response object returned by the server
response = requests.get(url)
# Display the HTTP status code (200 means the request was successful)
print("Status Code:", response.status_code)
# Display the raw JSON response as a text string (before we parse it into Python objects)
print("Raw Response:", response.text)
# Parse the JSON text from the response into a Python dictionary we can work with
data = response.json()
# Access specific fields from the dictionary and print them. data["title"] gets the title field.
print("Parsed Title:", data["title"])
# Access the body field from the dictionary
print("Parsed Body:", data["body"])
# Convert the entire dictionary back to JSON and print it with nice formatting (2-space indentation)
print(json.dumps(data, indent=2))
What Happens Here
When you run this script:
-
Python imports the
requestsmodule (your HTTP client) -
You specify a URL pointing to a public test API (jsonplaceholder.typicode.com)
-
requests.get(url)sends an HTTP GET request -
The server responds with HTTP status 200 (success) and JSON data
-
response.json()converts the JSON string into a Python dictionary -
You access dictionary keys like
data["title"]just like you learned in Python Essentials
This mirrors how RESTCONF will return JSON from Cisco devices in later labs.
Section 2 — Wrap API Requests in Functions
Create api_jokes.py:
# Import the requests library so we can make HTTP requests
import requests
# Define a function that fetches a random Chuck Norris joke from an API
def get_joke():
# Store the URL of the API endpoint that returns random jokes
url = "https://api.chucknorris.io/jokes/random"
# Make a GET request to the API with a timeout of 10 seconds (prevents hanging forever)
r = requests.get(url, timeout=10)
# Check if the request was successful (status code 200 means OK)
if r.status_code == 200:
# Parse the JSON response into a Python dictionary
joke = r.json()
# Return just the joke text from the "value" key in the dictionary
return joke["value"]
else:
# If the request failed, return an error message instead of trying to parse JSON
return f"Error: status={r.status_code}"
# Loop 3 times (i takes values 0, 1, 2)
for i in range(3):
# Call get_joke() three times and print each joke returned
print(get_joke())
Why Use Functions?
Wrapping API calls in functions makes your code:
-
Reusable: Call
get_joke()as many times as you want -
Maintainable: If the API changes, fix it in one place
-
Testable: You can test your function separately
-
Readable: Your main code says what it does (
get_joke()) not how
Error Handling for Beginners
Notice the if r.status_code == 200: check:
-
If the server returns status code 200 (success), process the JSON
-
Otherwise, return an error message instead of crashing
-
This prevents your program from breaking if the API is down or unavailable
-
The
timeout=10parameter tells requests to wait max 10 seconds—prevents hanging forever if the server doesn’t respond
Section 3 — YAML Structure and Python Usage
In this section, you’ll learn YAML fundamentals and how to parse and create YAML using Python. YAML is common in network automation workflows because it’s readable and works well for configuration files.
3.1 Read YAML into Python
Create yaml_intro.py:
import yaml
# A sample YAML string (usually you'd read this from a file)
yaml_text = """
device:
hostname: R1
mgmt_ip: 10.10.20.10
roles:
- router
- edge
"""
# Load YAML into a Python dictionary
data = yaml.safe_load(yaml_text)
print(type(data))
print(data)
print("Hostname:", data["device"]["hostname"])
print("First role:", data["device"]["roles"][0])
3.2 Write Python Data to YAML
Create yaml_write_example.py:
import yaml
inventory = {
"devices": [
{"name": "R1", "mgmt_ip": "10.10.20.10", "role": "router"},
{"name": "SW1", "mgmt_ip": "10.10.20.30", "role": "switch"}
]
}
with open("inventory.yaml", "w", encoding="utf-8") as f:
yaml.safe_dump(inventory, f, sort_keys=False)
print("Wrote inventory.yaml")
3.3 Convert API JSON to YAML
Create api_to_yaml.py:
import requests
import yaml
url = "https://jsonplaceholder.typicode.com/posts/1"
response = requests.get(url, timeout=10)
data = response.json()
with open("post_1.yaml", "w", encoding="utf-8") as f:
yaml.safe_dump(data, f, sort_keys=False)
print("Saved API response to post_1.yaml")
3.4 Use YAML as Input to an API Call
Create api_from_yaml.py and an input file post_payload.yaml:
title: ITC 2310 YAML Post
body: Created from a YAML file
userId: 1
import requests
import yaml
with open("post_payload.yaml", "r", encoding="utf-8") as f:
payload = yaml.safe_load(f)
url = "https://jsonplaceholder.typicode.com/posts"
headers = {"Content-Type": "application/json"}
resp = requests.post(url, json=payload, headers=headers)
print("Status:", resp.status_code)
print("Response JSON:", resp.json())
Section 4 — Writing API Data to a File
Modify api_jokes.py to log jokes to a file:
# Open a file named jokes_output.txt in write mode ("w" creates it or overwrites if it exists)
# encoding="utf-8" ensures special characters are saved correctly
# The 'with' statement automatically closes the file when done, even if an error occurs
with open("jokes_output.txt", "w", encoding="utf-8") as f:
# Loop 5 times to fetch and save 5 jokes
for i in range(5):
# Call get_joke() to fetch a joke, concatenate a newline character (\n), and write to file
f.write(get_joke() + "\n")
This pattern appears repeatedly in automation:
-
Retrieve data from an API (or network device)
-
Process or format the data
-
Write results to a log file for auditing and troubleshooting
You now have a permanent record of the jokes retrieved. In network automation, you’ll log configuration changes, interface statistics, and device inventories the same way.
Section 5 — POST Request (Sending Data to an API)
Create api_post_example.py:
# Import the requests library for making HTTP requests
import requests
# Store the URL endpoint where we want to create a new post
url = "https://jsonplaceholder.typicode.com/posts"
# Create a dictionary containing the data for the new post
payload = {
"title": "ITC 2310 Sample", # The title of the new post
"body": "Hello world!", # The body/content of the new post
"userId": 1 # The ID of the user creating the post
}
# Create a headers dictionary that tells the server we're sending JSON data
headers = {"Content-Type": "application/json"}
# Make an HTTP POST request to create a new resource on the server
# json=payload automatically converts the dictionary to JSON and sends it
# headers=headers sends our Content-Type header
resp = requests.post(url, json=payload, headers=headers)
# Print the HTTP status code (201 means Created, indicating success)
print("Status:", resp.status_code)
# Parse the JSON response and print it (the server typically echoes back the data it created)
print("Response JSON:", resp.json())
GET vs. POST: The Difference
-
GET: Request (retrieve) data from a server. Like asking "Show me the weather."
-
POST: Send data to a server to create something new. Like saying "Create a new forum post with this text."
In this example:
-
payloadis a Python dictionary containing the data you want to send -
json=payloadtells requests to convert the dictionary to JSON and send it -
headers={"Content-Type": "application/json"}tells the server "I’m sending JSON data" -
The server responds with status 201 (created) and returns the new resource’s data
jsonplaceholder.typicode.com is a fake API—it doesn’t actually store your data, but it behaves like a real one for learning.
Common Errors & Troubleshooting
| Error | Solution |
|---|---|
|
Install it: |
|
Install it: |
|
API server is down or you have no internet. Check your connection and try a different public API if the first is unreachable. |
|
The API didn’t return that key. Print the whole |
|
The response isn’t valid JSON. Check |
|
You’re trying to access |
YAML parse error |
YAML is indentation-sensitive. Use spaces (not tabs) and verify consistent indentation. |
Reflection Questions
After completing this lab activity you should be able to answer these questions:
-
Describe the HTTP request/response cycle: When you call
requests.get(url), what travels from your computer to the server and back? -
Status codes matter: Why is it important to check
response.status_codebefore trying to parse the JSON? What could go wrong if you skip this check? -
JSON vs. Python: After you call
response.json(), what Python data type are you working with? Why is this useful? (Hint: It lets you usedata["key"]access.) -
Error handling: In
api_jokes.py, why do we usetimeout=10in therequests.get()call? What problem does it solve? -
Real-world application: When you automate network devices with RESTCONF in later labs, what role will understanding HTTP methods (GET, POST) and JSON parsing play?
-
Challenge question: What would change in your code if an API required sending authentication (username/password)? (Hint: Headers!)
-
YAML vs. JSON: Compare YAML and JSON in terms of readability and common automation use cases. Which is easier to read? Which is more common in APIs?
-
YAML in automation: Why do automation tools often use YAML files for inventory and configuration?
Optional Challenge Exercises
Try these if you finish early or want more practice:
-
Parse nested JSON: The jsonplaceholder API includes
commentsendpoints. Fetch comments and parse out nested fields (e.g.,comment["body"]["nested_field"]). -
Add logging: Modify your scripts to write detailed logs including timestamps, status codes, and raw responses to a log file using Python’s
loggingmodule. -
Error recovery: Update
api_jokes.pyto retry a failed request up to 3 times before giving up. -
Multiple endpoints: Write a script that fetches data from multiple different public APIs in a loop and combines the results into a summary file.
-
Input validation: Modify
api_post_example.pyto prompt the user for input, validate it (e.g., title isn’t empty), and then POST it. -
YAML inventory: Create a
devices.yamlfile with at least 4 devices (name, IP, role). Write a script that reads the file and prints a formatted inventory report.