Calling Web API's in Python

By Michael Fudge

Part 1. Essentials Before We Code

Okay, Let's Start with "What is an API?"

API stands for Application Programming Interface. Simply put, it is code written to interact with something. For example if you want to write a program to make your computer perform text to speech you can either get your PhD in digital signal processing and write it yourself, or you can find someone else's code and figure out how to integrate it within your application. Programmers do the latter all the time, which is called consuming the API. API's make programming easier because we re-use other people's code and don't have to write everything ourselves.

You have been consuming API's all along in this course. Every time we import a Python module we are adding other people's code to our programs, and consuming their API. In addition, the built in Python string functions like startswith() and upper() are part of Pythons internal API. If we want to convert a string to upper case, we don't have to write that code ourselves, we can just use the built in string API!

What makes Python so great is there are many useful API's built into the language. There is also a website, called the Python Package Index https://pypi.org/ where you can find other API's and then install them into your Python environment using the pip utility.

What is a Web API?

A web API is an API that gets called over the world-wide-web. In this scenairo, the API code is not on your computer. It's on another computer on your network (almost always a web server over the Internet). When your code wants to call the Web API:

  1. It must make an request which travels over the network to the computer, a web server, hosting the API.
  2. The web server hosting the API runs the API code and generates a response which it sends back to your computer

Why Web API's?

Web API's are commonplace in the era of cloud computing, but the question is: why?

Initially the web focused on the direct user-consumption of information. We used a browser and search engine to get the news, sports scores, watch youtube or get the latest weather forecast. We did not need web API's because all of this consumption took place in a web browser, and the services we used could just send us HTML, which our browser rendered into nice looking webpages.

The emergence of smart devices like watches, phones, media players, and intelligent speakers has caused a shift in the web from user-based to device-based. Most of the information we consume nowadays is no longer in a browser, but instead on a variety of devices. We watch movies on our Roku players, check the news and sports scores on our smart phones, or get weather reports from Alexa. For example:

  • When we use an app on our smartphone to get stock quotes, a web API is delivering data to the app on our phone. The app is then displaying the information on our screen.
  • When we ask Alexa to play a song, Web API will search for the the song (on Spotify for example) and return back information about the song including how the speaker can play it.

You might think you need to know a lot about networking to call a web API, but actually HTTP (Hyptertext Transport Protocol) handles most of the details for you. HTTP is the protocol that makes the web work. You do need to understand some basics of HTTP in order to correctly consume web API's.

Understanding HTTP

The Hyptertext Transport Protocol (HTTP) is the means by which hosts on the web communicate. The communication occurs in a request/response pair.

  • An HTTP request is sent by the client to the web server and consists of a URL or Uniform Resource Locator and an HTTP Verb
  • An HTTP response is sent from the web server to the client and consists of a Status Code and a Response Body

URL

A URL consists of the name of the web server (the host) the path to the resource, and optional query arguments. For example this URL accesses the iSchool's Spring 2019 Undergraduate class schedule:https://ischool.syr.edu/classes/spring-2019/undergraduate.

In addition, you can include an optional query which affects how the resource returns a reponse. For example here's page 2 of the class schedule: https://ischool.syr.edu/classes/spring-2019/undergraduate/?page=2. URL's are controlled by the web developer and/or application running on the server. The client does not have any say is how the URL looks or behaves, just like you cannot choose another person's phone number or email address. One must simply know the URL to access the resource.

HTTP Verbs

In addition to the URL, an HTTP Verb must be included in the request. The verb specifies how the resource will be accessed.

  • The GET verb fetches an existing resource from the web server. No data is sent other than the URL.
  • The POST verb is for sending a data payload to the webserver along with the URL.
  • There are other verbs like PUT, PATCH, DELETE, HEAD, TRACE and OPTIONS but we will mostly stick to GET and POST.

Status Codes

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. Status codes are 3 digit numbers and the first number indicates the type of response:

  • 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. For example a 304 - Not modified means you requested the resource from the server, but the response is coming from the browser's cache (content already downloaded by your browser).
  • 4xx - codes which begin with a 4 indicate a client error. The most common code here is 404 - Not Found, which occurs when the client makes a request a URL for which the resource does not exist.
  • 5xx - codes which begin with a 5 indicate a server error. The most common code here is 500 - Internal server error, which indicated the server could not process the request.

Response Body

Included in any response, whether successful or not, is a response body. The response body contains the actual content. With most browser responses the content is HTML (Hypertext Markup Language). HTML is a content type for rendering data in a webpage; It has data and also layout information for how the page should look.

HTML is not a suitable format for Web API's because we only want the data - not the layout. As such most web API's return a response type in XML extensible markup language or JSON Javascript Object Notation formats. Both formats only contain data.

JSON (Javascript Object Notation)

Because the reciever of the information is now a device as opposed to a web browser, HTML is not a suitable format. HTML includes presentation and layout information with the data, making it difficult for a device to process. Instead we just want the API to return the data in a structured format so that our consumer program can extract the information we need.

Most web API's return data back to the consumer in JSON (JavaScript Object Notation) format. JSON is a lightweight data format which is easy for people to read and write.

Here's an example JSON response we might get from a weather API:

{ 'location' : 'Syracuse, NY',
  'time' : '2018-10-31 9:00+05:00',
  'temperature' : 59.6,
  'humidity' : 95.5
}

If you think that looks a lot like a Python dictionary object, you would be right! It's very easy to convert JSON into a Python object (a process called de-serialization) and to conver a Python object back into JSON (that's called serialization). This makes Python a very capable language for consuming web API's.

Putting it all together

There were a lot of concepts in this section, let's distill them into aa generic algorithm for consuming web API's in Python.

To call a web API in Python, we:

  1. Make an HTTP GET or POST Request to the web API's URL
  2. Check the Response Status Code 200/OK to ensure the request worked
  3. If OK we de-serialize the response Body JSON format into a Python object

That's it! The only thing that changes about the process is:

  1. The HTTP URL and Verb - it will depend on the specific web API we choose to call. We must read the API's instructions to understand how to use it.
  2. The Python object we get back from the de-serialized response. Once again it will depend on the API we call. It is up to you to read the instructions and figure out how to extract what you need from the response.

Part 2. Making HTTP Requests in Python using the Requests Module

Before we call Web API's in Python, we must first understand how to make HTTP requests. For this, we will use the requests module: http://docs.python-requests.org/en/master/user/quickstart/. This library is based on Python's internal urllib library, but is more expressive and easier to understand. requests makes dealing with the HTTP protocol very pleasant by eliminating a lot of the boilerplate code you need to make a request and handle the response.

URLLIB: Life without the Requests module

When we use urllib we there is more boilerplate code - extra code we must write to make things work. Making the request and converting the response to a Python object are both 2 step processes.

In [18]:
import urllib.request, urllib.error, urllib.parse
import json
try:
    data = { 'name' : 'mike', 'age' : 45 }
    request = urllib.request.Request('https://httpbin.org/get?' + urllib.parse.urlencode(data) ) # make the request URL
    response = urllib.request.urlopen(request) # execute the request
    raw_data = response.read()           # read the data
    object_data = json.loads(raw_data)   # deserilaize the data into python object
    print(object_data)
except urllib.error.HTTPError as e:
    print(e)
{'args': {'age': '45', 'name': 'mike'}, 'headers': {'Accept-Encoding': 'identity', 'Host': 'httpbin.org', 'User-Agent': 'Python-urllib/3.6'}, 'origin': '71.176.79.102, 71.176.79.102', 'url': 'https://httpbin.org/get?name=mike&age=45'}

The equivalent in Requests

The requests module requires just one line of code for request and response. All of the dirty work of encoding the URL and deserializing the response are handled for us!

In [19]:
import requests
data = { 'name' : 'mike', 'age' : 45 }
response = requests.get('https://httpbin.org/get', params = data) # make and execute the request to URL in one step!
if response.ok:
    object_data = response.json() # read and de-serialize in one step!
    print(object_data)
else:
    print(response.status_code, response.reason)
{'args': {'age': '45', 'name': 'mike'}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.22.0'}, 'origin': '71.176.79.102, 71.176.79.102', 'url': 'https://httpbin.org/get?name=mike&age=45'}

HTTP Requests

To make a request simply call requests.get() with the url string as the argument. This example gets the contents of the URL https://httpbin.org/html which returns a section of the novel Moby Dick as an HTML page.

In [20]:
response = requests.get('https://httpbin.org/html')
html = response.text
print(html[:296], '...') # just the first 296 characters, please
<!DOCTYPE html>
<html>
  <head>
  </head>
  <body>
      <h1>Herman Melville - Moby-Dick</h1>

      <div>
        <p>
          Availing himself of the mild, summer-cool weather that now reigned in these latitudes, and in preparation for the peculiarly active pursuits shortly to be anticipated, ...

Status Codes - Did it work?

How do you know if the request worked? You should check the response.ok variable. This is True when the HTTP response code is 200. A response code of 200 has the reason OK.

In [21]:
response = requests.get('https://httpbin.org/html')
print("OK?", response.ok)
print("HTTP Status Code:", response.status_code)
print("HTTP Status Code Reason:", response.reason)
OK? True
HTTP Status Code: 200
HTTP Status Code Reason: OK

Here's an example of a response which is not OK. I'm requesting the URL https://httpbin.org/mikefudge which should not be found on that web server. This yields a response status code of 404 and a reason of NOT FOUND.

In [22]:
response = requests.get('https://httpbin.org/mikefudge')
print("OK?", response.ok)
print("HTTP Status Code:", response.status_code)
print("HTTP Status Code Reason:", response.reason)
OK? False
HTTP Status Code: 404
HTTP Status Code Reason: NOT FOUND

HTTP Responses

The HTTP response is stored in a Python variable called response. We can get the raw response as a string by asking for response.text. Here is the raw response from the URL https://httpbin.org/get which returns JSON as a Python string:

In [23]:
response = requests.get('https://httpbin.org/get')
if response.ok:
    print(response.text)
else:
    print(response.status_code, response.reason)
{
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.22.0"
  }, 
  "origin": "71.176.79.102, 71.176.79.102", 
  "url": "https://httpbin.org/get"
}

Deserializing the Response

If the response is in JSON format, we can easily deserialize the response into a Python object by calling the response.json(). For example we call the same URL https://httpbin.org/get, but this time easily extract the "origin" key from the Python object. It is far easier to extract information from a Python object than it is to search for what you need within the equivalent string!

In [24]:
response = requests.get('https://httpbin.org/get')
if response.ok:
    py_object = response.json() # de-serialize the string into a Python object!
    print("Python Object: ", py_object, '\n')
    print("Type of Object: ", type(py_object), '\n')
    print("Just the value of the 'origin' key: ", py_object['origin'], '\n')
else:
    print(response.status_code, response.reason)
Python Object:  {'args': {}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Host': 'httpbin.org', 'User-Agent': 'python-requests/2.22.0'}, 'origin': '71.176.79.102, 71.176.79.102', 'url': 'https://httpbin.org/get'} 

Type of Object:  <class 'dict'> 

Just the value of the 'origin' key:  71.176.79.102, 71.176.79.102 

What Happens when the Response Cannot be Deserialized?

What happens if you try and deserialize response content which is not in JSON format? You will get an exception of type json.decoder.JSONDecodeError. For example when we try to request the HTML at 'https://httpbin.org/html', the response is OK but the response.json() function fails to decode the text into a Python object:

In [25]:
try:
    url = 'https://httpbin.org/html'
    response = requests.get(url)
    if response.ok:
        print("HTTP Status Code:", response.status_code, response.reason)
        print(response.json()) # ERROR This is not JSON!!!
except json.decoder.JSONDecodeError:
    print("Sorry the contents of", url, "are not in the JSON format!")
HTTP Status Code: 200 OK
Sorry the contents of https://httpbin.org/html are not in the JSON format!

Putting it all together - making a request

Here's some boilerplate code to make an HTTP request. Just change the URL and go! If the response is JSON, it will deserialize it into the variable pyobj. The code handles invalid HTTP responses and content which is not JSON. Any time you call a web API your code should be similar to this!

In [ ]:
try:
    url = 'Any-URL-Here'
    response = requests.get(url)
    if response.ok:
        pyobj = response.json()
        print("YOUR PYTHON OBJECT: ", pyobj)
    else:
        print(response.status_code, response.reason)
except json.decoder.JSONDecodeError:
    print("Sorry the response from", url, "is not in the JSON format!")

Putting it all together - but what if I write a function to make the request?

As a general rule, you do not want error checking inside the function. Error handling is the responsbility of the caller. For example

In [ ]:
def make_request(url):
    response = requests.get(url)
    response.raise_for_status()
    pyobj = response.json()
    return pyobj


try:
    url = 'Your-Url-Here'
    data = make_request(url)

except requests.exceptions.HTTPError as e:
    print("There was an invalid HTTP response code ", e)

except json.decoder.JSONDecodeError:
    print("Sorry the response could not be deserialized into JSON format!")
    

For more information please review the Python requests quickstart: https://requests.readthedocs.io/en/master/user/quickstart/

Part 3. Common Ways web API's are called

Now that you understand how to initiate HTTP requests and handle responses in Python code, we will conclude this reading by demonstrating several common methods by which web API's are called. In every example the response will be in JSON format, which is by far the most popular format for web API's. We will omit any boilerplate code to check the response status code and format so that we can instead focus on how to properly formulate requests.

Read The Docs!

If you want to understand how to consume a given Web API, you must read the docs. Any web API you are going to consider will include instructions for how to consume the API. They won't always have relevant examples in Python, but instead will explain things like whether you need to add an HTTP header or place an Argument on the Query string. Its up to YOU to figure out how to translate that into Python code. This part of the guide will walk you through most of the common ways this is done, serving as a cookbook of sorts.

Free APIs?

Sadly, the days of free access to Web API's is coming to an end. There's simply no money to be made in offering your serivce to other programmers if you cannot profit. For example, if I offer a weather API for free there will be hundereds of weather apps in the app store using my API. Those apps will charge customers and/or show advertisements and I will not make any money from the consumers while they profit off my service. If I want make money off my API, I must force the API consumer to register and pay per use.

Most API's we will use do this. They have a free tier for testing / trying out the API, which is usually sufficient for your demo day project. The web API's we consume in this reading are all free - an exception and not the rule! I chose free API's so that you can focus on the requests and reponses rather than the logistics of acquiring an API key.

HTTP GET Request to a resource.

The simplest Web API call is an unauthenticated HTTP GET request with no arguments. Sadly, these types of API's are rare nowadays.

This example will call the httpbin Web API to help you answer what is the IP Address of my computer? the IP address is returned in the origin key:

In [27]:
web_api_url = 'https://httpbin.org/ip'
response = requests.get(web_api_url)
response.json()
Out[27]:
{'origin': '71.176.79.102, 71.176.79.102'}

HTTP GET with Query String Parameters

Some API's allow you to include arguments on the query string. These are added to the end of the URL and effect what the URL does with the request.

In this example we will call the open street maps web API. We will request the search URL but include parameters for what we're searching for (in this case Animal Kingdom Lodge) and the format we would like the response (JSON).

We build a Python dictionary variable of query parameters required by the web API on line 2, and then pass them to the requests.get() function as the named argument params = options.

To refresh your memory, a named argument is optional in a function call. If you include it you must assign the parameter to the argument. In the example the parameter is params and the argument (what goes into the function) is the variable options.

In [28]:
web_api_url = 'https://nominatim.openstreetmap.org/search'  
options = { 'q' : 'Animal Kingdom Lodge', 'format' : 'json'}
response = requests.get(web_api_url, params = options)
print("The URL is:", response.url)
response.json()
The URL is: https://nominatim.openstreetmap.org/search?q=Animal+Kingdom+Lodge&format=json
Out[28]:
[{'place_id': 68270960,
  'licence': 'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright',
  'osm_type': 'node',
  'osm_id': 5725706621,
  'boundingbox': ['28.3526885', '28.3527885', '-81.6026352', '-81.6025352'],
  'lat': '28.3527385',
  'lon': '-81.6025852',
  'display_name': 'Animal Kingdom Lodge, 2901, Osceola Parkway, Bay Lake, Reedy Creek Improvement District, Orange County, Florida, 34747, United States',
  'class': 'tourism',
  'type': 'hotel',
  'importance': 0.30100000000000005,
  'icon': 'https://nominatim.openstreetmap.org/images/mapicons/accommodation_hotel2.p.20.png'}]

Arguments in the HTTP Header

Some web API's like Reddit require you to include values in the HTTP Header. For the Reddit API you need a custom User-Agent key with a value which indicates what your application does.

For example, this code requests the top stories from subreddit /r/news in JSON format. We include the headers required by adding a named argument headers = custom_headers to the requests.get() function.

In [3]:
web_api_url = 'https://www.reddit.com/r/news/top.json?count=2'
custom_headers = {'User-Agent' : 'sample-python-application'}
response = requests.get(web_api_url, headers = custom_headers)
response.json() 
Out[3]:
{'kind': 'Listing',
 'data': {'modhash': '',
  'dist': 25,
  'children': [{'kind': 't3',
    'data': {'approved_at_utc': None,
     'subreddit': 'news',
     'selftext': '',
     'author_fullname': 't2_1oogh4ln',
     'saved': False,
     'mod_reason_title': None,
     'gilded': 0,
     'clicked': False,
     'title': 'A vegan couple have been charged with first-degree murder after their 18-month-old son starved to death on a diet of only raw fruit and vegetables',
     'link_flair_richtext': [],
     'subreddit_name_prefixed': 'r/news',
     'hidden': False,
     'pwls': 6,
     'link_flair_css_class': None,
     'downs': 0,
     'hide_score': False,
     'name': 't3_ed9nm2',
     'quarantine': False,
     'link_flair_text_color': 'dark',
     'author_flair_background_color': None,
     'subreddit_type': 'public',
     'ups': 53226,
     'total_awards_received': 5,
     'media_embed': {},
     'author_flair_template_id': None,
     'is_original_content': False,
     'user_reports': [],
     'secure_media': None,
     'is_reddit_media_domain': False,
     'is_meta': False,
     'category': None,
     'secure_media_embed': {},
     'link_flair_text': None,
     'can_mod_post': False,
     'score': 53226,
     'approved_by': None,
     'author_premium': True,
     'thumbnail': '',
     'edited': False,
     'author_flair_css_class': None,
     'steward_reports': [],
     'author_flair_richtext': [],
     'gildings': {'gid_1': 4},
     'content_categories': None,
     'is_self': False,
     'mod_note': None,
     'created': 1576874246.0,
     'link_flair_type': 'text',
     'wls': 6,
     'removed_by_category': None,
     'banned_by': None,
     'author_flair_type': 'text',
     'domain': 'news.sky.com',
     'allow_live_comments': True,
     'selftext_html': None,
     'likes': None,
     'suggested_sort': None,
     'banned_at_utc': None,
     'view_count': None,
     'archived': False,
     'no_follow': False,
     'is_crosspostable': False,
     'pinned': False,
     'over_18': False,
     'all_awardings': [{'count': 1,
       'is_enabled': True,
       'subreddit_id': None,
       'description': 'Gives the author a week of Reddit Premium and %{coin_symbol}100 Coins to do with as they please.',
       'end_date': 1578672000,
       'award_sub_type': 'GLOBAL',
       'coin_reward': 100,
       'icon_url': 'https://i.redd.it/award_images/t5_22cerq/x8zqi9ynmh541_BabySnooTears.png',
       'days_of_premium': 7,
       'is_new': False,
       'id': 'award_aadfc1d7-d4b3-429e-bc71-61876e465ea2',
       'icon_height': 2048,
       'resized_icons': [{'url': 'https://preview.redd.it/award_images/t5_22cerq/x8zqi9ynmh541_BabySnooTears.png?width=16&amp;height=16&amp;auto=webp&amp;s=c9a1a2d9f0d2bd30810e8df3e5678d04cfd48eb7',
         'width': 16,
         'height': 16},
        {'url': 'https://preview.redd.it/award_images/t5_22cerq/x8zqi9ynmh541_BabySnooTears.png?width=32&amp;height=32&amp;auto=webp&amp;s=1bb43511d3fc25cb4381a32664a26716116a9664',
         'width': 32,
         'height': 32},
        {'url': 'https://preview.redd.it/award_images/t5_22cerq/x8zqi9ynmh541_BabySnooTears.png?width=48&amp;height=48&amp;auto=webp&amp;s=e2abde3bbe5b601e2608a07a79b21315fadcb3d9',
         'width': 48,
         'height': 48},
        {'url': 'https://preview.redd.it/award_images/t5_22cerq/x8zqi9ynmh541_BabySnooTears.png?width=64&amp;height=64&amp;auto=webp&amp;s=50423415a196197c51c71239ddd10ee1e553adad',
         'width': 64,
         'height': 64},
        {'url': 'https://preview.redd.it/award_images/t5_22cerq/x8zqi9ynmh541_BabySnooTears.png?width=128&amp;height=128&amp;auto=webp&amp;s=9d1ba3b1fccacf2b9374c9569b6fd0cd53ad9c39',
         'width': 128,
         'height': 128}],
       'days_of_drip_extension': 0,
       'award_type': 'global',
       'start_date': 1576718100,
       'coin_price': 700,
       'icon_width': 2048,
       'subreddit_coin_reward': 0,
       'name': 'Baby Snoo Tears'},
      {'count': 4,
       'is_enabled': True,
       'subreddit_id': None,
       'description': "Shows the Silver Award... and that's it.",
       'end_date': None,
       'award_sub_type': 'GLOBAL',
       'coin_reward': 0,
       'icon_url': 'https://www.redditstatic.com/gold/awards/icon/silver_512.png',
       'days_of_premium': 0,
       'is_new': False,
       'id': 'gid_1',
       'icon_height': 512,
       'resized_icons': [{'url': 'https://www.redditstatic.com/gold/awards/icon/silver_16.png',
         'width': 16,
         'height': 16},
        {'url': 'https://www.redditstatic.com/gold/awards/icon/silver_32.png',
         'width': 32,
         'height': 32},
        {'url': 'https://www.redditstatic.com/gold/awards/icon/silver_48.png',
         'width': 48,
         'height': 48},
        {'url': 'https://www.redditstatic.com/gold/awards/icon/silver_64.png',
         'width': 64,
         'height': 64},
        {'url': 'https://www.redditstatic.com/gold/awards/icon/silver_128.png',
         'width': 128,
         'height': 128}],
       'days_of_drip_extension': 0,
       'award_type': 'global',
       'start_date': None,
       'coin_price': 100,
       'icon_width': 512,
       'subreddit_coin_reward': 0,
       'name': 'Silver'}],
     'awarders': ['borumlive'],
     'media_only': False,
     'can_gild': False,
     'spoiler': False,
     'locked': False,
     'author_flair_text': None,
     'visited': False,
     'removed_by': None,
     'num_reports': None,
     'distinguished': None,
     'subreddit_id': 't5_2qh3l',
     'mod_reason_by': None,
     'removal_reason': None,
     'link_flair_background_color': '',
     'id': 'ed9nm2',
     'is_robot_indexable': True,
     'report_reasons': None,
     'author': 'M7plusoneequalsm8',
     'discussion_type': None,
     'num_comments': 5906,
     'send_replies': True,
     'whitelist_status': 'all_ads',
     'contest_mode': False,
     'mod_reports': [],
     'author_patreon_flair': False,
     'author_flair_text_color': None,
     'permalink': '/r/news/comments/ed9nm2/a_vegan_couple_have_been_charged_with_firstdegree/',
     'parent_whitelist_status': 'all_ads',
     'stickied': False,
     'url': 'https://news.sky.com/story/vegan-parents-accused-of-starving-child-to-death-on-diet-of-fruit-and-vegetables-11891094?dcmp=snt-sf-twitter',
     'subreddit_subscribers': 19450961,
     'created_utc': 1576845446.0,
     'num_crossposts': 15,
     'media': None,
     'is_video': False}},
   {'kind': 't3',
    'data': {'approved_at_utc': None,
     'subreddit': 'news',
     'selftext': '',
     'author_fullname': 't2_68477',
     'saved': False,
     'mod_reason_title': None,
     'gilded': 0,
     'clicked': False,
     'title': 'If you made a claim for $125 from Equifax, you’re not getting it after court awards nearly $80 million to attorneys',
     'link_flair_richtext': [],
     'subreddit_name_prefixed': 'r/news',
     'hidden': False,
     'pwls': 6,
     'link_flair_css_class': None,
     'downs': 0,
     'hide_score': False,
     'name': 't3_eczsno',
     'quarantine': False,
     'link_flair_text_color': 'dark',
     'author_flair_background_color': None,
     'subreddit_type': 'public',
     'ups': 47016,
     'total_awards_received': 0,
     'media_embed': {},
     'author_flair_template_id': None,
     'is_original_content': False,
     'user_reports': [],
     'secure_media': None,
     'is_reddit_media_domain': False,
     'is_meta': False,
     'category': None,
     'secure_media_embed': {},
     'link_flair_text': None,
     'can_mod_post': False,
     'score': 47016,
     'approved_by': None,
     'author_premium': False,
     'thumbnail': '',
     'edited': False,
     'author_flair_css_class': None,
     'steward_reports': [],
     'author_flair_richtext': [],
     'gildings': {},
     'content_categories': None,
     'is_self': False,
     'mod_note': None,
     'created': 1576819966.0,
     'link_flair_type': 'text',
     'wls': 6,
     'removed_by_category': None,
     'banned_by': None,
     'author_flair_type': 'text',
     'domain': 'cnbc.com',
     'allow_live_comments': True,
     'selftext_html': None,
     'likes': None,
     'suggested_sort': None,
     'banned_at_utc': None,
     'view_count': None,
     'archived': False,
     'no_follow': False,
     'is_crosspostable': False,
     'pinned': False,
     'over_18': False,
     'all_awardings': [],
     'awarders': [],
     'media_only': False,
     'can_gild': False,
     'spoiler': False,
     'locked': False,
     'author_flair_text': None,
     'visited': False,
     'removed_by': None,
     'num_reports': None,
     'distinguished': None,
     'subreddit_id': 't5_2qh3l',
     'mod_reason_by': None,
     'removal_reason': None,
     'link_flair_background_color': '',
     'id': 'eczsno',
     'is_robot_indexable': True,
     'report_reasons': None,
     'author': 'plato_thyself',
     'discussion_type': None,
     'num_comments': 2503,
     'send_replies': False,
     'whitelist_status': 'all_ads',
     'contest_mode': False,
     'mod_reports': [],
     'author_patreon_flair': False,
     'author_flair_text_color': None,
     'permalink': '/r/news/comments/eczsno/if_you_made_a_claim_for_125_from_equifax_youre/',
     'parent_whitelist_status': 'all_ads',
     'stickied': False,
     'url': 'https://www.cnbc.com/2019/12/19/court-awards-80-million-to-consumer-attorneys-in-equifax-case.html',
     'subreddit_subscribers': 19450961,
     'created_utc': 1576791166.0,
     'num_crossposts': 8,
     'media': None,
     'is_video': False}},
   {'kind': 't3',
    'data': {'approved_at_utc': None,
     'subreddit': 'news',
     'selftext': '',
     'author_fullname': 't2_40z2dydl',
     'saved': False,
     'mod_reason_title': None,
     'gilded': 0,
     'clicked': False,
     'title': '19-year-old Chrystul Kizer faces life in prison for killing accused pedophile who allegedly abused her',
     'link_flair_richtext': [],
     'subreddit_name_prefixed': 'r/news',
     'hidden': False,
     'pwls': 6,
     'link_flair_css_class': None,
     'downs': 0,
     'hide_score': False,
     'name': 't3_ed1mz5',
     'quarantine': False,
     'link_flair_text_color': 'dark',
     'author_flair_background_color': None,
     'subreddit_type': 'public',
     'ups': 3967,
     'total_awards_received': 0,
     'media_embed': {},
     'author_flair_template_id': None,
     'is_original_content': False,
     'user_reports': [],
     'secure_media': None,
     'is_reddit_media_domain': False,
     'is_meta': False,
     'category': None,
     'secure_media_embed': {},
     'link_flair_text': None,
     'can_mod_post': False,
     'score': 3967,
     'approved_by': None,
     'author_premium': True,
     'thumbnail': '',
     'edited': False,
     'author_flair_css_class': None,
     'steward_reports': [],
     'author_flair_richtext': [],
     'gildings': {},
     'content_categories': None,
     'is_self': False,
     'mod_note': None,
     'created': 1576827935.0,
     'link_flair_type': 'text',
     'wls': 6,
     'removed_by_category': None,
     'banned_by': None,
     'author_flair_type': 'text',
     'domain': 'abcnews.go.com',
     'allow_live_comments': True,
     'selftext_html': None,
     'likes': None,
     'suggested_sort': None,
     'banned_at_utc': None,
     'view_count': None,
     'archived': False,
     'no_follow': False,
     'is_crosspostable': False,
     'pinned': False,
     'over_18': False,
     'all_awardings': [],
     'awarders': [],
     'media_only': False,
     'can_gild': False,
     'spoiler': False,
     'locked': False,
     'author_flair_text': None,
     'visited': False,
     'removed_by': None,
     'num_reports': None,
     'distinguished': None,
     'subreddit_id': 't5_2qh3l',
     'mod_reason_by': None,
     'removal_reason': None,
     'link_flair_background_color': '',
     'id': 'ed1mz5',
     'is_robot_indexable': True,
     'report_reasons': None,
     'author': 'Vigilant_Tyranos',
     'discussion_type': None,
     'num_comments': 591,
     'send_replies': True,
     'whitelist_status': 'all_ads',
     'contest_mode': False,
     'mod_reports': [],
     'author_patreon_flair': False,
     'author_flair_text_color': None,
     'permalink': '/r/news/comments/ed1mz5/19yearold_chrystul_kizer_faces_life_in_prison_for/',
     'parent_whitelist_status': 'all_ads',
     'stickied': False,
     'url': 'https://abcnews.go.com/US/chrystul-kizer-19-faces-life-prison-killing-accused/story?id=67805720',
     'subreddit_subscribers': 19450961,
     'created_utc': 1576799135.0,
     'num_crossposts': 2,
     'media': None,
     'is_video': False}},
   {'kind': 't3',
    'data': {'approved_at_utc': None,
     'subreddit': 'news',
     'selftext': '',
     'author_fullname': 't2_40rffwxf',
     'saved': False,
     'mod_reason_title': None,
     'gilded': 0,
     'clicked': False,
     'title': 'Deputy sheriff allegedly scoured obituaries, broke into homes of people at funerals',
     'link_flair_richtext': [],
     'subreddit_name_prefixed': 'r/news',
     'hidden': False,
     'pwls': 6,
     'link_flair_css_class': None,
     'downs': 0,
     'hide_score': False,
     'name': 't3_ecxm1s',
     'quarantine': False,
     'link_flair_text_color': 'dark',
     'author_flair_background_color': None,
     'subreddit_type': 'public',
     'ups': 2714,
     'total_awards_received': 0,
     'media_embed': {},
     'author_flair_template_id': None,
     'is_original_content': False,
     'user_reports': [],
     'secure_media': None,
     'is_reddit_media_domain': False,
     'is_meta': False,
     'category': None,
     'secure_media_embed': {},
     'link_flair_text': None,
     'can_mod_post': False,
     'score': 2714,
     'approved_by': None,
     'author_premium': False,
     'thumbnail': '',
     'edited': False,
     'author_flair_css_class': None,
     'steward_reports': [],
     'author_flair_richtext': [],
     'gildings': {},
     'content_categories': None,
     'is_self': False,
     'mod_note': None,
     'created': 1576810639.0,
     'link_flair_type': 'text',
     'wls': 6,
     'removed_by_category': None,
     'banned_by': None,
     'author_flair_type': 'text',
     'domain': 'nbcnews.com',
     'allow_live_comments': True,
     'selftext_html': None,
     'likes': None,
     'suggested_sort': None,
     'banned_at_utc': None,
     'view_count': None,
     'archived': False,
     'no_follow': False,
     'is_crosspostable': False,
     'pinned': False,
     'over_18': False,
     'all_awardings': [],
     'awarders': [],
     'media_only': False,
     'can_gild': False,
     'spoiler': False,
     'locked': False,
     'author_flair_text': None,
     'visited': False,
     'removed_by': None,
     'num_reports': None,
     'distinguished': None,
     'subreddit_id': 't5_2qh3l',
     'mod_reason_by': None,
     'removal_reason': None,
     'link_flair_background_color': '',
     'id': 'ecxm1s',
     'is_robot_indexable': True,
     'report_reasons': None,
     'author': 'FabulousMouse8',
     'discussion_type': None,
     'num_comments': 196,
     'send_replies': True,
     'whitelist_status': 'all_ads',
     'contest_mode': False,
     'mod_reports': [],
     'author_patreon_flair': False,
     'author_flair_text_color': None,
     'permalink': '/r/news/comments/ecxm1s/deputy_sheriff_allegedly_scoured_obituaries_broke/',
     'parent_whitelist_status': 'all_ads',
     'stickied': False,
     'url': 'https://www.nbcnews.com/news/us-news/wisconsin-deputy-allegedly-scoured-obits-broke-homes-people-funerals-n1104021?cid=referral_taboolafeed',
     'subreddit_subscribers': 19450961,
     'created_utc': 1576781839.0,
     'num_crossposts': 2,
     'media': None,
     'is_video': False}},
   {'kind': 't3',
    'data': {'approved_at_utc': None,
     'subreddit': 'news',
     'selftext': '',
     'author_fullname': 't2_hg9rx',
     'saved': False,
     'mod_reason_title': None,
     'gilded': 0,
     'clicked': False,
     'title': 'US woman - with diplomatic immunity - to be charged for death of British citizen Harry Dunn',
     'link_flair_richtext': [],
     'subreddit_name_prefixed': 'r/news',
     'hidden': False,
     'pwls': 6,
     'link_flair_css_class': None,
     'downs': 0,
     'hide_score': False,
     'name': 't3_edaccy',
     'quarantine': False,
     'link_flair_text_color': 'dark',
     'author_flair_background_color': None,
     'subreddit_type': 'public',
     'ups': 1929,
     'total_awards_received': 0,
     'media_embed': {},
     'author_flair_template_id': None,
     'is_original_content': False,
     'user_reports': [],
     'secure_media': None,
     'is_reddit_media_domain': False,
     'is_meta': False,
     'category': None,
     'secure_media_embed': {},
     'link_flair_text': None,
     'can_mod_post': False,
     'score': 1929,
     'approved_by': None,
     'author_premium': False,
     'thumbnail': '',
     'edited': False,
     'author_flair_css_class': None,
     'steward_reports': [],
     'author_flair_richtext': [],
     'gildings': {},
     'content_categories': None,
     'is_self': False,
     'mod_note': None,
     'created': 1576878098.0,
     'link_flair_type': 'text',
     'wls': 6,
     'removed_by_category': None,
     'banned_by': None,
     'author_flair_type': 'text',
     'domain': 'bbc.co.uk',
     'allow_live_comments': True,
     'selftext_html': None,
     'likes': None,
     'suggested_sort': None,
     'banned_at_utc': None,
     'view_count': None,
     'archived': False,
     'no_follow': False,
     'is_crosspostable': False,
     'pinned': False,
     'over_18': False,
     'all_awardings': [],
     'awarders': [],
     'media_only': False,
     'can_gild': False,
     'spoiler': False,
     'locked': False,
     'author_flair_text': None,
     'visited': False,
     'removed_by': None,
     'num_reports': None,
     'distinguished': None,
     'subreddit_id': 't5_2qh3l',
     'mod_reason_by': None,
     'removal_reason': None,
     'link_flair_background_color': '',
     'id': 'edaccy',
     'is_robot_indexable': True,
     'report_reasons': None,
     'author': 'legs_of_thunder',
     'discussion_type': None,
     'num_comments': 292,
     'send_replies': True,
     'whitelist_status': 'all_ads',
     'contest_mode': False,
     'mod_reports': [],
     'author_patreon_flair': False,
     'author_flair_text_color': None,
     'permalink': '/r/news/comments/edaccy/us_woman_with_diplomatic_immunity_to_be_charged/',
     'parent_whitelist_status': 'all_ads',
     'stickied': False,
     'url': 'https://www.bbc.co.uk/news/uk-england-50870459',
     'subreddit_subscribers': 19450961,
     'created_utc': 1576849298.0,
     'num_crossposts': 1,
     'media': None,
     'is_video': False}},
   {'kind': 't3',
    'data': {'approved_at_utc': None,
     'subreddit': 'news',
     'selftext': '',
     'author_fullname': 't2_16b6ci',
     'saved': False,
     'mod_reason_title': None,
     'gilded': 0,
     'clicked': False,
     'title': 'Roads melt in South Australia as temperature reaches 49.9c -(121.8f)',
     'link_flair_richtext': [],
     'subreddit_name_prefixed': 'r/news',
     'hidden': False,
     'pwls': 6,
     'link_flair_css_class': None,
     'downs': 0,
     'hide_score': False,
     'name': 't3_ecz1pz',
     'quarantine': False,
     'link_flair_text_color': 'dark',
     'author_flair_background_color': None,
     'subreddit_type': 'public',
     'ups': 1145,
     'total_awards_received': 0,
     'media_embed': {},
     'author_flair_template_id': None,
     'is_original_content': False,
     'user_reports': [],
     'secure_media': None,
     'is_reddit_media_domain': False,
     'is_meta': False,
     'category': None,
     'secure_media_embed': {},
     'link_flair_text': None,
     'can_mod_post': False,
     'score': 1145,
     'approved_by': None,
     'author_premium': False,
     'thumbnail': '',
     'edited': False,
     'author_flair_css_class': None,
     'steward_reports': [],
     'author_flair_richtext': [],
     'gildings': {},
     'content_categories': None,
     'is_self': False,
     'mod_note': None,
     'created': 1576816783.0,
     'link_flair_type': 'text',
     'wls': 6,
     'removed_by_category': None,
     'banned_by': None,
     'author_flair_type': 'text',
     'domain': 'mobile.abc.net.au',
     'allow_live_comments': True,
     'selftext_html': None,
     'likes': None,
     'suggested_sort': None,
     'banned_at_utc': None,
     'view_count': None,
     'archived': False,
     'no_follow': False,
     'is_crosspostable': False,
     'pinned': False,
     'over_18': False,
     'all_awardings': [],
     'awarders': [],
     'media_only': False,
     'can_gild': False,
     'spoiler': False,
     'locked': False,
     'author_flair_text': None,
     'visited': False,
     'removed_by': None,
     'num_reports': None,
     'distinguished': None,
     'subreddit_id': 't5_2qh3l',
     'mod_reason_by': None,
     'removal_reason': None,
     'link_flair_background_color': '',
     'id': 'ecz1pz',
     'is_robot_indexable': True,
     'report_reasons': None,
     'author': 'CuriousSmell',
     'discussion_type': None,
     'num_comments': 288,
     'send_replies': True,
     'whitelist_status': 'all_ads',
     'contest_mode': False,
     'mod_reports': [],
     'author_patreon_flair': False,
     'author_flair_text_color': None,
     'permalink': '/r/news/comments/ecz1pz/roads_melt_in_south_australia_as_temperature/',
     'parent_whitelist_status': 'all_ads',
     'stickied': False,
     'url': 'https://mobile.abc.net.au/news/2019-12-19/catastrophic-fire-conditions-forecast-in-sa-as-temperatures-soar/11815346?pfmredir=sm&amp;fbclid=IwAR2-QGtIBfjWoi287xjtE0GL_pWm8vDqE607CyX7FV1JlTkbsoPnHggHETk',
     'subreddit_subscribers': 19450961,
     'created_utc': 1576787983.0,
     'num_crossposts': 1,
     'media': None,
     'is_video': False}},
   {'kind': 't3',
    'data': {'approved_at_utc': None,
     'subreddit': 'news',
     'selftext': '',
     'author_fullname': 't2_2b52fv24',
     'saved': False,
     'mod_reason_title': None,
     'gilded': 0,
     'clicked': False,
     'title': 'New civil lawsuit alleges Harvey Weinstein sexually assaulted a 16-year-old Polish model in 2002',
     'link_flair_richtext': [],
     'subreddit_name_prefixed': 'r/news',
     'hidden': False,
     'pwls': 6,
     'link_flair_css_class': None,
     'downs': 0,
     'hide_score': False,
     'name': 't3_ed54xb',
     'quarantine': False,
     'link_flair_text_color': 'dark',
     'author_flair_background_color': None,
     'subreddit_type': 'public',
     'ups': 1083,
     'total_awards_received': 0,
     'media_embed': {},
     'author_flair_template_id': None,
     'is_original_content': False,
     'user_reports': [],
     'secure_media': None,
     'is_reddit_media_domain': False,
     'is_meta': False,
     'category': None,
     'secure_media_embed': {},
     'link_flair_text': None,
     'can_mod_post': False,
     'score': 1083,
     'approved_by': None,
     'author_premium': True,
     'thumbnail': '',
     'edited': False,
     'author_flair_css_class': None,
     'steward_reports': [],
     'author_flair_richtext': [],
     'gildings': {},
     'content_categories': None,
     'is_self': False,
     'mod_note': None,
     'created': 1576845461.0,
     'link_flair_type': 'text',
     'wls': 6,
     'removed_by_category': None,
     'banned_by': None,
     'author_flair_type': 'text',
     'domain': 'cnn.com',
     'allow_live_comments': True,
     'selftext_html': None,
     'likes': None,
     'suggested_sort': None,
     'banned_at_utc': None,
     'view_count': None,
     'archived': False,
     'no_follow': False,
     'is_crosspostable': False,
     'pinned': False,
     'over_18': False,
     'all_awardings': [],
     'awarders': [],
     'media_only': False,
     'can_gild': False,
     'spoiler': False,
     'locked': False,
     'author_flair_text': None,
     'visited': False,
     'removed_by': None,
     'num_reports': None,
     'distinguished': None,
     'subreddit_id': 't5_2qh3l',
     'mod_reason_by': None,
     'removal_reason': None,
     'link_flair_background_color': '',
     'id': 'ed54xb',
     'is_robot_indexable': True,
     'report_reasons': None,
     'author': 'joesoldlegs',
     'discussion_type': None,
     'num_comments': 48,
     'send_replies': True,
     'whitelist_status': 'all_ads',
     'contest_mode': False,
     'mod_reports': [],
     'author_patreon_flair': False,
     'author_flair_text_color': None,
     'permalink': '/r/news/comments/ed54xb/new_civil_lawsuit_alleges_harvey_weinstein/',
     'parent_whitelist_status': 'all_ads',
     'stickied': False,
     'url': 'https://www.cnn.com/2019/12/19/us/harvey-weinstein-new-civil-lawsuit/index.html',
     'subreddit_subscribers': 19450961,
     'created_utc': 1576816661.0,
     'num_crossposts': 1,
     'media': None,
     'is_video': False}},
   {'kind': 't3',
    'data': {'approved_at_utc': None,
     'subreddit': 'news',
     'selftext': '',
     'author_fullname': 't2_46ks53kz',
     'saved': False,
     'mod_reason_title': None,
     'gilded': 0,
     'clicked': False,
     'title': 'Adult Survivors Act: New Jeffrey Epstein accusers push for protections for adult sex abuse survivors',
     'link_flair_richtext': [],
     'subreddit_name_prefixed': 'r/news',
     'hidden': False,
     'pwls': 6,
     'link_flair_css_class': None,
     'downs': 0,
     'hide_score': False,
     'name': 't3_ed12wn',
     'quarantine': False,
     'link_flair_text_color': 'dark',
     'author_flair_background_color': None,
     'subreddit_type': 'public',
     'ups': 982,
     'total_awards_received': 0,
     'media_embed': {},
     'author_flair_template_id': None,
     'is_original_content': False,
     'user_reports': [],
     'secure_media': None,
     'is_reddit_media_domain': False,
     'is_meta': False,
     'category': None,
     'secure_media_embed': {},
     'link_flair_text': None,
     'can_mod_post': False,
     'score': 982,
     'approved_by': None,
     'author_premium': False,
     'thumbnail': '',
     'edited': False,
     'author_flair_css_class': None,
     'steward_reports': [],
     'author_flair_richtext': [],
     'gildings': {},
     'content_categories': None,
     'is_self': False,
     'mod_note': None,
     'created': 1576825425.0,
     'link_flair_type': 'text',
     'wls': 6,
     'removed_by_category': None,
     'banned_by': None,
     'author_flair_type': 'text',
     'domain': 'cbsnews.com',
     'allow_live_comments': True,
     'selftext_html': None,
     'likes': None,
     'suggested_sort': None,
     'banned_at_utc': None,
     'view_count': None,
     'archived': False,
     'no_follow': False,
     'is_crosspostable': False,
     'pinned': False,
     'over_18': False,
     'all_awardings': [],
     'awarders': [],
     'media_only': False,
     'can_gild': False,
     'spoiler': False,
     'locked': False,
     'author_flair_text': None,
     'visited': False,
     'removed_by': None,
     'num_reports': None,
     'distinguished': None,
     'subreddit_id': 't5_2qh3l',
     'mod_reason_by': None,
     'removal_reason': None,
     'link_flair_background_color': '',
     'id': 'ed12wn',
     'is_robot_indexable': True,
     'report_reasons': None,
     'author': 'CaptainWales69',
     'discussion_type': None,
     'num_comments': 22,
     'send_replies': True,
     'whitelist_status': 'all_ads',
     'contest_mode': False,
     'mod_reports': [],
     'author_patreon_flair': False,
     'author_flair_text_color': None,
     'permalink': '/r/news/comments/ed12wn/adult_survivors_act_new_jeffrey_epstein_accusers/',
     'parent_whitelist_status': 'all_ads',
     'stickied': False,
     'url': 'https://www.cbsnews.com/news/adult-survivors-act-new-jeffrey-epstein-accusers-push-for-protections-for-adult-sex-abuse-survivors/',
     'subreddit_subscribers': 19450961,
     'created_utc': 1576796625.0,
     'num_crossposts': 1,
     'media': None,
     'is_video': False}},
   {'kind': 't3',
    'data': {'approved_at_utc': None,
     'subreddit': 'news',
     'selftext': '',
     'author_fullname': 't2_1b5nnm8q',
     'saved': False,
     'mod_reason_title': None,
     'gilded': 0,
     'clicked': False,
     'title': 'For the first time in history, the US economy has started and ended a decade without a recession',
     'link_flair_richtext': [],
     'subreddit_name_prefixed': 'r/news',
     'hidden': False,
     'pwls': 6,
     'link_flair_css_class': None,
     'downs': 0,
     'hide_score': False,
     'name': 't3_ed8d81',
     'quarantine': False,
     'link_flair_text_color': 'dark',
     'author_flair_background_color': None,
     'subreddit_type': 'public',
     'ups': 881,
     'total_awards_received': 0,
     'media_embed': {},
     'author_flair_template_id': None,
     'is_original_content': False,
     'user_reports': [],
     'secure_media': None,
     'is_reddit_media_domain': False,
     'is_meta': False,
     'category': None,
     'secure_media_embed': {},
     'link_flair_text': None,
     'can_mod_post': False,
     'score': 881,
     'approved_by': None,
     'author_premium': True,
     'thumbnail': '',
     'edited': False,
     'author_flair_css_class': None,
     'steward_reports': [],
     'author_flair_richtext': [],
     'gildings': {},
     'content_categories': None,
     'is_self': False,
     'mod_note': None,
     'created': 1576866037.0,
     'link_flair_type': 'text',
     'wls': 6,
     'removed_by_category': None,
     'banned_by': None,
     'author_flair_type': 'text',
     'domain': 'cnbc.com',
     'allow_live_comments': True,
     'selftext_html': None,
     'likes': None,
     'suggested_sort': None,
     'banned_at_utc': None,
     'view_count': None,
     'archived': False,
     'no_follow': False,
     'is_crosspostable': False,
     'pinned': False,
     'over_18': False,
     'all_awardings': [],
     'awarders': [],
     'media_only': False,
     'can_gild': False,
     'spoiler': False,
     'locked': False,
     'author_flair_text': None,
     'visited': False,
     'removed_by': None,
     'num_reports': None,
     'distinguished': None,
     'subreddit_id': 't5_2qh3l',
     'mod_reason_by': None,
     'removal_reason': None,
     'link_flair_background_color': '',
     'id': 'ed8d81',
     'is_robot_indexable': True,
     'report_reasons': None,
     'author': 'suspect309',
     'discussion_type': None,
     'num_comments': 527,
     'send_replies': True,
     'whitelist_status': 'all_ads',
     'contest_mode': False,
     'mod_reports': [],
     'author_patreon_flair': False,
     'author_flair_text_color': None,
     'permalink': '/r/news/comments/ed8d81/for_the_first_time_in_history_the_us_economy_has/',
     'parent_whitelist_status': 'all_ads',
     'stickied': False,
     'url': 'https://www.cnbc.com/2019/12/19/us-economy-avoids-a-recession-for-the-longest-time-ever.html',
     'subreddit_subscribers': 19450961,
     'created_utc': 1576837237.0,
     'num_crossposts': 2,
     'media': None,
     'is_video': False}},
   {'kind': 't3',
    'data': {'approved_at_utc': None,
     'subreddit': 'news',
     'selftext': '',
     'author_fullname': 't2_3xiit59l',
     'saved': False,
     'mod_reason_title': None,
     'gilded': 0,
     'clicked': False,
     'title': "Kentucky ex-governor pardoned child rapist because victim's hymen was intact. Matt Bevin under avalanche of criticism after pardoning 428 people in final days of term including two convicted child rapists",
     'link_flair_richtext': [],
     'subreddit_name_prefixed': 'r/news',
     'hidden': False,
     'pwls': 6,
     'link_flair_css_class': None,
     'downs': 0,
     'hide_score': False,
     'name': 't3_edc25m',
     'quarantine': False,
     'link_flair_text_color': 'dark',
     'author_flair_background_color': None,
     'subreddit_type': 'public',
     'ups': 1533,
     'total_awards_received': 0,
     'media_embed': {},
     'author_flair_template_id': None,
     'is_original_content': False,
     'user_reports': [],
     'secure_media': None,
     'is_reddit_media_domain': False,
     'is_meta': False,
     'category': None,
     'secure_media_embed': {},
     'link_flair_text': None,
     'can_mod_post': False,
     'score': 1533,
     'approved_by': None,
     'author_premium': True,
     'thumbnail': '',
     'edited': False,
     'author_flair_css_class': None,
     'steward_reports': [],
     'author_flair_richtext': [],
     'gildings': {},
     'content_categories': None,
     'is_self': False,
     'mod_note': None,
     'created': 1576886623.0,
     'link_flair_type': 'text',
     'wls': 6,
     'removed_by_category': None,
     'banned_by': None,
     'author_flair_type': 'text',
     'domain': 'theguardian.com',
     'allow_live_comments': False,
     'selftext_html': None,
     'likes': None,
     'suggested_sort': None,
     'banned_at_utc': None,
     'view_count': None,
     'archived': False,
     'no_follow': False,
     'is_crosspostable': False,
     'pinned': False,
     'over_18': False,
     'all_awardings': [],
     'awarders': [],
     'media_only': False,
     'can_gild': False,
     'spoiler': False,
     'locked': False,
     'author_flair_text': None,
     'visited': False,
     'removed_by': None,
     'num_reports': None,
     'distinguished': None,
     'subreddit_id': 't5_2qh3l',
     'mod_reason_by': None,
     'removal_reason': None,
     'link_flair_background_color': '',
     'id': 'edc25m',
     'is_robot_indexable': True,
     'report_reasons': None,
     'author': 'OldFashionedJizz',
     'discussion_type': None,
     'num_comments': 227,
     'send_replies': True,
     'whitelist_status': 'all_ads',
     'contest_mode': False,
     'mod_reports': [],
     'author_patreon_flair': False,
     'author_flair_text_color': None,
     'permalink': '/r/news/comments/edc25m/kentucky_exgovernor_pardoned_child_rapist_because/',
     'parent_whitelist_status': 'all_ads',
     'stickied': False,
     'url': 'https://www.theguardian.com/us-news/2019/dec/20/matt-bevin-kentucky-pardoned-child-rapist',
     'subreddit_subscribers': 19450961,
     'created_utc': 1576857823.0,
     'num_crossposts': 0,
     'media': None,
     'is_video': False}},
   {'kind': 't3',
    'data': {'approved_at_utc': None,
     'subreddit': 'news',
     'selftext': '',
     'author_fullname': 't2_2b52fv24',
     'saved': False,
     'mod_reason_title': None,
     'gilded': 0,
     'clicked': False,
     'title': 'New Jersey banned discrimination based on hairstyle a year after a black student wrestler cut his dreadlocks to compete',
     'link_flair_richtext': [],
     'subreddit_name_prefixed': 'r/news',
     'hidden': False,
     'pwls': 6,
     'link_flair_css_class': None,
     'downs': 0,
     'hide_score': False,
     'name': 't3_ed2thv',
     'quarantine': False,
     'link_flair_text_color': 'dark',
     'author_flair_background_color': None,
     'subreddit_type': 'public',
     'ups': 682,
     'total_awards_received': 0,
     'media_embed': {},
     'author_flair_template_id': None,
     'is_original_content': False,
     'user_reports': [],
     'secure_media': None,
     'is_reddit_media_domain': False,
     'is_meta': False,
     'category': None,
     'secure_media_embed': {},
     'link_flair_text': None,
     'can_mod_post': False,
     'score': 682,
     'approved_by': None,
     'author_premium': True,
     'thumbnail': '',
     'edited': False,
     'author_flair_css_class': None,
     'steward_reports': [],
     'author_flair_richtext': [],
     'gildings': {},
     'content_categories': None,
     'is_self': False,
     'mod_note': None,
     'created': 1576833605.0,
     'link_flair_type': 'text',
     'wls': 6,
     'removed_by_category': None,
     'banned_by': None,
     'author_flair_type': 'text',
     'domain': 'cnn.com',
     'allow_live_comments': True,
     'selftext_html': None,
     'likes': None,
     'suggested_sort': None,
     'banned_at_utc': None,
     'view_count': None,
     'archived': False,
     'no_follow': False,
     'is_crosspostable': False,
     'pinned': False,
     'over_18': False,
     'all_awardings': [],
     'awarders': [],
     'media_only': False,
     'can_gild': False,
     'spoiler': False,
     'locked': False,
     'author_flair_text': None,
     'visited': False,
     'removed_by': None,
     'num_reports': None,
     'distinguished': None,
     'subreddit_id': 't5_2qh3l',
     'mod_reason_by': None,
     'removal_reason': None,
     'link_flair_background_color': '',
     'id': 'ed2thv',
     'is_robot_indexable': True,
     'report_reasons': None,
     'author': 'joesoldlegs',
     'discussion_type': None,
     'num_comments': 162,
     'send_replies': True,
     'whitelist_status': 'all_ads',
     'contest_mode': False,
     'mod_reports': [],
     'author_patreon_flair': False,
     'author_flair_text_color': None,
     'permalink': '/r/news/comments/ed2thv/new_jersey_banned_discrimination_based_on/',
     'parent_whitelist_status': 'all_ads',
     'stickied': False,
     'url': 'https://www.cnn.com/2019/12/19/us/new-jersey-governor-discrimination-hairstyle-trnd/index.html',
     'subreddit_subscribers': 19450961,
     'created_utc': 1576804805.0,
     'num_crossposts': 1,
     'media': None,
     'is_video': False}},
   {'kind': 't3',
    'data': {'approved_at_utc': None,
     'subreddit': 'news',
     'selftext': '',
     'author_fullname': 't2_2b52fv24',
     'saved': False,
     'mod_reason_title': None,
     'gilded': 0,
     'clicked': False,
     'title': "A 7-year-old temporarily living in a domestic violence shelter asked Santa for books and a 'very good dad'",
     'link_flair_richtext': [],
     'subreddit_name_prefixed': 'r/news',
     'hidden': False,
     'pwls': 6,
     'link_flair_css_class': None,
     'downs': 0,
     'hide_score': False,
     'name': 't3_eczf1x',
     'quarantine': False,
     'link_flair_text_color': 'dark',
     'author_flair_background_color': None,
     'subreddit_type': 'public',
     'ups': 682,
     'total_awards_received': 0,
     'media_embed': {},
     'author_flair_template_id': None,
     'is_original_content': False,
     'user_reports': [],
     'secure_media': None,
     'is_reddit_media_domain': False,
     'is_meta': False,
     'category': None,
     'secure_media_embed': {},
     'link_flair_text': None,
     'can_mod_post': False,
     'score': 682,
     'approved_by': None,
     'author_premium': True,
     'thumbnail': '',
     'edited': False,
     'author_flair_css_class': None,
     'steward_reports': [],
     'author_flair_richtext': [],
     'gildings': {},
     'content_categories': None,
     'is_self': False,
     'mod_note': None,
     'created': 1576818362.0,
     'link_flair_type': 'text',
     'wls': 6,
     'removed_by_category': None,
     'banned_by': None,
     'author_flair_type': 'text',
     'domain': 'cnn.com',
     'allow_live_comments': True,
     'selftext_html': None,
     'likes': None,
     'suggested_sort': None,
     'banned_at_utc': None,
     'view_count': None,
     'archived': False,
     'no_follow': False,
     'is_crosspostable': False,
     'pinned': False,
     'over_18': False,
     'all_awardings': [],
     'awarders': [],
     'media_only': False,
     'can_gild': False,
     'spoiler': False,
     'locked': False,
     'author_flair_text': None,
     'visited': False,
     'removed_by': None,
     'num_reports': None,
     'distinguished': None,
     'subreddit_id': 't5_2qh3l',
     'mod_reason_by': None,
     'removal_reason': None,
     'link_flair_background_color': '',
     'id': 'eczf1x',
     'is_robot_indexable': True,
     'report_reasons': None,
     'author': 'joesoldlegs',
     'discussion_type': None,
     'num_comments': 69,
     'send_replies': True,
     'whitelist_status': 'all_ads',
     'contest_mode': False,
     'mod_reports': [],
     'author_patreon_flair': False,
     'author_flair_text_color': None,
     'permalink': '/r/news/comments/eczf1x/a_7yearold_temporarily_living_in_a_domestic/',
     'parent_whitelist_status': 'all_ads',
     'stickied': False,
     'url': 'https://www.cnn.com/2019/12/18/us/shelter-santa-letter-trnd/index.html',
     'subreddit_subscribers': 19450961,
     'created_utc': 1576789562.0,
     'num_crossposts': 1,
     'media': None,
     'is_video': False}},
   {'kind': 't3',
    'data': {'approved_at_utc': None,
     'subreddit': 'news',
     'selftext': '',
     'author_fullname': 't2_7081u',
     'saved': False,
     'mod_reason_title': None,
     'gilded': 0,
     'clicked': False,
     'title': 'Police say armed robber who face-planted into Pizza Hut door left DNA',
     'link_flair_richtext': [],
     'subreddit_name_prefixed': 'r/news',
     'hidden': False,
     'pwls': 6,
     'link_flair_css_class': None,
     'downs': 0,
     'hide_score': False,
     'name': 't3_ecx5t1',
     'quarantine': False,
     'link_flair_text_color': 'dark',
     'author_flair_background_color': None,
     'subreddit_type': 'public',
     'ups': 408,
     'total_awards_received': 0,
     'media_embed': {},
     'author_flair_template_id': None,
     'is_original_content': False,
     'user_reports': [],
     'secure_media': None,
     'is_reddit_media_domain': False,
     'is_meta': False,
     'category': None,
     'secure_media_embed': {},
     'link_flair_text': None,
     'can_mod_post': False,
     'score': 408,
     'approved_by': None,
     'author_premium': False,
     'thumbnail': '',
     'edited': False,
     'author_flair_css_class': None,
     'steward_reports': [],
     'author_flair_richtext': [],
     'gildings': {},
     'content_categories': None,
     'is_self': False,
     'mod_note': None,
     'created': 1576808689.0,
     'link_flair_type': 'text',
     'wls': 6,
     'removed_by_category': None,
     'banned_by': None,
     'author_flair_type': 'text',
     'domain': 'lcsun-news.com',
     'allow_live_comments': True,
     'selftext_html': None,
     'likes': None,
     'suggested_sort': None,
     'banned_at_utc': None,
     'view_count': None,
     'archived': False,
     'no_follow': False,
     'is_crosspostable': False,
     'pinned': False,
     'over_18': False,
     'all_awardings': [],
     'awarders': [],
     'media_only': False,
     'can_gild': False,
     'spoiler': False,
     'locked': False,
     'author_flair_text': None,
     'visited': False,
     'removed_by': None,
     'num_reports': None,
     'distinguished': None,
     'subreddit_id': 't5_2qh3l',
     'mod_reason_by': None,
     'removal_reason': None,
     'link_flair_background_color': '',
     'id': 'ecx5t1',
     'is_robot_indexable': True,
     'report_reasons': None,
     'author': 'kellenbrent',
     'discussion_type': None,
     'num_comments': 33,
     'send_replies': True,
     'whitelist_status': 'all_ads',
     'contest_mode': False,
     'mod_reports': [],
     'author_patreon_flair': False,
     'author_flair_text_color': None,
     'permalink': '/r/news/comments/ecx5t1/police_say_armed_robber_who_faceplanted_into/',
     'parent_whitelist_status': 'all_ads',
     'stickied': False,
     'url': 'https://www.lcsun-news.com/story/news/local/2019/12/18/armed-robber-face-planted-into-pizza-hut-door-left-dna/2691962001/',
     'subreddit_subscribers': 19450961,
     'created_utc': 1576779889.0,
     'num_crossposts': 0,
     'media': None,
     'is_video': False}},
   {'kind': 't3',
    'data': {'approved_at_utc': None,
     'subreddit': 'news',
     'selftext': '',
     'author_fullname': 't2_3tigm902',
     'saved': False,
     'mod_reason_title': None,
     'gilded': 0,
     'clicked': False,
     'title': 'US Steel is closing a Detroit steel mill and laying off 1,500 workers',
     'link_flair_richtext': [],
     'subreddit_name_prefixed': 'r/news',
     'hidden': False,
     'pwls': 6,
     'link_flair_css_class': None,
     'downs': 0,
     'hide_score': False,
     'name': 't3_ed9vvx',
     'quarantine': False,
     'link_flair_text_color': 'dark',
     'author_flair_background_color': None,
     'subreddit_type': 'public',
     'ups': 456,
     'total_awards_received': 0,
     'media_embed': {},
     'author_flair_template_id': None,
     'is_original_content': False,
     'user_reports': [],
     'secure_media': None,
     'is_reddit_media_domain': False,
     'is_meta': False,
     'category': None,
     'secure_media_embed': {},
     'link_flair_text': None,
     'can_mod_post': False,
     'score': 456,
     'approved_by': None,
     'author_premium': True,
     'thumbnail': '',
     'edited': False,
     'author_flair_css_class': None,
     'steward_reports': [],
     'author_flair_richtext': [],
     'gildings': {},
     'content_categories': None,
     'is_self': False,
     'mod_note': None,
     'created': 1576875534.0,
     'link_flair_type': 'text',
     'wls': 6,
     'removed_by_category': None,
     'banned_by': None,
     'author_flair_type': 'text',
     'domain': 'cnn.com',
     'allow_live_comments': False,
     'selftext_html': None,
     'likes': None,
     'suggested_sort': None,
     'banned_at_utc': None,
     'view_count': None,
     'archived': False,
     'no_follow': False,
     'is_crosspostable': False,
     'pinned': False,
     'over_18': False,
     'all_awardings': [],
     'awarders': [],
     'media_only': False,
     'can_gild': False,
     'spoiler': False,
     'locked': False,
     'author_flair_text': None,
     'visited': False,
     'removed_by': None,
     'num_reports': None,
     'distinguished': None,
     'subreddit_id': 't5_2qh3l',
     'mod_reason_by': None,
     'removal_reason': None,
     'link_flair_background_color': '',
     'id': 'ed9vvx',
     'is_robot_indexable': True,
     'report_reasons': None,
     'author': 'PM_ME_YOUR_TREASON',
     'discussion_type': None,
     'num_comments': 139,
     'send_replies': True,
     'whitelist_status': 'all_ads',
     'contest_mode': False,
     'mod_reports': [],
     'author_patreon_flair': False,
     'author_flair_text_color': None,
     'permalink': '/r/news/comments/ed9vvx/us_steel_is_closing_a_detroit_steel_mill_and/',
     'parent_whitelist_status': 'all_ads',
     'stickied': False,
     'url': 'https://www.cnn.com/2019/12/20/business/us-steel-mill-closing/index.html?utm_source=feedburner&amp;utm_medium=feed&amp;utm_campaign=Feed%3A+rss%2Fcnn_latest+%28RSS%3A+CNN+-+Most+Recent%29',
     'subreddit_subscribers': 19450961,
     'created_utc': 1576846734.0,
     'num_crossposts': 1,
     'media': None,
     'is_video': False}},
   {'kind': 't3',
    'data': {'approved_at_utc': None,
     'subreddit': 'news',
     'selftext': '',
     'author_fullname': 't2_9oil3',
     'saved': False,
     'mod_reason_title': None,
     'gilded': 0,
     'clicked': False,
     'title': 'U.S. megachurch seeks $100K, prayers to ‘resurrect’ singer’s dead child',
     'link_flair_richtext': [],
     'subreddit_name_prefixed': 'r/news',
     'hidden': False,
     'pwls': 6,
     'link_flair_css_class': None,
     'downs': 0,
     'hide_score': False,
     'name': 't3_ed4l9q',
     'quarantine': False,
     'link_flair_text_color': 'dark',
     'author_flair_background_color': None,
     'subreddit_type': 'public',
     'ups': 396,
     'total_awards_received': 0,
     'media_embed': {},
     'author_flair_template_id': None,
     'is_original_content': False,
     'user_reports': [],
     'secure_media': None,
     'is_reddit_media_domain': False,
     'is_meta': False,
     'category': None,
     'secure_media_embed': {},
     'link_flair_text': None,
     'can_mod_post': False,
     'score': 396,
     'approved_by': None,
     'author_premium': False,
     'thumbnail': '',
     'edited': False,
     'author_flair_css_class': None,
     'steward_reports': [],
     'author_flair_richtext': [],
     'gildings': {},
     'content_categories': None,
     'is_self': False,
     'mod_note': None,
     'created': 1576842502.0,
     'link_flair_type': 'text',
     'wls': 6,
     'removed_by_category': None,
     'banned_by': None,
     'author_flair_type': 'text',
     'domain': 'globalnews.ca',
     'allow_live_comments': True,
     'selftext_html': None,
     'likes': None,
     'suggested_sort': None,
     'banned_at_utc': None,
     'view_count': None,
     'archived': False,
     'no_follow': False,
     'is_crosspostable': False,
     'pinned': False,
     'over_18': False,
     'all_awardings': [],
     'awarders': [],
     'media_only': False,
     'can_gild': False,
     'spoiler': False,
     'locked': False,
     'author_flair_text': None,
     'visited': False,
     'removed_by': None,
     'num_reports': None,
     'distinguished': None,
     'subreddit_id': 't5_2qh3l',
     'mod_reason_by': None,
     'removal_reason': None,
     'link_flair_background_color': '',
     'id': 'ed4l9q',
     'is_robot_indexable': True,
     'report_reasons': None,
     'author': 'uncoverthenews',
     'discussion_type': None,
     'num_comments': 99,
     'send_replies': True,
     'whitelist_status': 'all_ads',
     'contest_mode': False,
     'mod_reports': [],
     'author_patreon_flair': False,
     'author_flair_text_color': None,
     'permalink': '/r/news/comments/ed4l9q/us_megachurch_seeks_100k_prayers_to_resurrect/',
     'parent_whitelist_status': 'all_ads',
     'stickied': False,
     'url': 'https://globalnews.ca/news/6317020/kalley-heiligenthal-daughter-olive-resurrection/',
     'subreddit_subscribers': 19450961,
     'created_utc': 1576813702.0,
     'num_crossposts': 3,
     'media': None,
     'is_video': False}},
   {'kind': 't3',
    'data': {'approved_at_utc': None,
     'subreddit': 'news',
     'selftext': '',
     'author_fullname': 't2_73wku',
     'saved': False,
     'mod_reason_title': None,
     'gilded': 0,
     'clicked': False,
     'title': 'Reusable bamboo mugs leach dangerous amounts of formaldehyde and melamine',
     'link_flair_richtext': [],
     'subreddit_name_prefixed': 'r/news',
     'hidden': False,
     'pwls': 6,
     'link_flair_css_class': None,
     'downs': 0,
     'hide_score': False,
     'name': 't3_edahjb',
     'quarantine': False,
     'link_flair_text_color': 'dark',
     'author_flair_background_color': None,
     'subreddit_type': 'public',
     'ups': 404,
     'total_awards_received': 0,
     'media_embed': {},
     'author_flair_template_id': None,
     'is_original_content': False,
     'user_reports': [],
     'secure_media': None,
     'is_reddit_media_domain': False,
     'is_meta': False,
     'category': None,
     'secure_media_embed': {},
     'link_flair_text': None,
     'can_mod_post': False,
     'score': 404,
     'approved_by': None,
     'author_premium': False,
     'thumbnail': '',
     'edited': False,
     'author_flair_css_class': None,
     'steward_reports': [],
     'author_flair_richtext': [],
     'gildings': {},
     'content_categories': None,
     'is_self': False,
     'mod_note': None,
     'created': 1576878869.0,
     'link_flair_type': 'text',
     'wls': 6,
     'removed_by_category': None,
     'banned_by': None,
     'author_flair_type': 'text',
     'domain': 'chemistryworld.com',
     'allow_live_comments': True,
     'selftext_html': None,
     'likes': None,
     'suggested_sort': None,
     'banned_at_utc': None,
     'view_count': None,
     'archived': False,
     'no_follow': False,
     'is_crosspostable': False,
     'pinned': False,
     'over_18': False,
     'all_awardings': [],
     'awarders': [],
     'media_only': False,
     'can_gild': False,
     'spoiler': False,
     'locked': False,
     'author_flair_text': None,
     'visited': False,
     'removed_by': None,
     'num_reports': None,
     'distinguished': None,
     'subreddit_id': 't5_2qh3l',
     'mod_reason_by': None,
     'removal_reason': None,
     'link_flair_background_color': '',
     'id': 'edahjb',
     'is_robot_indexable': True,
     'report_reasons': None,
     'author': 'razeal113',
     'discussion_type': None,
     'num_comments': 47,
     'send_replies': False,
     'whitelist_status': 'all_ads',
     'contest_mode': False,
     'mod_reports': [],
     'author_patreon_flair': False,
     'author_flair_text_color': None,
     'permalink': '/r/news/comments/edahjb/reusable_bamboo_mugs_leach_dangerous_amounts_of/',
     'parent_whitelist_status': 'all_ads',
     'stickied': False,
     'url': 'https://www.chemistryworld.com/news/reusable-bamboo-mugs-leach-dangerous-amounts-of-formaldehyde-and-melamine/4010950.article?fbclid=IwAR2k1kKB4oSXinoz0ZQBe6h58KAYFWrq3_adWgWhjbxXICtUj6zOtEWaki4',
     'subreddit_subscribers': 19450961,
     'created_utc': 1576850069.0,
     'num_crossposts': 0,
     'media': None,
     'is_video': False}},
   {'kind': 't3',
    'data': {'approved_at_utc': None,
     'subreddit': 'news',
     'selftext': '',
     'author_fullname': 't2_fn8i1',
     'saved': False,
     'mod_reason_title': None,
     'gilded': 0,
     'clicked': False,
     'title': 'Detroit-area mayor, others charged in fraud, bribery scheme',
     'link_flair_richtext': [],
     'subreddit_name_prefixed': 'r/news',
     'hidden': False,
     'pwls': 6,
     'link_flair_css_class': None,
     'downs': 0,
     'hide_score': False,
     'name': 't3_ed4gvn',
     'quarantine': False,
     'link_flair_text_color': 'dark',
     'author_flair_background_color': None,
     'subreddit_type': 'public',
     'ups': 174,
     'total_awards_received': 0,
     'media_embed': {},
     'author_flair_template_id': None,
     'is_original_content': False,
     'user_reports': [],
     'secure_media': None,
     'is_reddit_media_domain': False,
     'is_meta': False,
     'category': None,
     'secure_media_embed': {},
     'link_flair_text': None,
     'can_mod_post': False,
     'score': 174,
     'approved_by': None,
     'author_premium': True,
     'thumbnail': '',
     'edited': False,
     'author_flair_css_class': None,
     'steward_reports': [],
     'author_flair_richtext': [],
     'gildings': {},
     'content_categories': None,
     'is_self': False,
     'mod_note': None,
     'created': 1576841850.0,
     'link_flair_type': 'text',
     'wls': 6,
     'removed_by_category': None,
     'banned_by': None,
     'author_flair_type': 'text',
     'domain': 'apnews.com',
     'allow_live_comments': True,
     'selftext_html': None,
     'likes': None,
     'suggested_sort': None,
     'banned_at_utc': None,
     'view_count': None,
     'archived': False,
     'no_follow': False,
     'is_crosspostable': False,
     'pinned': False,
     'over_18': False,
     'all_awardings': [],
     'awarders': [],
     'media_only': False,
     'can_gild': False,
     'spoiler': False,
     'locked': False,
     'author_flair_text': None,
     'visited': False,
     'removed_by': None,
     'num_reports': None,
     'distinguished': None,
     'subreddit_id': 't5_2qh3l',
     'mod_reason_by': None,
     'removal_reason': None,
     'link_flair_background_color': '',
     'id': 'ed4gvn',
     'is_robot_indexable': True,
     'report_reasons': None,
     'author': 'pantangeli',
     'discussion_type': None,
     'num_comments': 17,
     'send_replies': True,
     'whitelist_status': 'all_ads',
     'contest_mode': False,
     'mod_reports': [],
     'author_patreon_flair': False,
     'author_flair_text_color': None,
     'permalink': '/r/news/comments/ed4gvn/detroitarea_mayor_others_charged_in_fraud_bribery/',
     'parent_whitelist_status': 'all_ads',
     'stickied': False,
     'url': 'https://apnews.com/1b5a1f2663353d59a409512097181db1',
     'subreddit_subscribers': 19450961,
     'created_utc': 1576813050.0,
     'num_crossposts': 0,
     'media': None,
     'is_video': False}},
   {'kind': 't3',
    'data': {'approved_at_utc': None,
     'subreddit': 'news',
     'selftext': '',
     'author_fullname': 't2_7ueps',
     'saved': False,
     'mod_reason_title': None,
     'gilded': 0,
     'clicked': False,
     'title': 'Coke targeted teens by saying sugary drinks are healthy',
     'link_flair_richtext': [],
     'subreddit_name_prefixed': 'r/news',
     'hidden': False,
     'pwls': 6,
     'link_flair_css_class': None,
     'downs': 0,
     'hide_score': False,
     'name': 't3_ecxq51',
     'quarantine': False,
     'link_flair_text_color': 'dark',
     'author_flair_background_color': None,
     'subreddit_type': 'public',
     'ups': 146,
     'total_awards_received': 0,
     'media_embed': {},
     'author_flair_template_id': None,
     'is_original_content': False,
     'user_reports': [],
     'secure_media': None,
     'is_reddit_media_domain': False,
     'is_meta': False,
     'category': None,
     'secure_media_embed': {},
     'link_flair_text': None,
     'can_mod_post': False,
     'score': 146,
     'approved_by': None,
     'author_premium': True,
     'thumbnail': '',
     'edited': False,
     'author_flair_css_class': None,
     'steward_reports': [],
     'author_flair_richtext': [],
     'gildings': {},
     'content_categories': None,
     'is_self': False,
     'mod_note': None,
     'created': 1576811120.0,
     'link_flair_type': 'text',
     'wls': 6,
     'removed_by_category': None,
     'banned_by': None,
     'author_flair_type': 'text',
     'domain': 'cnn.com',
     'allow_live_comments': True,
     'selftext_html': None,
     'likes': None,
     'suggested_sort': None,
     'banned_at_utc': None,
     'view_count': None,
     'archived': False,
     'no_follow': False,
     'is_crosspostable': False,
     'pinned': False,
     'over_18': False,
     'all_awardings': [],
     'awarders': [],
     'media_only': False,
     'can_gild': False,
     'spoiler': False,
     'locked': False,
     'author_flair_text': None,
     'visited': False,
     'removed_by': None,
     'num_reports': None,
     'distinguished': None,
     'subreddit_id': 't5_2qh3l',
     'mod_reason_by': None,
     'removal_reason': None,
     'link_flair_background_color': '',
     'id': 'ecxq51',
     'is_robot_indexable': True,
     'report_reasons': None,
     'author': 'thenewyorkgod',
     'discussion_type': None,
     'num_comments': 72,
     'send_replies': True,
     'whitelist_status': 'all_ads',
     'contest_mode': False,
     'mod_reports': [],
     'author_patreon_flair': False,
     'author_flair_text_color': None,
     'permalink': '/r/news/comments/ecxq51/coke_targeted_teens_by_saying_sugary_drinks_are/',
     'parent_whitelist_status': 'all_ads',
     'stickied': False,
     'url': 'https://www.cnn.com/2019/12/19/business/coca-cola-teens-advertisement-trnd/index.html?utm_content=2019-12-19T19%3A00%3A09&amp;utm_source=twCNN&amp;utm_medium=social&amp;utm_term=link',
     'subreddit_subscribers': 19450961,
     'created_utc': 1576782320.0,
     'num_crossposts': 1,
     'media': None,
     'is_video': False}},
   {'kind': 't3',
    'data': {'approved_at_utc': None,
     'subreddit': 'news',
     'selftext': '',
     'author_fullname': 't2_fn8i1',
     'saved': False,
     'mod_reason_title': None,
     'gilded': 0,
     'clicked': False,
     'title': 'Twitter removes 5,929 Saudi accounts it deems state backed',
     'link_flair_richtext': [],
     'subreddit_name_prefixed': 'r/news',
     'hidden': False,
     'pwls': 6,
     'link_flair_css_class': None,
     'downs': 0,
     'hide_score': False,
     'name': 't3_edc49t',
     'quarantine': False,
     'link_flair_text_color': 'dark',
     'author_flair_background_color': None,
     'subreddit_type': 'public',
     'ups': 197,
     'total_awards_received': 0,
     'media_embed': {},
     'author_flair_template_id': None,
     'is_original_content': False,
     'user_reports': [],
     'secure_media': None,
     'is_reddit_media_domain': False,
     'is_meta': False,
     'category': None,
     'secure_media_embed': {},
     'link_flair_text': None,
     'can_mod_post': False,
     'score': 197,
     'approved_by': None,
     'author_premium': True,
     'thumbnail': '',
     'edited': False,
     'author_flair_css_class': None,
     'steward_reports': [],
     'author_flair_richtext': [],
     'gildings': {},
     'content_categories': None,
     'is_self': False,
     'mod_note': None,
     'created': 1576886878.0,
     'link_flair_type': 'text',
     'wls': 3,
     'removed_by_category': None,
     'banned_by': None,
     'author_flair_type': 'text',
     'domain': 'apnews.com',
     'allow_live_comments': False,
     'selftext_html': None,
     'likes': None,
     'suggested_sort': None,
     'banned_at_utc': None,
     'view_count': None,
     'archived': False,
     'no_follow': False,
     'is_crosspostable': False,
     'pinned': False,
     'over_18': True,
     'all_awardings': [],
     'awarders': [],
     'media_only': False,
     'can_gild': False,
     'spoiler': False,
     'locked': False,
     'author_flair_text': None,
     'visited': False,
     'removed_by': None,
     'num_reports': None,
     'distinguished': None,
     'subreddit_id': 't5_2qh3l',
     'mod_reason_by': None,
     'removal_reason': None,
     'link_flair_background_color': '',
     'id': 'edc49t',
     'is_robot_indexable': True,
     'report_reasons': None,
     'author': 'pantangeli',
     'discussion_type': None,
     'num_comments': 15,
     'send_replies': True,
     'whitelist_status': 'promo_adult_nsfw',
     'contest_mode': False,
     'mod_reports': [],
     'author_patreon_flair': False,
     'author_flair_text_color': None,
     'permalink': '/r/news/comments/edc49t/twitter_removes_5929_saudi_accounts_it_deems/',
     'parent_whitelist_status': 'all_ads',
     'stickied': False,
     'url': 'https://apnews.com/7c9fd798212cac63925d205142e811ea',
     'subreddit_subscribers': 19450961,
     'created_utc': 1576858078.0,
     'num_crossposts': 0,
     'media': None,
     'is_video': False}},
   {'kind': 't3',
    'data': {'approved_at_utc': None,
     'subreddit': 'news',
     'selftext': '',
     'author_fullname': 't2_fn8i1',
     'saved': False,
     'mod_reason_title': None,
     'gilded': 0,
     'clicked': False,
     'title': 'Virginia attorney charged in extortion plot over Roundup',
     'link_flair_richtext': [],
     'subreddit_name_prefixed': 'r/news',
     'hidden': False,
     'pwls': 6,
     'link_flair_css_class': None,
     'downs': 0,
     'hide_score': False,
     'name': 't3_eczhih',
     'quarantine': False,
     'link_flair_text_color': 'dark',
     'author_flair_background_color': None,
     'subreddit_type': 'public',
     'ups': 134,
     'total_awards_received': 0,
     'media_embed': {},
     'author_flair_template_id': None,
     'is_original_content': False,
     'user_reports': [],
     'secure_media': None,
     'is_reddit_media_domain': False,
     'is_meta': False,
     'category': None,
     'secure_media_embed': {},
     'link_flair_text': None,
     'can_mod_post': False,
     'score': 134,
     'approved_by': None,
     'author_premium': True,
     'thumbnail': '',
     'edited': False,
     'author_flair_css_class': None,
     'steward_reports': [],
     'author_flair_richtext': [],
     'gildings': {},
     'content_categories': None,
     'is_self': False,
     'mod_note': None,
     'created': 1576818648.0,
     'link_flair_type': 'text',
     'wls': 6,
     'removed_by_category': None,
     'banned_by': None,
     'author_flair_type': 'text',
     'domain': 'apnews.com',
     'allow_live_comments': False,
     'selftext_html': None,
     'likes': None,
     'suggested_sort': None,
     'banned_at_utc': None,
     'view_count': None,
     'archived': False,
     'no_follow': False,
     'is_crosspostable': False,
     'pinned': False,
     'over_18': False,
     'all_awardings': [],
     'awarders': [],
     'media_only': False,
     'can_gild': False,
     'spoiler': False,
     'locked': False,
     'author_flair_text': None,
     'visited': False,
     'removed_by': None,
     'num_reports': None,
     'distinguished': None,
     'subreddit_id': 't5_2qh3l',
     'mod_reason_by': None,
     'removal_reason': None,
     'link_flair_background_color': '',
     'id': 'eczhih',
     'is_robot_indexable': True,
     'report_reasons': None,
     'author': 'pantangeli',
     'discussion_type': None,
     'num_comments': 3,
     'send_replies': True,
     'whitelist_status': 'all_ads',
     'contest_mode': False,
     'mod_reports': [],
     'author_patreon_flair': False,
     'author_flair_text_color': None,
     'permalink': '/r/news/comments/eczhih/virginia_attorney_charged_in_extortion_plot_over/',
     'parent_whitelist_status': 'all_ads',
     'stickied': False,
     'url': 'https://apnews.com/ac2c00fd7a4d0ea36888ac8a39dd32ac',
     'subreddit_subscribers': 19450961,
     'created_utc': 1576789848.0,
     'num_crossposts': 0,
     'media': None,
     'is_video': False}},
   {'kind': 't3',
    'data': {'approved_at_utc': None,
     'subreddit': 'news',
     'selftext': '',
     'author_fullname': 't2_u190q',
     'saved': False,
     'mod_reason_title': None,
     'gilded': 0,
     'clicked': False,
     'title': 'Missing Texas mom Heidi Broussard found dead, newborn alive, family says.',
     'link_flair_richtext': [],
     'subreddit_name_prefixed': 'r/news',
     'hidden': False,
     'pwls': 6,
     'link_flair_css_class': None,
     'downs': 0,
     'hide_score': False,
     'name': 't3_eda4hg',
     'quarantine': False,
     'link_flair_text_color': 'dark',
     'author_flair_background_color': None,
     'subreddit_type': 'public',
     'ups': 151,
     'total_awards_received': 0,
     'media_embed': {},
     'author_flair_template_id': None,
     'is_original_content': False,
     'user_reports': [],
     'secure_media': None,
     'is_reddit_media_domain': False,
     'is_meta': False,
     'category': None,
     'secure_media_embed': {},
     'link_flair_text': None,
     'can_mod_post': False,
     'score': 151,
     'approved_by': None,
     'author_premium': False,
     'thumbnail': '',
     'edited': False,
     'author_flair_css_class': None,
     'steward_reports': [],
     'author_flair_richtext': [],
     'gildings': {},
     'content_categories': None,
     'is_self': False,
     'mod_note': None,
     'created': 1576876876.0,
     'link_flair_type': 'text',
     'wls': 6,
     'removed_by_category': None,
     'banned_by': None,
     'author_flair_type': 'text',
     'domain': 'nbcnews.com',
     'allow_live_comments': False,
     'selftext_html': None,
     'likes': None,
     'suggested_sort': None,
     'banned_at_utc': None,
     'view_count': None,
     'archived': False,
     'no_follow': False,
     'is_crosspostable': False,
     'pinned': False,
     'over_18': False,
     'all_awardings': [],
     'awarders': [],
     'media_only': False,
     'can_gild': False,
     'spoiler': False,
     'locked': False,
     'author_flair_text': None,
     'visited': False,
     'removed_by': None,
     'num_reports': None,
     'distinguished': None,
     'subreddit_id': 't5_2qh3l',
     'mod_reason_by': None,
     'removal_reason': None,
     'link_flair_background_color': '',
     'id': 'eda4hg',
     'is_robot_indexable': True,
     'report_reasons': None,
     'author': 'phileo56',
     'discussion_type': None,
     'num_comments': 49,
     'send_replies': True,
     'whitelist_status': 'all_ads',
     'contest_mode': False,
     'mod_reports': [],
     'author_patreon_flair': False,
     'author_flair_text_color': None,
     'permalink': '/r/news/comments/eda4hg/missing_texas_mom_heidi_broussard_found_dead/',
     'parent_whitelist_status': 'all_ads',
     'stickied': False,
     'url': 'https://www.nbcnews.com/news/us-news/baby-found-safe-houston-area-home-where-police-reported-be-n1105586',
     'subreddit_subscribers': 19450961,
     'created_utc': 1576848076.0,
     'num_crossposts': 0,
     'media': None,
     'is_video': False}},
   {'kind': 't3',
    'data': {'approved_at_utc': None,
     'subreddit': 'news',
     'selftext': '',
     'author_fullname': 't2_xb5de',
     'saved': False,
     'mod_reason_title': None,
     'gilded': 0,
     'clicked': False,
     'title': 'Utah woman had biological weapon of mass destruction, police say',
     'link_flair_richtext': [],
     'subreddit_name_prefixed': 'r/news',
     'hidden': False,
     'pwls': 6,
     'link_flair_css_class': None,
     'downs': 0,
     'hide_score': False,
     'name': 't3_ed4jdr',
     'quarantine': False,
     'link_flair_text_color': 'dark',
     'author_flair_background_color': None,
     'subreddit_type': 'public',
     'ups': 118,
     'total_awards_received': 0,
     'media_embed': {},
     'author_flair_template_id': None,
     'is_original_content': False,
     'user_reports': [],
     'secure_media': None,
     'is_reddit_media_domain': False,
     'is_meta': False,
     'category': None,
     'secure_media_embed': {},
     'link_flair_text': None,
     'can_mod_post': False,
     'score': 118,
     'approved_by': None,
     'author_premium': False,
     'thumbnail': '',
     'edited': False,
     'author_flair_css_class': None,
     'steward_reports': [],
     'author_flair_richtext': [],
     'gildings': {},
     'content_categories': None,
     'is_self': False,
     'mod_note': None,
     'created': 1576842220.0,
     'link_flair_type': 'text',
     'wls': 6,
     'removed_by_category': None,
     'banned_by': None,
     'author_flair_type': 'text',
     'domain': 'deseret.com',
     'allow_live_comments': True,
     'selftext_html': None,
     'likes': None,
     'suggested_sort': None,
     'banned_at_utc': None,
     'view_count': None,
     'archived': False,
     'no_follow': False,
     'is_crosspostable': False,
     'pinned': False,
     'over_18': False,
     'all_awardings': [],
     'awarders': [],
     'media_only': False,
     'can_gild': False,
     'spoiler': False,
     'locked': False,
     'author_flair_text': None,
     'visited': False,
     'removed_by': None,
     'num_reports': None,
     'distinguished': None,
     'subreddit_id': 't5_2qh3l',
     'mod_reason_by': None,
     'removal_reason': None,
     'link_flair_background_color': '',
     'id': 'ed4jdr',
     'is_robot_indexable': True,
     'report_reasons': None,
     'author': 'zrk03',
     'discussion_type': None,
     'num_comments': 40,
     'send_replies': True,
     'whitelist_status': 'all_ads',
     'contest_mode': False,
     'mod_reports': [],
     'author_patreon_flair': False,
     'author_flair_text_color': None,
     'permalink': '/r/news/comments/ed4jdr/utah_woman_had_biological_weapon_of_mass/',
     'parent_whitelist_status': 'all_ads',
     'stickied': False,
     'url': 'https://www.deseret.com/utah/2019/12/19/21030713/utah-woman-biological-weapon-mass-destruction-police-fbi',
     'subreddit_subscribers': 19450961,
     'created_utc': 1576813420.0,
     'num_crossposts': 0,
     'media': None,
     'is_video': False}},
   {'kind': 't3',
    'data': {'approved_at_utc': None,
     'subreddit': 'news',
     'selftext': '',
     'author_fullname': 't2_2ixyyl0k',
     'saved': False,
     'mod_reason_title': None,
     'gilded': 0,
     'clicked': False,
     'title': 'Virginia biochemist crowned Miss America 2020',
     'link_flair_richtext': [],
     'subreddit_name_prefixed': 'r/news',
     'hidden': False,
     'pwls': 6,
     'link_flair_css_class': None,
     'downs': 0,
     'hide_score': False,
     'name': 't3_edaqdv',
     'quarantine': False,
     'link_flair_text_color': 'dark',
     'author_flair_background_color': None,
     'subreddit_type': 'public',
     'ups': 135,
     'total_awards_received': 0,
     'media_embed': {},
     'author_flair_template_id': None,
     'is_original_content': False,
     'user_reports': [],
     'secure_media': None,
     'is_reddit_media_domain': False,
     'is_meta': False,
     'category': None,
     'secure_media_embed': {},
     'link_flair_text': None,
     'can_mod_post': False,
     'score': 135,
     'approved_by': None,
     'author_premium': True,
     'thumbnail': '',
     'edited': False,
     'author_flair_css_class': None,
     'steward_reports': [],
     'author_flair_richtext': [],
     'gildings': {},
     'content_categories': None,
     'is_self': False,
     'mod_note': None,
     'created': 1576880102.0,
     'link_flair_type': 'text',
     'wls': 6,
     'removed_by_category': None,
     'banned_by': None,
     'author_flair_type': 'text',
     'domain': 'bbc.com',
     'allow_live_comments': False,
     'selftext_html': None,
     'likes': None,
     'suggested_sort': None,
     'banned_at_utc': None,
     'view_count': None,
     'archived': False,
     'no_follow': False,
     'is_crosspostable': False,
     'pinned': False,
     'over_18': False,
     'all_awardings': [],
     'awarders': [],
     'media_only': False,
     'can_gild': False,
     'spoiler': False,
     'locked': False,
     'author_flair_text': None,
     'visited': False,
     'removed_by': None,
     'num_reports': None,
     'distinguished': None,
     'subreddit_id': 't5_2qh3l',
     'mod_reason_by': None,
     'removal_reason': None,
     'link_flair_background_color': '',
     'id': 'edaqdv',
     'is_robot_indexable': True,
     'report_reasons': None,
     'author': 'Al-Andalusia',
     'discussion_type': None,
     'num_comments': 17,
     'send_replies': True,
     'whitelist_status': 'all_ads',
     'contest_mode': False,
     'mod_reports': [],
     'author_patreon_flair': False,
     'author_flair_text_color': None,
     'permalink': '/r/news/comments/edaqdv/virginia_biochemist_crowned_miss_america_2020/',
     'parent_whitelist_status': 'all_ads',
     'stickied': False,
     'url': 'https://www.bbc.com/news/world-us-canada-50862408',
     'subreddit_subscribers': 19450961,
     'created_utc': 1576851302.0,
     'num_crossposts': 0,
     'media': None,
     'is_video': False}},
   {'kind': 't3',
    'data': {'approved_at_utc': None,
     'subreddit': 'news',
     'selftext': '',
     'author_fullname': 't2_4o6djbd3',
     'saved': False,
     'mod_reason_title': None,
     'gilded': 0,
     'clicked': False,
     'title': 'Google fined 150 million euros by France',
     'link_flair_richtext': [],
     'subreddit_name_prefixed': 'r/news',
     'hidden': False,
     'pwls': 6,
     'link_flair_css_class': None,
     'downs': 0,
     'hide_score': False,
     'name': 't3_ed8uc5',
     'quarantine': False,
     'link_flair_text_color': 'dark',
     'author_flair_background_color': None,
     'subreddit_type': 'public',
     'ups': 109,
     'total_awards_received': 0,
     'media_embed': {},
     'author_flair_template_id': None,
     'is_original_content': False,
     'user_reports': [],
     'secure_media': None,
     'is_reddit_media_domain': False,
     'is_meta': False,
     'category': None,
     'secure_media_embed': {},
     'link_flair_text': None,
     'can_mod_post': False,
     'score': 109,
     'approved_by': None,
     'author_premium': False,
     'thumbnail': '',
     'edited': False,
     'author_flair_css_class': None,
     'steward_reports': [],
     'author_flair_richtext': [],
     'gildings': {},
     'content_categories': None,
     'is_self': False,
     'mod_note': None,
     'created': 1576869281.0,
     'link_flair_type': 'text',
     'wls': 6,
     'removed_by_category': None,
     'banned_by': None,
     'author_flair_type': 'text',
     'domain': 'cnbc.com',
     'allow_live_comments': False,
     'selftext_html': None,
     'likes': None,
     'suggested_sort': None,
     'banned_at_utc': None,
     'view_count': None,
     'archived': False,
     'no_follow': False,
     'is_crosspostable': False,
     'pinned': False,
     'over_18': False,
     'all_awardings': [],
     'awarders': [],
     'media_only': False,
     'can_gild': False,
     'spoiler': False,
     'locked': False,
     'author_flair_text': None,
     'visited': False,
     'removed_by': None,
     'num_reports': None,
     'distinguished': None,
     'subreddit_id': 't5_2qh3l',
     'mod_reason_by': None,
     'removal_reason': None,
     'link_flair_background_color': '',
     'id': 'ed8uc5',
     'is_robot_indexable': True,
     'report_reasons': None,
     'author': 'TysonYoungNp',
     'discussion_type': None,
     'num_comments': 17,
     'send_replies': True,
     'whitelist_status': 'all_ads',
     'contest_mode': False,
     'mod_reports': [],
     'author_patreon_flair': False,
     'author_flair_text_color': None,
     'permalink': '/r/news/comments/ed8uc5/google_fined_150_million_euros_by_france/',
     'parent_whitelist_status': 'all_ads',
     'stickied': False,
     'url': 'https://www.cnbc.com/2019/12/20/google-fined-150-million-euros-by-france.html',
     'subreddit_subscribers': 19450961,
     'created_utc': 1576840481.0,
     'num_crossposts': 1,
     'media': None,
     'is_video': False}},
   {'kind': 't3',
    'data': {'approved_at_utc': None,
     'subreddit': 'news',
     'selftext': '',
     'author_fullname': 't2_fn8i1',
     'saved': False,
     'mod_reason_title': None,
     'gilded': 0,
     'clicked': False,
     'title': 'Medical marijuana headed to the 2020 ballot in South Dakota',
     'link_flair_richtext': [],
     'subreddit_name_prefixed': 'r/news',
     'hidden': False,
     'pwls': 6,
     'link_flair_css_class': None,
     'downs': 0,
     'hide_score': False,
     'name': 't3_edb7ch',
     'quarantine': False,
     'link_flair_text_color': 'dark',
     'author_flair_background_color': None,
     'subreddit_type': 'public',
     'ups': 136,
     'total_awards_received': 0,
     'media_embed': {},
     'author_flair_template_id': None,
     'is_original_content': False,
     'user_reports': [],
     'secure_media': None,
     'is_reddit_media_domain': False,
     'is_meta': False,
     'category': None,
     'secure_media_embed': {},
     'link_flair_text': None,
     'can_mod_post': False,
     'score': 136,
     'approved_by': None,
     'author_premium': True,
     'thumbnail': '',
     'edited': False,
     'author_flair_css_class': None,
     'steward_reports': [],
     'author_flair_richtext': [],
     'gildings': {},
     'content_categories': None,
     'is_self': False,
     'mod_note': None,
     'created': 1576882513.0,
     'link_flair_type': 'text',
     'wls': 6,
     'removed_by_category': None,
     'banned_by': None,
     'author_flair_type': 'text',
     'domain': 'kelo.com',
     'allow_live_comments': False,
     'selftext_html': None,
     'likes': None,
     'suggested_sort': None,
     'banned_at_utc': None,
     'view_count': None,
     'archived': False,
     'no_follow': False,
     'is_crosspostable': False,
     'pinned': False,
     'over_18': False,
     'all_awardings': [],
     'awarders': [],
     'media_only': False,
     'can_gild': False,
     'spoiler': False,
     'locked': False,
     'author_flair_text': None,
     'visited': False,
     'removed_by': None,
     'num_reports': None,
     'distinguished': None,
     'subreddit_id': 't5_2qh3l',
     'mod_reason_by': None,
     'removal_reason': None,
     'link_flair_background_color': '',
     'id': 'edb7ch',
     'is_robot_indexable': True,
     'report_reasons': None,
     'author': 'pantangeli',
     'discussion_type': None,
     'num_comments': 25,
     'send_replies': True,
     'whitelist_status': 'all_ads',
     'contest_mode': False,
     'mod_reports': [],
     'author_patreon_flair': False,
     'author_flair_text_color': None,
     'permalink': '/r/news/comments/edb7ch/medical_marijuana_headed_to_the_2020_ballot_in/',
     'parent_whitelist_status': 'all_ads',
     'stickied': False,
     'url': 'https://kelo.com/news/articles/2019/dec/20/medical-marijuana-headed-to-the-2020-ballot-in-south-dakota/968290/',
     'subreddit_subscribers': 19450961,
     'created_utc': 1576853713.0,
     'num_crossposts': 0,
     'media': None,
     'is_video': False}}],
  'after': 't3_edb7ch',
  'before': 't3_ed9nm2'}}

Too many stories to handle? Well the API allows you to include a query string parameter for story limit. This example shows you can combine the headers and params named arguments if your API requires it. This is true for any function call with named arguments, BTW.

NOTE: 'Accept' : 'application/json' in the header asks the API to return the respose as json instead of XML.

In this example we return just one 1 story!

In [29]:
web_api_url = 'https://www.reddit.com/r/news/top.json'
custom_headers = {'User-Agent' : 'sample-python-application', 'Accept' : 'application/json'}
options = { 'limit' : 1 }
response = requests.get(web_api_url, headers = custom_headers, params = options)
response.json() 
Out[29]:
{'kind': 'Listing',
 'data': {'modhash': '',
  'dist': 1,
  'children': [{'kind': 't3',
    'data': {'approved_at_utc': None,
     'subreddit': 'news',
     'selftext': '',
     'author_fullname': 't2_1oogh4ln',
     'saved': False,
     'mod_reason_title': None,
     'gilded': 0,
     'clicked': False,
     'title': 'A vegan couple have been charged with first-degree murder after their 18-month-old son starved to death on a diet of only raw fruit and vegetables',
     'link_flair_richtext': [],
     'subreddit_name_prefixed': 'r/news',
     'hidden': False,
     'pwls': 6,
     'link_flair_css_class': None,
     'downs': 0,
     'hide_score': False,
     'name': 't3_ed9nm2',
     'quarantine': False,
     'link_flair_text_color': 'dark',
     'author_flair_background_color': None,
     'subreddit_type': 'public',
     'ups': 60380,
     'total_awards_received': 5,
     'media_embed': {},
     'author_flair_template_id': None,
     'is_original_content': False,
     'user_reports': [],
     'secure_media': None,
     'is_reddit_media_domain': False,
     'is_meta': False,
     'category': None,
     'secure_media_embed': {},
     'link_flair_text': None,
     'can_mod_post': False,
     'score': 60380,
     'approved_by': None,
     'author_premium': True,
     'thumbnail': '',
     'edited': False,
     'author_flair_css_class': None,
     'steward_reports': [],
     'author_flair_richtext': [],
     'gildings': {'gid_1': 4},
     'content_categories': None,
     'is_self': False,
     'mod_note': None,
     'created': 1576874246.0,
     'link_flair_type': 'text',
     'wls': 6,
     'removed_by_category': None,
     'banned_by': None,
     'author_flair_type': 'text',
     'domain': 'news.sky.com',
     'allow_live_comments': True,
     'selftext_html': None,
     'likes': None,
     'suggested_sort': None,
     'banned_at_utc': None,
     'view_count': None,
     'archived': False,
     'no_follow': False,
     'is_crosspostable': False,
     'pinned': False,
     'over_18': False,
     'all_awardings': [{'count': 1,
       'is_enabled': True,
       'subreddit_id': None,
       'description': 'Gives the author a week of Reddit Premium and %{coin_symbol}100 Coins to do with as they please.',
       'end_date': 1578672000,
       'award_sub_type': 'GLOBAL',
       'coin_reward': 100,
       'icon_url': 'https://i.redd.it/award_images/t5_22cerq/x8zqi9ynmh541_BabySnooTears.png',
       'days_of_premium': 7,
       'is_new': False,
       'id': 'award_aadfc1d7-d4b3-429e-bc71-61876e465ea2',
       'icon_height': 2048,
       'resized_icons': [{'url': 'https://preview.redd.it/award_images/t5_22cerq/x8zqi9ynmh541_BabySnooTears.png?width=16&amp;height=16&amp;auto=webp&amp;s=c9a1a2d9f0d2bd30810e8df3e5678d04cfd48eb7',
         'width': 16,
         'height': 16},
        {'url': 'https://preview.redd.it/award_images/t5_22cerq/x8zqi9ynmh541_BabySnooTears.png?width=32&amp;height=32&amp;auto=webp&amp;s=1bb43511d3fc25cb4381a32664a26716116a9664',
         'width': 32,
         'height': 32},
        {'url': 'https://preview.redd.it/award_images/t5_22cerq/x8zqi9ynmh541_BabySnooTears.png?width=48&amp;height=48&amp;auto=webp&amp;s=e2abde3bbe5b601e2608a07a79b21315fadcb3d9',
         'width': 48,
         'height': 48},
        {'url': 'https://preview.redd.it/award_images/t5_22cerq/x8zqi9ynmh541_BabySnooTears.png?width=64&amp;height=64&amp;auto=webp&amp;s=50423415a196197c51c71239ddd10ee1e553adad',
         'width': 64,
         'height': 64},
        {'url': 'https://preview.redd.it/award_images/t5_22cerq/x8zqi9ynmh541_BabySnooTears.png?width=128&amp;height=128&amp;auto=webp&amp;s=9d1ba3b1fccacf2b9374c9569b6fd0cd53ad9c39',
         'width': 128,
         'height': 128}],
       'days_of_drip_extension': 0,
       'award_type': 'global',
       'start_date': 1576718100,
       'coin_price': 700,
       'icon_width': 2048,
       'subreddit_coin_reward': 0,
       'name': 'Baby Snoo Tears'},
      {'count': 4,
       'is_enabled': True,
       'subreddit_id': None,
       'description': "Shows the Silver Award... and that's it.",
       'end_date': None,
       'award_sub_type': 'GLOBAL',
       'coin_reward': 0,
       'icon_url': 'https://www.redditstatic.com/gold/awards/icon/silver_512.png',
       'days_of_premium': 0,
       'is_new': False,
       'id': 'gid_1',
       'icon_height': 512,
       'resized_icons': [{'url': 'https://www.redditstatic.com/gold/awards/icon/silver_16.png',
         'width': 16,
         'height': 16},
        {'url': 'https://www.redditstatic.com/gold/awards/icon/silver_32.png',
         'width': 32,
         'height': 32},
        {'url': 'https://www.redditstatic.com/gold/awards/icon/silver_48.png',
         'width': 48,
         'height': 48},
        {'url': 'https://www.redditstatic.com/gold/awards/icon/silver_64.png',
         'width': 64,
         'height': 64},
        {'url': 'https://www.redditstatic.com/gold/awards/icon/silver_128.png',
         'width': 128,
         'height': 128}],
       'days_of_drip_extension': 0,
       'award_type': 'global',
       'start_date': None,
       'coin_price': 100,
       'icon_width': 512,
       'subreddit_coin_reward': 0,
       'name': 'Silver'}],
     'awarders': ['borumlive'],
     'media_only': False,
     'can_gild': False,
     'spoiler': False,
     'locked': False,
     'author_flair_text': None,
     'visited': False,
     'removed_by': None,
     'num_reports': None,
     'distinguished': None,
     'subreddit_id': 't5_2qh3l',
     'mod_reason_by': None,
     'removal_reason': None,
     'link_flair_background_color': '',
     'id': 'ed9nm2',
     'is_robot_indexable': True,
     'report_reasons': None,
     'author': 'M7plusoneequalsm8',
     'discussion_type': None,
     'num_comments': 6507,
     'send_replies': True,
     'whitelist_status': 'all_ads',
     'contest_mode': False,
     'mod_reports': [],
     'author_patreon_flair': False,
     'author_flair_text_color': None,
     'permalink': '/r/news/comments/ed9nm2/a_vegan_couple_have_been_charged_with_firstdegree/',
     'parent_whitelist_status': 'all_ads',
     'stickied': False,
     'url': 'https://news.sky.com/story/vegan-parents-accused-of-starving-child-to-death-on-diet-of-fruit-and-vegetables-11891094?dcmp=snt-sf-twitter',
     'subreddit_subscribers': 19451297,
     'created_utc': 1576845446.0,
     'num_crossposts': 18,
     'media': None,
     'is_video': False}}],
  'after': 't3_ed9nm2',
  'before': None}}

HTTP Posts

Some web API's require you to send a substantial amount of data to them. In this case an HTTP POST a more appropriate request than a GET. For example to get sentiment from text through the text-processing web API, you must include the text as part of the data payload. We accomplish this through the requests.post() function and the data = payload named argument:

In [30]:
tweet = "I dislike the Voice. I will not be sad when that show is cancelled. Horrible!"
web_api_url = 'http://text-processing.com/api/sentiment/'
payload = { 'text' : tweet }
response = requests.post(web_api_url, data = payload)
response.json()
Out[30]:
{'probability': {'neg': 0.7638937484899654,
  'neutral': 0.021024021628106692,
  'pos': 0.23610625151003461},
 'label': 'neg'}

Part 4. API Authentication

Most API's require Authentication. At the very mimimum, authentication is a means to track who is calling the API so the service provider may place limits on your use of it. In the case of social network API's you use the API to and permission on behalf of another user. In all cases of API authentication the service knows who you are, and the credentials allow your computer program to act on your behalf. Therefore is is of utmost importance that you protect your API keys from falling into the wrong hands!

In this section we will explain some of the common ways that API Authentication is implemented.

IMPORTANT NOTE You will see examples of API's being called, but the credentials used here will not work. Get your own set of credentials to run the example code.

API Keys

API Keys are the simplest form of authentication. You must first sign up for the API on their website and in exchange some personal information you will be issued an API key. This key is required to send requests to the API so they can track how many requests you are making. The key is usually a randomly generated set of characters which is unique to the service.

Where you put the API key depends on the service you are using. There is no one way to do this and you will need to read through the documentation to figure it out. Common places are:

  • In the URL itself.
  • In the HTTP request header.

API Key in the URL

The Darksky API https://darksky.net/dev is an example of a service where the key is in the URL. For these types of services you will need to build the URL before making the request. This is a job best handed by Python's f-strings. Here's an example of the API call getting the current weather conditions at coordinates 37.8267,-122.4233

In [31]:
darksky_key = 'zyx1234567890abcdefg'
lat = 37.8267
long = -122.4233
url = f"https://api.darksky.net/forecast/{darksky_key}/{lat}/{long}"
response = requests.get(url)
print(response.url)
https://api.darksky.net/forecast/zyx1234567890abcdefg/37.8267/-122.4233

API Key in the HTTP Request Header

The Zomato API https://developers.zomato.com/api is an example of a service where the key is placed in the HTTP request header. For this type of authentication, you will need to know the name of the header the key should be placed under. It could be api-key or x-api-key or user_key or key or mike-fudge. Again you will need to read through the documentation to figure it out.

With this approach, you need to pass a dictionary into the headers= named argument. For example:

In [32]:
zomato_key = 'zyx1234567890abcdefg'
custom_headers = { 'user-key': zomato_key, 'Accept' : 'application/json' }
url = 'https://developers.zomato.com/api/v2.1/categories'
response = requests.get(url, headers = custom_headers )
print(response.url)
https://developers.zomato.com/api/v2.1/categories

My response is not in JSON format!

Also notice the header includes 'Accept' : 'application/json' this is a common value placed in the HTTP request header to ask the API to return the results in JSON format. If your response is coming back in XML format, use this to option in the header to request a response in JSON format.

OAUTH2 Authentication

Most of the social service API's like facebook, twitter, yelp, and spotify use the OAUTH2 protocol for authentication. OAUTH (https://www.digitalocean.com/community/tutorials/an-introduction-to-oauth-2) is a complex protocol with a variety of flexible authentication workflows. The easist of the OAUTH2 workflows is the client credentials flow and if your API support this one, USE IT. Basically its a two step process

  1. make an HTTP request to get an authorization token
  2. make subsequent HTTP requests to the API, putting the token in the header.

The details:

  1. When you signup on the website, the API issues you a client_id (think of this as a unique username for your API) and a client_secret (consider this your client_id's password). this means you are not authenitcating with your own credentials. The service will provide you with an authorization endpoint which is token issuer. In Effect you "Log in" to the service not with your username and password but with the client_id and client_secret instead.
  2. You make an HTTP request to the authorization endpoint, sending your client_id and client_secret, typically in the query string. This asks the API to authenticate your credentials and issue you a token in the response headers under the key Authroization. The token returned is a very long text string. Tokens expire and cannot be used forever.
  3. From this point forward, when you need to call the API, you provide your issued token in the header, like this { 'Authorization' : 'Bearer ' + token }

As a best practice you should renew the token only when it expires.

Here's an example of what this might look like for a given API. In this example, the API gets the token using the HTTP GET method, which is less secure and not used as much anymore.

In [ ]:
# OAUTH2 CLIENT CREDENTIALS FLOW - GET METHOD
client_id='abc123'
client_secret='zyx1234567890abcdefg'

# get the Bearer token, HTTP GET Method
query_string = { 'grant_type' : 'client_credentials', 'client_id' : client_id, 'client_secret' : client_secret }
token_url = 'https://someservice.com/token'
auth_response = requests.get(endpoint_url, params=query_string)
token = auth_response.headers['Key-For-Access-Token']

# now that we have the token we can keep using it in our api calls.
api_url = 'https://someservice.com/api'
api_headers = {'Authorization' : f"Bearer {token}" }
api_response = requests.get(api_url, headers = api_headers)
data = api_response.json()

Here is another example, this time the service uses the HTTP Post method. This is a more secure method than the GET method because the client_id and client_secret are not visible on the URL. Most services will use this pattern today:

In [ ]:
# OAUTH2 CLIENT CREDENTIALS FLOW - POST METHOD
client_id='abc123'
client_secret='zyx1234567890abcdefg'
body = { 'grant_type' : 'client_credentials'}

# get the Bearer Token, HTTP POST method
token_url = 'https://someservice.com/token'
response = requests.post(token_url,  auth=(client_id, client_secret), data=body)
token = response.headers['Key-For-Access-Token']
print(token)

# now that we have the token we can keep using it in our api calls.oken
headers = { "Authorization" : f"Bearer {token}" }
query_string = { 'q' : 'search-example' }
endpoint_url = 'https://someservice/api/search'
response = requests.get(endpoint_url, headers=headers, params = query_string)
data = response.json()

Concuding Remarks - API Authentication

There is no one way to call every API and thus you will need to read the documentation and play around with your code in order to learn how to correctly authenticate to the API and retrieve a valid response. Take a look at the examples and documentation provided.

Lots of trial and error are required, so be persistent! We expect everyone to suffer through this process as part of what it takes to complete your demo day project. This shows us you are capable of learning new things independently and thus are ready to handle the next great thing Python throws our way!