A REST API (REpresentational State Transfer API) is a set of functions that can be accessed over the internet. The functions are organized in a way that they can be accessed using the common protocol of the Web, HTTP (Hypertext-Transport Protocol).
By design, REST APIs are stateless, meaning:
calls can be made independently of one another,
each call contains all of the data needed to complete itself successfully, and
no one call depends on the next.
REST APIs are designed around resources, which consists of a URI (usually a URL), and an HTTP request method.
The methods are:
GET: retrieve a resource
POST: create a new resource
PUT: update a resource
DELETE: remove a resource
PATCH: update a resource with partial data
HTTP without a rest API Example
Let’s start with an example of invoking a simple HTTP request without using a REST API.
Let’s retrieve the content of the course website programmatically.
Notice the response is HTML content. This is a markup language used to create web pages and is intended for humans.
import requestsuri ="https://su-ist356-m003-fall-2025.github.io/course-home/"response = requests.get(uri)# Note: we're just printing the first 1000 characters here to avoid# overwhelming outputprint(response.text[:1000])
This example will use the funny names API to demonstrate how to retrieve data from a REST API. Note that the code is exactly the same as the previous example, but the URL is different.
uri ="https://cent.ischool-iot.net/api/funnyname/random"response = requests.get(uri)print(response.text)
[{"first": "Clay", "last": "Potts"}]
Parsing the JSON response
You can see from the example above, the response is in JSON format. Recall that JSON is a lightweight data-interchange format that is easy for humans to read and write and easy for machines to parse and generate.
Since REST API’s are for machines, it makes sense to use the JSON format. We can deserialize the JSON response into a Python dictionary or list using the json() method on requests.
uri ="https://cent.ischool-iot.net/api/funnyname/random"response = requests.get(uri)funny_person = response.json()print(funny_person) # list of dictprint(funny_person[0]['first'], funny_person[0]['last'])
[{'first': 'May', 'last': 'Flies'}]
May Flies
Response Codes for Handling Errors
When the server returns a response to a request, included in the response is the HTTP status code, which tells the client whether or not the request worked. Unlike a regular API / function call such as print() this is necessary because there is a lot that can go wrong when you call a function over the open internet. Status codes are 3 digit numbers and the first number indicates the type of response:
1xx - codes are informational. These are seldom used in web APIs.
2xx - codes which begin with a 2 indicate success. The most common code is 200 - OK.
3xx - codes which begin with a 3 indicate redirection - the response is not comming from the request URL you requested. For -example a 304 - Not modified means your response is coming from the browser’s cache (content already downloaded).
4xx - codes which begin with a 4 indicate a client error. The most common code here is 404 - Not Found. Any 4xx errors mean the requestor did something wrong. (In this case, that’s you!)
5xx - codes which begin with a 5 indicate a server error. The most common code here is 500 - Internal server error, which indicates the server could not process the request. When this happens it could be the web API’s problem or the way you made the request.
We handle errors using the raise_for_status() method on the response object. This method will raise an exception if the response is any status code other than 2xx. It’s good to raise an exception here because it will stop the program from continuing and potentially causing more problems.
# This intentionally fails with 404 - not founduri ="https://cent.ischool-iot.net/api/funnynamez/random"response = requests.get(uri)response.raise_for_status()# none of this code is relevant if the status is not 2xxfunny_person = response.json()print(funny_person) # list of dictprint(funny_person[0]['first'], funny_person[0]['last'])
---------------------------------------------------------------------------HTTPError Traceback (most recent call last)
CellIn[4], line 4 2 uri = "https://cent.ischool-iot.net/api/funnynamez/random" 3 response = requests.get(uri)
----> 4response.raise_for_status() 5# none of this code is relevant if the status is not 2xx 6 funny_person = response.json()
File /opt/hostedtoolcache/Python/3.13.12/x64/lib/python3.13/site-packages/requests/models.py:1026, in Response.raise_for_status(self) 1021 http_error_msg = (
1022f"{self.status_code} Server Error: {reason} for url: {self.url}" 1023 )
1025if http_error_msg:
-> 1026raise HTTPError(http_error_msg, response=self)
HTTPError: 404 Client Error: NOT FOUND for url: https://cent.ischool-iot.net/api/funnynamez/random
Algorithm for calling any REST API in Python
Prepare the request URI
headers
query parameters
body
Make the request with URI and appropriate method
Check the response status code with rise_for_status()
Deserialize the response into a Python object
The process is always the same, only the way the requrest is prepares and your handling of the response content will change.
Examples
In these examples we use the JSONPlaceholder API, which is a mock API for fake data. It’s mostly meant to test out API calls.
Example 1:
In this example, we get a single user’s street address.
The Query String is a part of the URL that is used to pass data to the server on the URL. It is appended to the end of the URL and begins with a question mark “?”. The query string is made up of a series of key-value pairs separated by an ampersand (&).
In the Python requests library, you can pass the query string as a dictionary under the params named argument.
To illustrate, here’s an example where we use the arXiv’s REST API to retrieve information about the paper Attention Is All You Need. In the first example, we put the query information directly into the URL:
The Syracuse University Center for Emerging Network Technologies (CENT) created an Internet of Things (IoT) portal. The portal makes REST APIs available to IoT devices which commonly do not have the computing powert to perform these tasks, and to students so they don’t have to pay for a cloud service to use REST APIs in their projects.
Then sign in with your SU NetID and password. When you sign in, you will see a box with “Your API Key”. This key is unique to you. You can use this to do API requests, as illustrated below.
This allows you to test the API’s in the browser. Swagger is a tool that helps you design, build, document, and consume REST APIs. The swagger interface shows how the API is called with a curl command, which allows you to make the same request from the command line.
Now on the Swagger page click the “Authorize” button in the upper right. You will be prompted for your API key. Paste the API key you copied.
You should now be able to use any of the APIs listed on the page. To try it, click one of the drop downs, then Execute. For example, click the “google” drop down, then the one for /api/google/geocode. This API provides latitude and longitude for a given location. Hit the big Execute button. Type in a location in the “location” box (e.g., “Syracuse University”) then hit the Execute button (now smaller). In the Response body, you should see a JSON string that gives information about the location, including the latitude and longitude.
Example
Let’s use swagger to call the funny names API for 10 random names, then translate the curl command into Python code.
# Translate the following code to use requests# curl -X 'GET' \# 'https://cent.ischool-iot.net/api/funnyname/random?n=10' \# -H 'accept: application/json'uri ="https://cent.ischool-iot.net/api/funnyname/random"params = {'n': 10}response = requests.get(uri, params=params)response.raise_for_status()funny_people = response.json()for person in funny_people:print(person['first'], person['last'])
Elieen Onyewe
Leiv Amess
Sara Docktur-Indahaus
Carrie Meehom
Bo Enarreau
Ida Knowe
Sara Bellum
Pete Terpan
Joanne Bill
Sal Boat
HTTP Headers
HTTP headers are the key / value pairs that are sent in the request or response. They are used to pass additional information about the request or response. Unlike the query string, they are not part of the URL and are not visible to the user.
In the IoT portal, the headers are used to pass the API Key which verifies who you are.
Example:
Use the random API to get 10 intergers between 1 and 100.
# Note: replace apikey with your API from the IoT portalapikey ="GETYOUROWNKEYFROMIOTPORTAL"uri ="https://cent.ischool-iot.net/api/random/int?"params = { 'count': 10, 'm"in': 1, 'max': 100 }headers = { "X-API-KEY": apikey} # goes in the headerresponse.raise_for_status()print(response.url) # see the full URL no API key therenumbers = response.json()print(numbers)
CautionCode Challenge 5.1.2
Figure out how to call these in the IoT portal:
Google geocode API to take a location and get a latitute and longitude
Weather API to get the weather for a latitude and longitude
Write a Streamlit app to input a location (e.g., “Syracuse University”) and return the current weather conditions. Use the st.metric to display the temperature and humidity with units. e.g. 56°F and 80% humidity.