Calling API's in Python

Introduction

In cloud computing, an API (Application Programming Interface) refers to an Internet URL that is used to exchange data. Most commonly, data is returned by an API as JSON or XML. A general description of API's can be found in this article.

The Purpose and Use of API's

API's are extremely valuable. Why? They provide lots of information we depend on, without every web site and application having to store that information or build the same functions. Need a map? Call a map API, with directions and images. Need a list of products, reviews, places or business, people, or geographical locations? Call an API to obtain almost anything.

An API is very much like an external function. It can be considered a "black box", that processes data and returns a result, and the internal processing is unknown. In fact, API's are very often developed and maintained by other organizations, who allow developers to access them for free or based on fees. As a user of the API, we may not know where the actual data is coming from - and we don't usually care. It could be coming from a database, files, articles or books. The details are hidden, but the format of the results is usually publicly known.

The diagram below illustrates how a modular program might call a remote API. API's are very similar to external functions, except the method exists somewhere on the Internet as a web method.

Diagram of API call in Modular Program

Figure 1: Diagram of an API Call in a Modular Program

Sample API Service

One of the best examples of a public API is a weather service. There are many existing services that provide some type of API. For demonstration purposes, we will use www.weatherapi.com.

In order to prevent overuse and abuse, most services require users to sign up and obtain an "API key". In many cases, registration and occasional use of the services are free. But the API key provides a way for the host organization to track how the service is being used and by whom.

Cloud-based API's are normally accessed using a URL, similar to requesting an HTML page in a browser. The difference is that an API returns data - not HTML. The data is normally JSON or XML, although some services will return other types of data.

weatherapi.com provides many different API "end points", the URL's used to interact with the service. The simplest one can be called using a zip code. Here is the syntax for a simple API request:

https://api.weatherapi.com/v1/current.json?key=API_KEY&q=ZIP_CODE&aqi=no;

This is called a REST API Call. It looks like a web page address. Besides the basic endpoint (https://api.weatherapi.com/v1/current.json), there are three parameters:

  1. API_KEY
  2. ZIP_CODE
  3. aqi - which is a flag for returning air-quality information.

The first parameter is defined using a "?" after the root web address. All following parameters are defined by an ampersand ("&").

An actual API call would look like the following:

https://api.weatherapi.com/v1/current.json?key=396210bf5f8743aba3301040210804&q=47401&aqi=no;

(The API_KEY above is the instructors API key from apiweather.com. The zip code is for Bloomington, Indiana.)

Handling an API Response

A sample of the JSON that is returned from weatherapi.com is below. Delving into the aspects of how JSON is formatted is beyond this article, although more details are available in Introduction to JSON.

For our purposes, the main thing to recognize in the data below is that there are two "levels" and two records. Level 1 is a record named "location". Below that is another record named "current". The second record has a second level of information in a field named "condition".

        {
            "location": {
                "name": "Bloomington",
                "region": "Indiana",
                "country": "USA",
                "lat": 39.1,
                "lon": -86.46,
                "tz_id": "America/Indiana/Indianapolis",
                "localtime_epoch": 1617840748,
                "localtime": "2021-04-07 20:12"
            },
            "current": {
                "last_updated_epoch": 1617836400,
                "last_updated": "2021-04-07 19:00",
                "temp_c": 21.1,
                "temp_f": 70.0,
                "is_day": 1,
                "condition": {
                    "text": "Partly cloudy",
                    "icon": "//cdn.weatherapi.com/weather/64x64/day/116.png",
                    "code": 1003
                },
                "wind_mph": 12.5,
                "wind_kph": 20.2,
                "wind_degree": 150,
                "wind_dir": "SSE",
                "pressure_mb": 1010.0,
                "pressure_in": 30.3,
                "precip_mm": 2.7,
                "precip_in": 0.11,
                "humidity": 64,
                "cloud": 25,
                "feelslike_c": 21.1,
                "feelslike_f": 70.0,
                "vis_km": 16.0,
                "vis_miles": 9.0,
                "uv": 5.0,
                "gust_mph": 15.7,
                "gust_kph": 25.2
            }
        }
    

API's and JSON in Python

Calling an API and processing the JSON result requires two additional libraries (requests and json). They can be imported using the following code at the top of the Python program:

import requests
import json

It is necessary to install the requests library, as it does not come with Python. This can be done using a command window (or PowerShell) with:

pip install requests

See these instructions for more information.

Calling the API and Getting Data

Calling an API uses the end point, and any required parameters.

The syntax for calling the API and getting the JSON result ready for processing look like this:

# send request to API and get response
response_data = requests.get(URL)
# store the response text in a variable
data = response_data.text
# parse the json text (like above) into a JSON object
json_data = json.loads(data)

Once we have a JSON object, we can find individual records and fields using syntax like the following:

location_name = json_data['location']['name']

Notice that the JSON object is used like an array. There are two indexes referenced by name. The first one is the record name. In this case, "location". The second reference is to a field inside the location record, referenced by the field name, "name". This same approach works for either record and any field. In the case of the "condition" field, it is a record, too. We can reference those fields by adding a third reference:

condition = json_data['current']['condition']['text']

Sample API Program

Below is a full, functioning program to pull weather data from api.weatherapi.com. You have permission to clone and use this code to practice calling an API (including permission to use the read-only API_KEY for testing purposes.

# 2021-04-07
# drm
# SDEV 140 Sample
# d. marrero
# sample code to pull json data from an API
# this program calls an API with a zip code and receives weather info for a single location

# imports
# one must do: pip install requests to make the following library available
import requests
import json

API_KEY = "396210bf5f8743aba3301040210804"
ZIP_CODE = "47401"

# URL to obtain country list as JSON: API key and Zip Code are concatenated with the base URL
URL = "https://api.weatherapi.com/v1/current.json?key=" + API_KEY + "&q=" + ZIP_CODE + "&aqi=no";

# send request to API and get response
response_data = requests.get(URL)

# store response in variable
data = response_data.text

# parse the json into a JSON object
json_data = json.loads(data)

# uncomment following line to see raw json
# print (data)

# find individual fields in the JSON data
location_name = json_data['location']['name']
region = json_data['location']['region']
latitude = json_data['location']['lat']
longitude = json_data['location']['lon']
temp_c = json_data['current']['temp_c']
temp_f = json_data['current']['temp_f']
wind_mph = json_data['current']['wind_mph']
condition = json_data['current']['condition']['text']

print("")
print (f"Zip Code: {ZIP_CODE}")
print (f"Location name: {location_name}")
print (f"Region name: {region}")
print (f"Longitude: {longitude}")
print (f"Latitude: {latitude}")
print (f"Wind (mph): {wind_mph}")
print (f"Conditions: {condition}")