Photo by Fotis Fotopoulos on Unsplash
API Basics¶
Overview¶
This notebook will cover the terminology and steps needed to retrieve data from an API
- Prerequisites
- Motivation
- API Terminology (and Status Codes)
- Imports
- Example: Weather API
- Summary
Prerequisites¶
Concepts | Importance | Notes |
---|---|---|
Intro to Pandas | Necessary | Familiarity with working with dataframes |
Intro to Matplotlib | Helpful | Plotting on a data |
Motivation¶
There are many ways to gather data. Science and research entities like NASA are constantly producing and collecting data. As a result, attempting to collect and display live data can be difficult since new data is always being added. An API is a method to query a data source for the most current data or retrieve data from a remote source. This notebook will cover the basics regarding API usage to improve ease of access to publicly available data.
API Terminology¶
- API: Application Programming Interface which dictacts how code can communicate and access or update remote data through methods
- Request: Code sends
requests
to an API to either retrieve or update data - GET: A
GET
request retrieves data - SET: A
SET
request updates data
When working with public APIs, most methods will request data from an API (a GET
request)
Understanding Status Codes¶
There are multiple possible status codes that a request will return. For the purpose of simplicity, the two most important codes are:
- 200 OK: The server was able to successfully process the request and return the requested data
- 400 Bad Request: The server was not able to process the request do to an invalid request (usally the result of an invalid URL or unknown parameters)
Imports¶
import requests # access API
import matplotlib.pyplot as plt
Requests¶
The Requests Python package is a library that manages the requests made in Python
A request returns machine-readable data in a JSON. Among the data, requests returns:
- request.status_code: status code of the request (200, 400, etc...)
- request.text: data requested as a JSON
- request.json(): fully JSON returned by the request
Example: Weather API¶
The National Weather Serivce manages an API for weather in the United States. The API is hosted at https://api.weather.gov/
The first step when working with an API should be to check that the API is fuctioning by querying a general request without any additional parameters.
weather_request = requests.get("https://api.weather.gov/")
weather_request.status_code
200
Info
A 200 status code represents that the base API is working as expected and a query request returns no errors.
However, without any additional parameters, all the data that the request returns is just the status code.
# All Data
weather_request.json()
---------------------------------------------------------------------------
JSONDecodeError Traceback (most recent call last)
File ~/micromamba/envs/api-cookbook-dev/lib/python3.13/site-packages/requests/models.py:976, in Response.json(self, **kwargs)
975 try:
--> 976 return complexjson.loads(self.text, **kwargs)
977 except JSONDecodeError as e:
978 # Catch JSON-related errors and raise as requests.JSONDecodeError
979 # This aliases json.JSONDecodeError and simplejson.JSONDecodeError
File ~/micromamba/envs/api-cookbook-dev/lib/python3.13/json/__init__.py:346, in loads(s, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw)
343 if (cls is None and object_hook is None and
344 parse_int is None and parse_float is None and
345 parse_constant is None and object_pairs_hook is None and not kw):
--> 346 return _default_decoder.decode(s)
347 if cls is None:
File ~/micromamba/envs/api-cookbook-dev/lib/python3.13/json/decoder.py:345, in JSONDecoder.decode(self, s, _w)
341 """Return the Python representation of ``s`` (a ``str`` instance
342 containing a JSON document).
343
344 """
--> 345 obj, end = self.raw_decode(s, idx=_w(s, 0).end())
346 end = _w(s, end).end()
File ~/micromamba/envs/api-cookbook-dev/lib/python3.13/json/decoder.py:363, in JSONDecoder.raw_decode(self, s, idx)
362 except StopIteration as err:
--> 363 raise JSONDecodeError("Expecting value", s, err.value) from None
364 return obj, end
JSONDecodeError: Expecting value: line 1 column 1 (char 0)
During handling of the above exception, another exception occurred:
JSONDecodeError Traceback (most recent call last)
Cell In[3], line 2
1 # All Data
----> 2 weather_request.json()
File ~/micromamba/envs/api-cookbook-dev/lib/python3.13/site-packages/requests/models.py:980, in Response.json(self, **kwargs)
976 return complexjson.loads(self.text, **kwargs)
977 except JSONDecodeError as e:
978 # Catch JSON-related errors and raise as requests.JSONDecodeError
979 # This aliases json.JSONDecodeError and simplejson.JSONDecodeError
--> 980 raise RequestsJSONDecodeError(e.msg, e.doc, e.pos)
JSONDecodeError: Expecting value: line 1 column 1 (char 0)
The next step is to query with specific paramters. For the weather API the accepts either a grid around a NWS Weather Forecast Office or a specific latitude/longtiude position
The request will be formatted as:
https://api.weather.gov/points/<latitude>,<longitude>
More information about the documentation can be found at NWS Weather API.
For example, the location of the NCAR Mesa Lab is 39.97777
degrees latitude and -105.274966
degrees longitude
ncar_weather = requests.get("https://api.weather.gov/points/39.97777,-105.274966")
# Check request returned a valid response
ncar_weather.status_code
Success
With a valid request and paramters, this request will return data as well!
ncar_weather.json()
With the latitude and longtiude of a position, the API will return information about the closest NWS forecast office that can be further queried to return the weather. A JSON acts like a Python dictionary; to return the values stored, json()
can be queried for a specific key.
# JSON as Dictionary
print(type(ncar_weather.json()))
for key, value in ncar_weather.json().items():
print(f"\nkey: {key}")
print(f"value: {value}")
The closest forecast office from the NCAR Mesa Lab is forecastOffice
ncar_weather.json()["properties"]["forecastOffice"]
The query also return the hourly forecast as a further URL to query as a request under forecastHourly
ncar_forecast_url = ncar_weather.json()["properties"]["forecastHourly"]
ncar_forecast_url
ncar_forecast_hourly = requests.get(ncar_forecast_url)
ncar_forecast_hourly.status_code
Note
There is a lot more data returned from this request! The forecast information can be collected under properties and period. Each period of time has various weather values to chose from:
ncar_forecast_hourly.json()["properties"]["periods"][0].keys()
To plot, let’s collect the startTime
, endTime
and temperature
(° F) values
datetime_start = ncar_forecast_hourly.json()["properties"]["periods"][0]["startTime"]
datetime_end = ncar_forecast_hourly.json()["properties"]["periods"][-1]["endTime"]
print(datetime_start)
print(datetime_end)
# Temperatures every hour
hour_x = []
temperature = []
for period in ncar_forecast_hourly.json()["properties"]["periods"]:
hour_x.append(period["startTime"])
temperature.append(period["temperature"]) # collection of temperatures
print(temperature)
# Plot
fig, ax = plt.subplots(figsize=(22, 10))
# Plot Hourly Temperature
plt.bar(hour_x, temperature, color="dodgerblue")
# Setup Axis Limits and Title/Labels
plt.title(f"Hourly Tempearture at NCAR Mesa Lab for the Next {len(temperature)/24} Days")
plt.xlabel("Datetime")
plt.xticks(rotation=90, fontsize=8)
plt.ylabel("Temperature (\u00B0F)")
plt.show()
Success
We have ploted the forecasted hourly temperatures for next week!
Summary¶
In this notebook, we have...
- covered basics about API terminology
- gone through a practice example of requesting data from the National Weather Service API
- generated a plot of forecasted temperatures
What’s next?¶
Now we can continue investigating other APIs using some example workflows in this cookbook.