<jupyter_start><jupyter_text>In order to start interacting with APIs, we'll start by familiarizing ourselves with the `requests` library for python. This library provides functionality for creating and submitting requests to servers, as well as for handling the responses. We'll start by importing the library:<jupyter_code>import requests<jupyter_output><empty_output><jupyter_text>The `requests` import provides access to submitting requests through a number of pathways, intended to be relatively streightfoward. For example, common http verbs (GET, POST, DELETE, etc) are actually functions that can be called directly off the requests object. For basic requests (simple GET requests) all you need is to pass the URL to the function and it returns the response:<jupyter_code>requests.get('http://www.example.com')<jupyter_output><empty_output><jupyter_text>By default, instantiation of the object just prints the response code; if we capture the object returned by the function, we can dig into it a little deeper:<jupyter_code>response = requests.get('http://www.example.com')
response.url
response.status_code
response.text<jupyter_output><empty_output><jupyter_text>That allows us to view the contents of the body of the request, but as you can see, it's a bit difficult to make sense of. As we can see in the beginning of the body it specifies that the document type is html (``), which means this information is better rendered as a webpage. Browsers are much better at rendering html compared to jupyter notebooks, so if you want to see the site, you can click on the URL which should open it in a new tab:<jupyter_code>print(response.url)<jupyter_output><empty_output><jupyter_text>But no matter - we're not here to interact with web pages - we're here to interact with APIs, which are designed for machine readability (as opposed to webpages, which are designed for human readability). One more important function build into the `response` object is the `.json()` function - used to unpack json messages in the body of the response. It attemps to decode JSON formatted messages to a more readable format. In the event that there is no identifiable JSON content, it throws a `JSONDecodeError`. In our example response, we can see this happen:<jupyter_code>response.json()<jupyter_output><empty_output><jupyter_text>---We'll continue using the `requests` library, but if you're interested in a depper dive, the documentation for this library can be found [here](https://docs.python-requests.org/en/master/).But that's enough of html content - we're here to examine APIs! And like any good internet project, we'll start with one of the central tenents of the internet - cats! Cataas (**Cats** **a**s **a** **s**ervice - cataas.com) is a free API that provides free random cat images. We can make a request:<jupyter_code>cat_response = requests.get('https://cataas.com/cat')<jupyter_output><empty_output><jupyter_text>And then use the `display` method provided by the IPython library to render the content of the response:<jupyter_code>from IPython import display
display.Image(cat_response.content)<jupyter_output><empty_output><jupyter_text>So now we have an image of a cat in the response from our request to cataas. But are we sure it's a cat? Well let's check using a different API. I've signed up an account to a service called [imagga](https://imagga.com/) that performs image analysis, including assigning tags to an image with associated confidence levels.To access this service, we can submit our image that we obtained from the cat as a service endpoint by submitting it to the imagga service using a POST request. Just like the GET request, we can use the http verb as a function call with the requests library (`requests.post()`). Only this time, as we're submitting a post request, it will require some additional information beyond just the URL we submitted to cataas.We'll start by defining a few variables that we'll need to access ths service. The service requires credentials to access the APIs - in this case it requests them as a tuple containing an api key and an api secret, which were generated when I signed up. I've combined these two into a tuple which we will use in the request.<jupyter_code>api_key = 'acc_5a68b63ba144977'
api_secret = '8d19df5111960c0a9f79481d50751c59'

authorization = (api_key, api_secret)<jupyter_output><empty_output><jupyter_text>Next, we need two more things - first, the URL to the endpoint we wish to use. In this case, we are going to obtain a general category of the image from imagga using the api located at https://api.imagga.com/v2/categories/personal_photos.Secondly, we'll need the image we want categorized - in this case it is still available from our `cat_response.content` object we obtained from cataas.Now that we have all the required pieces to form the POST request, we can pass them to the `requests.post()` function and collect the response sent back from the imagga server:<jupyter_code>categorization_response = requests.post(
    'https://api.imagga.com/v2/categories/personal_photos',
    auth=authorization,
    files={'image': cat_response.content})<jupyter_output><empty_output><jupyter_text>The response object from the POST request actually contains a copy of the request as sent (a nice feature of the python `requests` library) that we can examine to see exactly what was sent over to the server. Note that there are a number of arguments that were automatically added by the `requests` library using default values:<jupyter_code>print("request url: " + categorization_response.request.url)
print("request header: " + str(categorization_response.request.headers))
print("request body: " + str(categorization_response.request.body))<jupyter_output><empty_output><jupyter_text>Of course, the thing we're REALLY interested in is how well the service was able to assign a category to our cat:<jupyter_code>categorization_response.json()<jupyter_output><empty_output><jupyter_text>Depending on the random image you got from cataas, you may not be satisfied with this answer. Let's try again with a different algorithm - one that assigns descriptive 'tags' to our image that describe it. You can see that the majority of the request is unchanged, except for the URL to which it is submitted:<jupyter_code>classification_response = requests.post(
    'https://api.imagga.com/v2/tags',
    auth=authorization,
    # files={'image': cat_response.content})
    data={'image': cat_response.content})<jupyter_output><empty_output><jupyter_text>Now when we get the results, we can see that there are a number of tags assigned with varying degrees of confidence - some of which may or may not be accurate.<jupyter_code>classification_response.json()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.06851139390352175, 'fraction_numerical': 0.0056229653743711154, 'mean_word_length': 5.536750483558994, 'pattern_counts': {'":': 0, '<': 53, '<?xml version=': 0, '>': 53, 'https://': 6, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '13', 'dir': 'jupyter-structured-clean-dedup', 'id': '316488', 'license': 'MIT', 'n_tokens_mistral': 1852, 'n_tokens_neox': 1779, 'n_words': 865, 'path': 'notebooks/Intro_to_APIs.ipynb', 'repo_name': 'AFIDSI/API_introduction', 'size': '6758'}