Learning FourSquare API with Python

Introduction

In this lab, you will learn in details how to make calls to the Foursquare API for different purposes. You will learn how to construct a URL to send a request to the API to search for a specific type of venues, to explore a particular venue, to explore a Foursquare user, to explore a geographical location, and to get trending venues around a location. Also, you will learn how to use the visualization library, Folium, to visualize the results.

Import necessary Libraries

In [1]:
import requests # library to handle requests
import pandas as pd # library for data analsysis
import numpy as np # library to handle data in a vectorized manner
import random # library for random number generation

!conda install -c conda-forge geopy --yes 
from geopy.geocoders import Nominatim # module to convert an address into latitude and longitude values

# libraries for displaying images
from IPython.display import Image 
from IPython.core.display import HTML 
    
# tranforming json file into a pandas dataframe library
from pandas.io.json import json_normalize

!conda install -c conda-forge folium=0.5.0 --yes
import folium # plotting library

print('Folium installed')
print('Libraries imported.')
Collecting package metadata: done
Solving environment: \ 
The environment is inconsistent, please check the package plan carefully
The following packages are causing the inconsistency:

  - defaults/linux-64::anaconda==5.3.1=py37_0
  - defaults/linux-64::astropy==3.0.4=py37h14c3975_0
  - defaults/linux-64::bkcharts==0.2=py37_0
  - defaults/linux-64::blaze==0.11.3=py37_0
  - defaults/linux-64::bokeh==0.13.0=py37_0
  - defaults/linux-64::bottleneck==1.2.1=py37h035aef0_1
  - defaults/linux-64::dask==0.19.1=py37_0
  - defaults/linux-64::datashape==0.5.4=py37_1
  - defaults/linux-64::mkl-service==1.1.2=py37h90e4bf4_5
  - defaults/linux-64::numba==0.39.0=py37h04863e7_0
  - defaults/linux-64::numexpr==2.6.8=py37hd89afb7_0
  - defaults/linux-64::odo==0.5.1=py37_0
  - defaults/linux-64::pytables==3.4.4=py37ha205bf6_0
  - defaults/linux-64::pytest-arraydiff==0.2=py37h39e3cac_0
  - defaults/linux-64::pytest-astropy==0.4.0=py37_0
  - defaults/linux-64::pytest-doctestplus==0.1.3=py37_0
  - defaults/linux-64::pywavelets==1.0.0=py37hdd07704_0
  - defaults/linux-64::scikit-image==0.14.0=py37hf484d3e_1
done

## Package Plan ##

  environment location: /home/jupyterlab/conda

  added / updated specs:
    - geopy


The following packages will be downloaded:

    package                    |            build
    ---------------------------|-----------------
    geographiclib-1.49         |             py_0          32 KB  conda-forge
    geopy-1.19.0               |             py_0          53 KB  conda-forge
    ------------------------------------------------------------
                                           Total:          85 KB

The following NEW packages will be INSTALLED:

  geographiclib      conda-forge/noarch::geographiclib-1.49-py_0

The following packages will be UPDATED:

  geopy              conda-forge/linux-64::geopy-1.11.0-py~ --> conda-forge/noarch::geopy-1.19.0-py_0



Downloading and Extracting Packages
geopy-1.19.0         | 53 KB     | ##################################### | 100% 
geographiclib-1.49   | 32 KB     | ##################################### | 100% 
Preparing transaction: done
Verifying transaction: done
Executing transaction: done
Collecting package metadata: done
Solving environment: / 
The environment is inconsistent, please check the package plan carefully
The following packages are causing the inconsistency:

  - defaults/linux-64::anaconda==5.3.1=py37_0
  - defaults/linux-64::astropy==3.0.4=py37h14c3975_0
  - defaults/linux-64::bkcharts==0.2=py37_0
  - defaults/linux-64::blaze==0.11.3=py37_0
  - defaults/linux-64::bokeh==0.13.0=py37_0
  - defaults/linux-64::bottleneck==1.2.1=py37h035aef0_1
  - defaults/linux-64::dask==0.19.1=py37_0
  - defaults/linux-64::datashape==0.5.4=py37_1
  - defaults/linux-64::mkl-service==1.1.2=py37h90e4bf4_5
  - defaults/linux-64::numba==0.39.0=py37h04863e7_0
  - defaults/linux-64::numexpr==2.6.8=py37hd89afb7_0
  - defaults/linux-64::odo==0.5.1=py37_0
  - defaults/linux-64::pytables==3.4.4=py37ha205bf6_0
  - defaults/linux-64::pytest-arraydiff==0.2=py37h39e3cac_0
  - defaults/linux-64::pytest-astropy==0.4.0=py37_0
  - defaults/linux-64::pytest-doctestplus==0.1.3=py37_0
  - defaults/linux-64::pywavelets==1.0.0=py37hdd07704_0
  - defaults/linux-64::scikit-image==0.14.0=py37hf484d3e_1
done

# All requested packages already installed.

Folium installed
Libraries imported.

Define Foursquare Credentials and Version

Make sure that you have created a Foursquare developer account and have your credentials handy
In [2]:
CLIENT_ID = '' # your Foursquare ID
CLIENT_SECRET = '' # your Foursquare Secret
VERSION = '20180604'
LIMIT = 30
print('Your credentails:')
print('CLIENT_ID: ')
print('CLIENT_SECRET:')
Your credentails:
CLIENT_ID: 
CLIENT_SECRET:

Let's again assume that you are staying at the Conrad hotel. So let's start by converting the Contrad Hotel's address to its latitude and longitude coordinates.

In order to define an instance of the geocoder, we need to define a user_agent. We will name our agent foursquare_agent, as shown below.

In [3]:
address = '102 North End Ave, New York, NY'

geolocator = Nominatim(user_agent="foursquare_agent")
location = geolocator.geocode(address)
latitude = location.latitude
longitude = location.longitude
print(latitude, longitude)
40.7149555 -74.0153365

1. Search for a specific venue category

https://api.foursquare.com/v2/venues/search?client_id=CLIENT_ID&client_secret=CLIENT_SECRET&ll=LATITUDE,LONGITUDE&v=VERSION&query=QUERY&radius=RADIUS&limit=LIMIT

Now, let's assume that it is lunch time, and you are craving Italian food. So, let's define a query to search for Italian food that is within 500 metres from the Conrad Hotel.

In [4]:
search_query = 'Italian'
radius = 500
print(search_query + ' .... OK!')
Italian .... OK!

Define the corresponding URL

In [5]:
url = 'https://api.foursquare.com/v2/venues/search?client_id={}&client_secret={}&ll={},{}&v={}&query={}&radius={}&limit={}'.format(CLIENT_ID, CLIENT_SECRET, latitude, longitude, VERSION, search_query, radius, LIMIT)
url
Out[5]:
'https://api.foursquare.com/v2/venues/search?client_id=POLHESRKW3XHK2RRL43QDI0MTY1IMDPIQYRHVYKTHVQBOAWZ&client_secret=MQEW54YAQYCNSE2C3RMF04TGMUZASA21XCCTE4LVEC3DBJT5&ll=40.7149555,-74.0153365&v=20180604&query=Italian&radius=500&limit=30'

Send the GET Request and examine the results

In [6]:
results = requests.get(url).json()
results
Out[6]:
{'meta': {'code': 200, 'requestId': '5ccac2d94434b9314d7603ab'},
 'response': {'venues': [{'id': '4fa862b3e4b0ebff2f749f06',
    'name': "Harry's Italian Pizza Bar",
    'location': {'address': '225 Murray St',
     'lat': 40.71521779064671,
     'lng': -74.01473940209351,
     'labeledLatLngs': [{'label': 'display',
       'lat': 40.71521779064671,
       'lng': -74.01473940209351}],
     'distance': 58,
     'postalCode': '10282',
     'cc': 'US',
     'city': 'New York',
     'state': 'NY',
     'country': 'United States',
     'formattedAddress': ['225 Murray St',
      'New York, NY 10282',
      'United States']},
    'categories': [{'id': '4bf58dd8d48988d1ca941735',
      'name': 'Pizza Place',
      'pluralName': 'Pizza Places',
      'shortName': 'Pizza',
      'icon': {'prefix': 'https://ss3.4sqi.net/img/categories_v2/food/pizza_',
       'suffix': '.png'},
      'primary': True}],
    'delivery': {'id': '294544',
     'url': 'https://www.seamless.com/menu/harrys-italian-pizza-bar-225-murray-st-new-york/294544?affiliate=1131&utm_source=foursquare-affiliate-network&utm_medium=affiliate&utm_campaign=1131&utm_content=294544',
     'provider': {'name': 'seamless',
      'icon': {'prefix': 'https://fastly.4sqi.net/img/general/cap/',
       'sizes': [40, 50],
       'name': '/delivery_provider_seamless_20180129.png'}}},
    'referralId': 'v-1556792026',
    'hasPerk': False},
   {'id': '4f3232e219836c91c7bfde94',
    'name': 'Conca Cucina Italian Restaurant',
    'location': {'address': '63 W Broadway',
     'lat': 40.71446,
     'lng': -74.010086,
     'labeledLatLngs': [{'label': 'display',
       'lat': 40.71446,
       'lng': -74.010086}],
     'distance': 446,
     'postalCode': '10007',
     'cc': 'US',
     'city': 'New York',
     'state': 'NY',
     'country': 'United States',
     'formattedAddress': ['63 W Broadway',
      'New York, NY 10007',
      'United States']},
    'categories': [{'id': '4d4b7105d754a06374d81259',
      'name': 'Food',
      'pluralName': 'Food',
      'shortName': 'Food',
      'icon': {'prefix': 'https://ss3.4sqi.net/img/categories_v2/food/default_',
       'suffix': '.png'},
      'primary': True}],
    'referralId': 'v-1556792026',
    'hasPerk': False},
   {'id': '3fd66200f964a520f4e41ee3',
    'name': 'Ecco',
    'location': {'address': '124 Chambers St',
     'crossStreet': 'btwn Church St & W Broadway',
     'lat': 40.71533713859952,
     'lng': -74.00884766217825,
     'labeledLatLngs': [{'label': 'display',
       'lat': 40.71533713859952,
       'lng': -74.00884766217825}],
     'distance': 549,
     'postalCode': '10007',
     'cc': 'US',
     'city': 'New York',
     'state': 'NY',
     'country': 'United States',
     'formattedAddress': ['124 Chambers St (btwn Church St & W Broadway)',
      'New York, NY 10007',
      'United States']},
    'categories': [{'id': '4bf58dd8d48988d110941735',
      'name': 'Italian Restaurant',
      'pluralName': 'Italian Restaurants',
      'shortName': 'Italian',
      'icon': {'prefix': 'https://ss3.4sqi.net/img/categories_v2/food/italian_',
       'suffix': '.png'},
      'primary': True}],
    'referralId': 'v-1556792026',
    'hasPerk': False}]}}

Get relevant part of JSON and transform it into a pandas dataframe

In [7]:
# assign relevant part of JSON to venues
venues = results['response']['venues']

# tranform venues into a dataframe
dataframe = json_normalize(venues)
dataframe.head()
Out[7]:
categories delivery.id delivery.provider.icon.name delivery.provider.icon.prefix delivery.provider.icon.sizes delivery.provider.name delivery.url hasPerk id location.address ... location.crossStreet location.distance location.formattedAddress location.labeledLatLngs location.lat location.lng location.postalCode location.state name referralId
0 [{'id': '4bf58dd8d48988d1ca941735', 'name': 'P... 294544 /delivery_provider_seamless_20180129.png https://fastly.4sqi.net/img/general/cap/ [40, 50] seamless https://www.seamless.com/menu/harrys-italian-p... False 4fa862b3e4b0ebff2f749f06 225 Murray St ... NaN 58 [225 Murray St, New York, NY 10282, United Sta... [{'label': 'display', 'lat': 40.71521779064671... 40.715218 -74.014739 10282 NY Harry's Italian Pizza Bar v-1556792026
1 [{'id': '4d4b7105d754a06374d81259', 'name': 'F... NaN NaN NaN NaN NaN NaN False 4f3232e219836c91c7bfde94 63 W Broadway ... NaN 446 [63 W Broadway, New York, NY 10007, United Sta... [{'label': 'display', 'lat': 40.71446, 'lng': ... 40.714460 -74.010086 10007 NY Conca Cucina Italian Restaurant v-1556792026
2 [{'id': '4bf58dd8d48988d110941735', 'name': 'I... NaN NaN NaN NaN NaN NaN False 3fd66200f964a520f4e41ee3 124 Chambers St ... btwn Church St & W Broadway 549 [124 Chambers St (btwn Church St & W Broadway)... [{'label': 'display', 'lat': 40.71533713859952... 40.715337 -74.008848 10007 NY Ecco v-1556792026

3 rows × 23 columns

Define information of interest and filter dataframe

In [8]:
# keep only columns that include venue name, and anything that is associated with location
filtered_columns = ['name', 'categories'] + [col for col in dataframe.columns if col.startswith('location.')] + ['id']
dataframe_filtered = dataframe.loc[:, filtered_columns]

# function that extracts the category of the venue
def get_category_type(row):
    try:
        categories_list = row['categories']
    except:
        categories_list = row['venue.categories']
        
    if len(categories_list) == 0:
        return None
    else:
        return categories_list[0]['name']

# filter the category for each row
dataframe_filtered['categories'] = dataframe_filtered.apply(get_category_type, axis=1)

# clean column names by keeping only last term
dataframe_filtered.columns = [column.split('.')[-1] for column in dataframe_filtered.columns]

dataframe_filtered
Out[8]:
name categories address cc city country crossStreet distance formattedAddress labeledLatLngs lat lng postalCode state id
0 Harry's Italian Pizza Bar Pizza Place 225 Murray St US New York United States NaN 58 [225 Murray St, New York, NY 10282, United Sta... [{'label': 'display', 'lat': 40.71521779064671... 40.715218 -74.014739 10282 NY 4fa862b3e4b0ebff2f749f06
1 Conca Cucina Italian Restaurant Food 63 W Broadway US New York United States NaN 446 [63 W Broadway, New York, NY 10007, United Sta... [{'label': 'display', 'lat': 40.71446, 'lng': ... 40.714460 -74.010086 10007 NY 4f3232e219836c91c7bfde94
2 Ecco Italian Restaurant 124 Chambers St US New York United States btwn Church St & W Broadway 549 [124 Chambers St (btwn Church St & W Broadway)... [{'label': 'display', 'lat': 40.71533713859952... 40.715337 -74.008848 10007 NY 3fd66200f964a520f4e41ee3

Let's visualize the Italian restaurants that are nearby

In [9]:
dataframe_filtered.name
Out[9]:
0          Harry's Italian Pizza Bar
1    Conca Cucina Italian Restaurant
2                               Ecco
Name: name, dtype: object
In [10]:
venues_map = folium.Map(location=[latitude, longitude], zoom_start=13) # generate map centred around the Conrad Hotel

# add a red circle marker to represent the Conrad Hotel
folium.features.CircleMarker(
    [latitude, longitude],
    radius=10,
    color='red',
    popup='Conrad Hotel',
    fill = True,
    fill_color = 'red',
    fill_opacity = 0.6
).add_to(venues_map)

# add the Italian restaurants as blue circle markers
for lat, lng, label in zip(dataframe_filtered.lat, dataframe_filtered.lng, dataframe_filtered.categories):
    folium.features.CircleMarker(
        [lat, lng],
        radius=5,
        color='blue',
        popup=label,
        fill = True,
        fill_color='blue',
        fill_opacity=0.6
    ).add_to(venues_map)

# display map
venues_map
Out[10]:

2. Explore a Given Venue

https://api.foursquare.com/v2/venues/VENUE_ID?client_id=CLIENT_ID&client_secret=CLIENT_SECRET&v=VERSION

A. Let's explore the closest Italian restaurant -- Harry's Italian Pizza Bar

In [11]:
venue_id = '4fa862b3e4b0ebff2f749f06' # ID of Harry's Italian Pizza Bar
url = 'https://api.foursquare.com/v2/venues/{}?client_id={}&client_secret={}&v={}'.format(venue_id, CLIENT_ID, CLIENT_SECRET, VERSION)
url
Out[11]:
'https://api.foursquare.com/v2/venues/4fa862b3e4b0ebff2f749f06?client_id=POLHESRKW3XHK2RRL43QDI0MTY1IMDPIQYRHVYKTHVQBOAWZ&client_secret=MQEW54YAQYCNSE2C3RMF04TGMUZASA21XCCTE4LVEC3DBJT5&v=20180604'

Send GET request for result

In [12]:
result = requests.get(url).json()
print(result['response']['venue'].keys())
result['response']['venue']
dict_keys(['id', 'name', 'contact', 'location', 'canonicalUrl', 'categories', 'verified', 'stats', 'url', 'price', 'hasMenu', 'likes', 'dislike', 'ok', 'rating', 'ratingColor', 'ratingSignals', 'delivery', 'menu', 'allowMenuUrlEdit', 'beenHere', 'specials', 'photos', 'reasons', 'hereNow', 'createdAt', 'tips', 'shortUrl', 'timeZone', 'listed', 'hours', 'popular', 'pageUpdates', 'inbox', 'attributes', 'bestPhoto', 'colors'])
Out[12]:
{'id': '4fa862b3e4b0ebff2f749f06',
 'name': "Harry's Italian Pizza Bar",
 'contact': {'phone': '2126081007', 'formattedPhone': '(212) 608-1007'},
 'location': {'address': '225 Murray St',
  'lat': 40.71521779064671,
  'lng': -74.01473940209351,
  'labeledLatLngs': [{'label': 'display',
    'lat': 40.71521779064671,
    'lng': -74.01473940209351}],
  'postalCode': '10282',
  'cc': 'US',
  'city': 'New York',
  'state': 'NY',
  'country': 'United States',
  'formattedAddress': ['225 Murray St',
   'New York, NY 10282',
   'United States']},
 'canonicalUrl': 'https://foursquare.com/v/harrys-italian-pizza-bar/4fa862b3e4b0ebff2f749f06',
 'categories': [{'id': '4bf58dd8d48988d1ca941735',
   'name': 'Pizza Place',
   'pluralName': 'Pizza Places',
   'shortName': 'Pizza',
   'icon': {'prefix': 'https://ss3.4sqi.net/img/categories_v2/food/pizza_',
    'suffix': '.png'},
   'primary': True},
  {'id': '4bf58dd8d48988d110941735',
   'name': 'Italian Restaurant',
   'pluralName': 'Italian Restaurants',
   'shortName': 'Italian',
   'icon': {'prefix': 'https://ss3.4sqi.net/img/categories_v2/food/italian_',
    'suffix': '.png'}}],
 'verified': False,
 'stats': {'tipCount': 57},
 'url': 'http://harrysitalian.com',
 'price': {'tier': 2, 'message': 'Moderate', 'currency': '$'},
 'hasMenu': True,
 'likes': {'count': 120,
  'groups': [{'type': 'others', 'count': 120, 'items': []}],
  'summary': '120 Likes'},
 'dislike': False,
 'ok': False,
 'rating': 7.2,
 'ratingColor': 'C5DE35',
 'ratingSignals': 213,
 'delivery': {'id': '294544',
  'url': 'https://www.seamless.com/menu/harrys-italian-pizza-bar-225-murray-st-new-york/294544?affiliate=1131&utm_source=foursquare-affiliate-network&utm_medium=affiliate&utm_campaign=1131&utm_content=294544',
  'provider': {'name': 'seamless',
   'icon': {'prefix': 'https://fastly.4sqi.net/img/general/cap/',
    'sizes': [40, 50],
    'name': '/delivery_provider_seamless_20180129.png'}}},
 'menu': {'type': 'Menu',
  'label': 'Menu',
  'anchor': 'View Menu',
  'url': 'https://foursquare.com/v/harrys-italian-pizza-bar/4fa862b3e4b0ebff2f749f06/menu',
  'mobileUrl': 'https://foursquare.com/v/4fa862b3e4b0ebff2f749f06/device_menu'},
 'allowMenuUrlEdit': True,
 'beenHere': {'count': 0,
  'unconfirmedCount': 0,
  'marked': False,
  'lastCheckinExpiredAt': 0},
 'specials': {'count': 0, 'items': []},
 'photos': {'count': 147,
  'groups': [{'type': 'checkin',
    'name': "Friends' check-in photos",
    'count': 0,
    'items': []},
   {'type': 'venue',
    'name': 'Venue photos',
    'count': 147,
    'items': [{'id': '4fad980de4b091b4626c3633',
      'createdAt': 1336776717,
      'source': {'name': 'Foursquare for Android',
       'url': 'https://foursquare.com/download/#/android'},
      'prefix': 'https://fastly.4sqi.net/img/general/',
      'suffix': '/ya1iQFI7pLjuIJp1PGDKlrZS3OJdHCF7tpILMmjv_2w.jpg',
      'width': 480,
      'height': 640,
      'user': {'id': '13676709',
       'firstName': 'Leony',
       'lastName': 'Naciri',
       'gender': 'none',
       'photo': {'prefix': 'https://fastly.4sqi.net/img/user/',
        'suffix': '/T0ANFNGNMCHUDEUE.jpg'}},
      'visibility': 'public'},
     {'id': '513a2e50e4b08ac082454376',
      'createdAt': 1362767440,
      'source': {'name': 'Foursquare for iOS',
       'url': 'https://foursquare.com/download/#/iphone'},
      'prefix': 'https://fastly.4sqi.net/img/general/',
      'suffix': '/6195366_2CTGLooEIZ-oVxZlMGjQFunPJuwJql1jtFnIf69-8Tw.jpg',
      'width': 717,
      'height': 959,
      'user': {'id': '6195366',
       'firstName': 'Michelle',
       'lastName': 'Thornton',
       'gender': 'female',
       'photo': {'prefix': 'https://fastly.4sqi.net/img/user/',
        'suffix': '/2RJUIIDMSRLF1UOE.jpg'}},
      'visibility': 'public'}]}],
  'summary': '0 photos'},
 'reasons': {'count': 1,
  'items': [{'summary': 'Lots of people like this place',
    'type': 'general',
    'reasonName': 'rawLikesReason'}]},
 'hereNow': {'count': 0, 'summary': 'Nobody here', 'groups': []},
 'createdAt': 1336435379,
 'tips': {'count': 57,
  'groups': [{'type': 'others',
    'name': 'All tips',
    'count': 57,
    'items': [{'id': '53d27909498e0523841340b6',
      'createdAt': 1406302473,
      'text': "Harry's Italian Pizza bar is known for it's amazing pizza, but did you know that the brunches here are amazing too? Try the Nutella French toast and we know you'll be sold.",
      'type': 'user',
      'canonicalUrl': 'https://foursquare.com/item/53d27909498e0523841340b6',
      'lang': 'en',
      'likes': {'count': 4,
       'groups': [{'type': 'others',
         'count': 4,
         'items': [{'id': '369426',
           'firstName': 'P.',
           'lastName': 'M.',
           'gender': 'male',
           'photo': {'prefix': 'https://fastly.4sqi.net/img/user/',
            'suffix': '/JPQYUWJKUT0H2OO4.jpg'}},
          {'id': '87587879',
           'firstName': 'Diane',
           'lastName': 'Danneels',
           'gender': 'female',
           'photo': {'prefix': 'https://fastly.4sqi.net/img/user/',
            'suffix': '/87587879-ESLRSZLQ2CBE2P4W.jpg'}},
          {'id': '87591341',
           'firstName': 'Tim',
           'lastName': 'Sheehan',
           'gender': 'male',
           'photo': {'prefix': 'https://fastly.4sqi.net/img/user/',
            'suffix': '/-Z4YK4VKE0JSVXIY1.jpg'}},
          {'id': '87473404',
           'firstName': 'TenantKing.com',
           'gender': 'none',
           'photo': {'prefix': 'https://fastly.4sqi.net/img/user/',
            'suffix': '/87473404-HI5DTBTK0HX401CA.png'},
           'type': 'page'}]}],
       'summary': '4 likes'},
      'logView': True,
      'agreeCount': 4,
      'disagreeCount': 0,
      'todo': {'count': 0},
      'user': {'id': '87473404',
       'firstName': 'TenantKing.com',
       'gender': 'none',
       'photo': {'prefix': 'https://fastly.4sqi.net/img/user/',
        'suffix': '/87473404-HI5DTBTK0HX401CA.png'},
       'type': 'page'}},
     {'id': '53962e98498ecd43845cc813',
      'createdAt': 1402351256,
      'text': "I'm not sure if this place is one of the famous pizzerias in NYC but I loved their pizza.",
      'type': 'user',
      'canonicalUrl': 'https://foursquare.com/item/53962e98498ecd43845cc813',
      'photo': {'id': '53962e9c498e53dbbf59b28e',
       'createdAt': 1402351260,
       'source': {'name': 'Foursquare for iOS',
        'url': 'https://foursquare.com/download/#/iphone'},
       'prefix': 'https://fastly.4sqi.net/img/general/',
       'suffix': '/7012699_DCcCtW8oRbhNv62E68gxtLnWn7IgRhapBr8vdnIQjYk.jpg',
       'width': 720,
       'height': 720,
       'visibility': 'public'},
      'photourl': 'https://fastly.4sqi.net/img/general/original/7012699_DCcCtW8oRbhNv62E68gxtLnWn7IgRhapBr8vdnIQjYk.jpg',
      'lang': 'en',
      'likes': {'count': 2,
       'groups': [{'type': 'others',
         'count': 2,
         'items': [{'id': '7012699',
           'firstName': 'Hani',
           'lastName': 'Alawadi',
           'gender': 'male',
           'photo': {'prefix': 'https://fastly.4sqi.net/img/user/',
            'suffix': '/2ZMJFSP2Z4R5NBVX.jpg'}},
          {'id': '61939758',
           'firstName': 'Leena',
           'lastName': 'Ragheb',
           'gender': 'female',
           'photo': {'prefix': 'https://fastly.4sqi.net/img/user/',
            'suffix': '/61939758_LF7CZdXW_sd2CLQ12BzaItezyCl3vEKHPsEpJ1HHYa2aewPt0H2_lL59BRdn8eiYNNtO4-hcl.jpg'}}]}],
       'summary': '2 likes'},
      'logView': True,
      'agreeCount': 2,
      'disagreeCount': 0,
      'todo': {'count': 0},
      'user': {'id': '7012699',
       'firstName': 'Hani',
       'lastName': 'Alawadi',
       'gender': 'male',
       'photo': {'prefix': 'https://fastly.4sqi.net/img/user/',
        'suffix': '/2ZMJFSP2Z4R5NBVX.jpg'}},
      'authorInteractionType': 'liked'}]}]},
 'shortUrl': 'http://4sq.com/JNblHV',
 'timeZone': 'America/New_York',
 'listed': {'count': 51,
  'groups': [{'type': 'others',
    'name': 'Lists from other people',
    'count': 51,
    'items': [{'id': '4fa32fd0e4b04193744746b1',
      'name': 'Manhattan Haunts',
      'description': '',
      'type': 'others',
      'user': {'id': '24592223',
       'firstName': 'Becca',
       'lastName': 'McArthur',
       'gender': 'female',
       'photo': {'prefix': 'https://fastly.4sqi.net/img/user/',
        'suffix': '/24592223-RAW2UYM0GIB1U40K.jpg'}},
      'editable': False,
      'public': True,
      'collaborative': False,
      'url': '/becca_mcarthur/list/manhattan-haunts',
      'canonicalUrl': 'https://foursquare.com/becca_mcarthur/list/manhattan-haunts',
      'createdAt': 1336094672,
      'updatedAt': 1380845377,
      'photo': {'id': '4e8cc9461081e3b3544e12e5',
       'createdAt': 1317849414,
       'prefix': 'https://fastly.4sqi.net/img/general/',
       'suffix': '/0NLVU2HC1JF4DXIMKWUFW3QBUT31DC11EFNYYHMJG3NDWAPS.jpg',
       'width': 492,
       'height': 330,
       'user': {'id': '742542',
        'firstName': 'Time Out New York',
        'gender': 'none',
        'photo': {'prefix': 'https://fastly.4sqi.net/img/user/',
         'suffix': '/XXHKCBSQHBORZNSR.jpg'},
        'type': 'page'},
       'visibility': 'public'},
      'followers': {'count': 22},
      'listItems': {'count': 187,
       'items': [{'id': 'v4fa862b3e4b0ebff2f749f06',
         'createdAt': 1342934485}]}},
     {'id': '4fae817be4b085f6b2a74d19',
      'name': 'USA NYC MAN FiDi',
      'description': 'Where to go for decent eats in the restaurant wasteland of Downtown NYC aka FiDi, along with Tribeca & Battery Park City.',
      'type': 'others',
      'user': {'id': '12113441',
       'firstName': 'Kino',
       'gender': 'male',
       'photo': {'prefix': 'https://fastly.4sqi.net/img/user/',
        'suffix': '/12113441-K5HTHFLU2MUCM0CM.jpg'}},
      'editable': False,
      'public': True,
      'collaborative': False,
      'url': '/kinosfault/list/usa-nyc-man-fidi',
      'canonicalUrl': 'https://foursquare.com/kinosfault/list/usa-nyc-man-fidi',
      'createdAt': 1336836475,
      'updatedAt': 1556754919,
      'photo': {'id': '55984992498e13ba75e353bb',
       'createdAt': 1436043666,
       'prefix': 'https://fastly.4sqi.net/img/general/',
       'suffix': '/12113441_iOa6Uh-Xi8bhj2-gpzkkw8MKiAIs7RmOcz_RM7m8ink.jpg',
       'width': 540,
       'height': 960,
       'user': {'id': '12113441',
        'firstName': 'Kino',
        'gender': 'male',
        'photo': {'prefix': 'https://fastly.4sqi.net/img/user/',
         'suffix': '/12113441-K5HTHFLU2MUCM0CM.jpg'}},
       'visibility': 'public'},
      'followers': {'count': 20},
      'listItems': {'count': 273,
       'items': [{'id': 'v4fa862b3e4b0ebff2f749f06',
         'createdAt': 1373909433}]}},
     {'id': '4fddeff0e4b0e078037ac0d3',
      'name': 'NYC Resturants',
      'description': '',
      'type': 'others',
      'user': {'id': '21563126',
       'firstName': 'Richard',
       'lastName': 'Revilla',
       'gender': 'male',
       'photo': {'prefix': 'https://fastly.4sqi.net/img/user/',
        'suffix': '/21563126_v05J1KPw_SVj6Ehq9g8B9jeAGjFUMsU5QGl-NZ8inUQ7pKQm5bKplW37EmR7jS2A7GYPBBAtl.jpg'}},
      'editable': False,
      'public': True,
      'collaborative': True,
      'url': '/rickr7/list/nyc-resturants',
      'canonicalUrl': 'https://foursquare.com/rickr7/list/nyc-resturants',
      'createdAt': 1339944944,
      'updatedAt': 1556326979,
      'photo': {'id': '5072dd13e4b09145cdf782d1',
       'createdAt': 1349704979,
       'prefix': 'https://fastly.4sqi.net/img/general/',
       'suffix': '/208205_fGh2OuAZ9qJ4agbAA5wMVNOSIm9kNUlRtNwj1N-adqg.jpg',
       'width': 800,
       'height': 800,
       'user': {'id': '208205',
        'firstName': 'Thalia',
        'lastName': 'K',
        'gender': 'female',
        'photo': {'prefix': 'https://fastly.4sqi.net/img/user/',
         'suffix': '/SNOOLCAW2AG04ZKD.jpg'}},
       'visibility': 'public'},
      'followers': {'count': 12},
      'listItems': {'count': 194,
       'items': [{'id': 't54ed3b13498e857fd7dbb6fc',
         'createdAt': 1514680908}]}},
     {'id': '5266c68a498e7c667807fe09',
      'name': 'Foodie Love in NY - 02',
      'description': '',
      'type': 'others',
      'user': {'id': '547977',
       'firstName': 'WiLL',
       'gender': 'male',
       'photo': {'prefix': 'https://fastly.4sqi.net/img/user/',
        'suffix': '/-Q5NYGDMFDMOITQRR.jpg'}},
      'editable': False,
      'public': True,
      'collaborative': False,
      'url': '/sweetiewill/list/foodie-love-in-ny--02',
      'canonicalUrl': 'https://foursquare.com/sweetiewill/list/foodie-love-in-ny--02',
      'createdAt': 1382467210,
      'updatedAt': 1391995585,
      'followers': {'count': 7},
      'listItems': {'count': 200,
       'items': [{'id': 'v4fa862b3e4b0ebff2f749f06',
         'createdAt': 1386809936}]}}]}]},
 'hours': {'status': 'Closed until 11:30 AM',
  'richStatus': {'entities': [], 'text': 'Closed until 11:30 AM'},
  'isOpen': False,
  'isLocalHoliday': False,
  'dayData': [],
  'timeframes': [{'days': 'Mon–Wed, Sun',
    'open': [{'renderedTime': '11:30 AM–11:00 PM'}],
    'segments': []},
   {'days': 'Thu–Sat',
    'includesToday': True,
    'open': [{'renderedTime': '11:30 AM–Midnight'}],
    'segments': []}]},
 'popular': {'isOpen': False,
  'isLocalHoliday': False,
  'timeframes': [{'days': 'Today',
    'includesToday': True,
    'open': [{'renderedTime': 'Noon–2:00 PM'},
     {'renderedTime': '5:00 PM–10:00 PM'}],
    'segments': []},
   {'days': 'Fri',
    'open': [{'renderedTime': 'Noon–3:00 PM'},
     {'renderedTime': '5:00 PM–11:00 PM'}],
    'segments': []},
   {'days': 'Sat',
    'open': [{'renderedTime': 'Noon–11:00 PM'}],
    'segments': []},
   {'days': 'Sun',
    'open': [{'renderedTime': 'Noon–3:00 PM'},
     {'renderedTime': '5:00 PM–8:00 PM'}],
    'segments': []},
   {'days': 'Mon',
    'open': [{'renderedTime': 'Noon–2:00 PM'},
     {'renderedTime': '6:00 PM–8:00 PM'}],
    'segments': []},
   {'days': 'Tue–Wed',
    'open': [{'renderedTime': 'Noon–2:00 PM'},
     {'renderedTime': '5:00 PM–10:00 PM'}],
    'segments': []}]},
 'pageUpdates': {'count': 0, 'items': []},
 'inbox': {'count': 0, 'items': []},
 'attributes': {'groups': [{'type': 'price',
    'name': 'Price',
    'summary': '$$',
    'count': 1,
    'items': [{'displayName': 'Price', 'displayValue': '$$', 'priceTier': 2}]},
   {'type': 'payments',
    'name': 'Credit Cards',
    'summary': 'Credit Cards',
    'count': 7,
    'items': [{'displayName': 'Credit Cards',
      'displayValue': 'Yes (incl. American Express)'}]},
   {'type': 'outdoorSeating',
    'name': 'Outdoor Seating',
    'summary': 'Outdoor Seating',
    'count': 1,
    'items': [{'displayName': 'Outdoor Seating', 'displayValue': 'Yes'}]},
   {'type': 'serves',
    'name': 'Menus',
    'summary': 'Happy Hour, Brunch & more',
    'count': 8,
    'items': [{'displayName': 'Brunch', 'displayValue': 'Brunch'},
     {'displayName': 'Lunch', 'displayValue': 'Lunch'},
     {'displayName': 'Dinner', 'displayValue': 'Dinner'},
     {'displayName': 'Happy Hour', 'displayValue': 'Happy Hour'}]},
   {'type': 'drinks',
    'name': 'Drinks',
    'summary': 'Beer, Wine & Cocktails',
    'count': 5,
    'items': [{'displayName': 'Beer', 'displayValue': 'Beer'},
     {'displayName': 'Wine', 'displayValue': 'Wine'},
     {'displayName': 'Cocktails', 'displayValue': 'Cocktails'}]},
   {'type': 'diningOptions',
    'name': 'Dining Options',
    'summary': 'Delivery',
    'count': 5,
    'items': [{'displayName': 'Delivery', 'displayValue': 'Delivery'}]}]},
 'bestPhoto': {'id': '4fad980de4b091b4626c3633',
  'createdAt': 1336776717,
  'source': {'name': 'Foursquare for Android',
   'url': 'https://foursquare.com/download/#/android'},
  'prefix': 'https://fastly.4sqi.net/img/general/',
  'suffix': '/ya1iQFI7pLjuIJp1PGDKlrZS3OJdHCF7tpILMmjv_2w.jpg',
  'width': 480,
  'height': 640,
  'visibility': 'public'},
 'colors': {'highlightColor': {'photoId': '4fad980de4b091b4626c3633',
   'value': -13619152},
  'highlightTextColor': {'photoId': '4fad980de4b091b4626c3633', 'value': -1},
  'algoVersion': 3}}

B. Get the venue's overall rating

In [13]:
try:
    print(result['response']['venue']['rating'])
except:
    print('This venue has not been rated yet.')
7.2

That is not a very good rating. Let's check the rating of the second closest Italian restaurant.

In [14]:
venue_id = '4f3232e219836c91c7bfde94' # ID of Conca Cucina Italian Restaurant
url = 'https://api.foursquare.com/v2/venues/{}?client_id={}&client_secret={}&v={}'.format(venue_id, CLIENT_ID, CLIENT_SECRET, VERSION)

result = requests.get(url).json()
try:
    print(result['response']['venue']['rating'])
except:
    print('This venue has not been rated yet.')
This venue has not been rated yet.

Since this restaurant has no ratings, let's check the third restaurant.

In [15]:
venue_id = '3fd66200f964a520f4e41ee3' # ID of Ecco
url = 'https://api.foursquare.com/v2/venues/{}?client_id={}&client_secret={}&v={}'.format(venue_id, CLIENT_ID, CLIENT_SECRET, VERSION)

result = requests.get(url).json()
try:
    print(result['response']['venue']['rating'])
except:
    print('This venue has not been rated yet.')
7.9

Since this restaurant has a slightly better rating, let's explore it further.

C. Get the number of tips

In [16]:
result['response']['venue']['tips']['count']
Out[16]:
17

D. Get the venue's tips

https://api.foursquare.com/v2/venues/VENUE_ID/tips?client_id=CLIENT_ID&client_secret=CLIENT_SECRET&v=VERSION&limit=LIMIT

Create URL and send GET request. Make sure to set limit to get all tips

In [17]:
## Ecco Tips
limit = 15 # set limit to be greater than or equal to the total number of tips
url = 'https://api.foursquare.com/v2/venues/{}/tips?client_id={}&client_secret={}&v={}&limit={}'.format(venue_id, CLIENT_ID, CLIENT_SECRET, VERSION, limit)

results = requests.get(url).json()
results
Out[17]:
{'meta': {'code': 200, 'requestId': '5ccac36b9fb6b7570071a977'},
 'response': {'tips': {'count': 17,
   'items': [{'id': '5ab1cb46c9a517174651d3fe',
     'createdAt': 1521601350,
     'text': 'A+ Italian food! Trust me on this: my mom’s side of the family is 100% Italian. I was born and bred to know good pasta when I see it, and Ecco is one of my all-time NYC favorites',
     'type': 'user',
     'canonicalUrl': 'https://foursquare.com/item/5ab1cb46c9a517174651d3fe',
     'lang': 'en',
     'likes': {'count': 0, 'groups': []},
     'logView': True,
     'agreeCount': 2,
     'disagreeCount': 0,
     'todo': {'count': 0},
     'user': {'id': '484542633',
      'firstName': 'Nick',
      'lastName': 'El-Tawil',
      'gender': 'male',
      'photo': {'prefix': 'https://fastly.4sqi.net/img/user/',
       'suffix': '/484542633_mK2Yum7T_7Tn9fWpndidJsmw2Hof_6T5vJBKCHPLMK5OL-U5ZiJGj51iwBstcpDLYa3Zvhvis.jpg'}},
     'authorInteractionType': 'liked'},
    {'id': '5cb7051b180b910039f90625',
     'createdAt': 1555498267,
     'text': 'Excellent food!! Osso bucco special one of the best I ever had! Lobster ravioli with porcini mushroomhomemade Italian cheesecake, tiramisu and napoleons...calamari fra diavolo was sautéed not fried',
     'type': 'user',
     'canonicalUrl': 'https://foursquare.com/item/5cb7051b180b910039f90625',
     'lang': 'en',
     'likes': {'count': 0, 'groups': []},
     'logView': True,
     'agreeCount': 1,
     'disagreeCount': 0,
     'lastVoteText': 'Upvoted 1 week ago',
     'lastUpvoteTimestamp': 1556113207,
     'todo': {'count': 0},
     'user': {'id': '446298346',
      'firstName': 'Lynn',
      'lastName': 'Bednar-Rando',
      'gender': 'female',
      'photo': {'prefix': 'https://fastly.4sqi.net/img/user/',
       'suffix': '/446298346_fdtqugvP_1WztOWXXDQX92HwIrjzLb6nMQsYiCVlkiTp58UGSFVBwQ0-5G5AdqxlEWphYWTnK.jpg'}},
     'authorInteractionType': 'liked'}]}}}

Get tips and list of associated features

In [18]:
tips = results['response']['tips']['items']

tip = results['response']['tips']['items'][0]
tip.keys()
Out[18]:
dict_keys(['id', 'createdAt', 'text', 'type', 'canonicalUrl', 'lang', 'likes', 'logView', 'agreeCount', 'disagreeCount', 'todo', 'user', 'authorInteractionType'])

Format column width and display all tips

In [19]:
pd.set_option('display.max_colwidth', -1)

tips_df = json_normalize(tips) # json normalize tips

# columns to keep
filtered_columns = ['text', 'agreeCount', 'disagreeCount', 'id', 'user.firstName', 'user.lastName', 'user.gender', 'user.id']
tips_filtered = tips_df.loc[:, filtered_columns]

# display tips
tips_filtered
Out[19]:
text agreeCount disagreeCount id user.firstName user.lastName user.gender user.id
0 A+ Italian food! Trust me on this: my mom’s side of the family is 100% Italian. I was born and bred to know good pasta when I see it, and Ecco is one of my all-time NYC favorites 2 0 5ab1cb46c9a517174651d3fe Nick El-Tawil male 484542633
1 Excellent food!! Osso bucco special one of the best I ever had! Lobster ravioli with porcini mushroomhomemade Italian cheesecake, tiramisu and napoleons...calamari fra diavolo was sautéed not fried 1 0 5cb7051b180b910039f90625 Lynn Bednar-Rando female 446298346

Now remember that because we are using a personal developer account, then we can access only 2 of the restaurant's tips, instead of all 15 tips.

3. Search a Foursquare User

https://api.foursquare.com/v2/users/USER_ID?client_id=CLIENT_ID&client_secret=CLIENT_SECRET&v=VERSION

Define URL, send GET request and display features associated with user

In [20]:
user_id = '484542633' # user ID with most agree counts and complete profile

url = 'https://api.foursquare.com/v2/users/{}?client_id={}&client_secret={}&v={}'.format(user_id, CLIENT_ID, CLIENT_SECRET, VERSION) # define URL

# send GET request
results = requests.get(url).json()
user_data = results['response']['user']

# display features associated with user
user_data.keys()
Out[20]:
dict_keys(['id', 'firstName', 'lastName', 'gender', 'canonicalUrl', 'photo', 'friends', 'tips', 'homeCity', 'bio', 'contact', 'photos', 'type', 'mayorships', 'checkins', 'lists', 'lenses'])
In [21]:
print('First Name: ' + user_data['firstName'])
print('Last Name: ' + user_data['lastName'])
print('Home City: ' + user_data['homeCity'])
First Name: Nick
Last Name: El-Tawil
Home City: New York, NY

How many tips has this user submitted?

In [22]:
user_data['tips']
Out[22]:
{'count': 242}

Wow! So it turns out that Nick is a very active Foursquare user, with more than 250 tips.

Get User's tips

In [23]:
# define tips URL
url = 'https://api.foursquare.com/v2/users/{}/tips?client_id={}&client_secret={}&v={}&limit={}'.format(user_id, CLIENT_ID, CLIENT_SECRET, VERSION, limit)

# send GET request and get user's tips
results = requests.get(url).json()
tips = results['response']['tips']['items']

# format column width
pd.set_option('display.max_colwidth', -1)

tips_df = json_normalize(tips)

# filter columns
filtered_columns = ['text', 'agreeCount', 'disagreeCount', 'id']
tips_filtered = tips_df.loc[:, filtered_columns]

# display user's tips
tips_filtered
Out[23]:
text agreeCount disagreeCount id
0 The best! I’m especially fond of the salmon burger, but I’ve had half of the menu and never been disappointed. There’s a reason this place is well known even outside of the Village! 1 0 5aec594b1f7440002c138612
1 I used to down a pint of chocolate like it was nothing back when I was bulking. Highly recommended! 1 0 5accc9f66fa81f196724807b
2 They serve coffee!!!!!! 1 0 5accc98c0313204c9d7ec157
3 I’m a fan. In fact, I’m such a big fan, I want Taim to hire me to be their spokesman. Kind of like the Arabic Jared Fogle - but without the kid stuff. 1 0 5accbf033abcaf09a24612a0
4 The linguine with clams is on point 👌 1 0 5accbe3a911fc423730f3ed3
5 Great for a quick, cheap lunch! Shorter lines than Chipotle too👌 1 0 5acbecb86fa81f1967e019b0
6 Quick, cheap lunch that tastes good! Way shorter line than Chipotle, too. 1 0 5acbec70a0215b732e264fe8
7 You’re not a real New Yorker until you’ve shame-ordered Insomnia Cookies for delivery at 3am 1 0 5acbbd4eb1538e45373b07f5
8 Good for you yet still tasty! Clean green protein is my go-to after I hit the gym 💪 2 0 5acbbcda01235808d5d6dc75
9 Coffee game on point 1 0 5acbbb1501235808d5d6525e
10 This is the dive bar to end all other dive bars. Go here if you like cheap drinks! 🥃 1 0 5ab576abea1e444f2abb051e
11 Burger game strong 💪 1 0 5ab575fb6bdee65f759da8c1
12 Great burgers & fries! Also, this place is exactly what it’s like when you go to a bar in the Southwest. Source: I’m from Arizona. 2 0 5ab5575d73fe2516ad8f363b
13 That guy looks familiar... 1 0 5ab5299635f98312029a53b7
14 Açaí bowl + peanut butter + whey protein = 💪💪💪 1 0 5ab42db53c858d64af2688a4

Let's get the venue for the tip with the greatest number of agree counts

In [24]:
tip_id = '5ab5575d73fe2516ad8f363b' # tip id

# define URL
url = 'http://api.foursquare.com/v2/tips/{}?client_id={}&client_secret={}&v={}'.format(tip_id, CLIENT_ID, CLIENT_SECRET, VERSION)

# send GET Request and examine results
result = requests.get(url).json()
print(result['response']['tip']['venue']['name'])
print(result['response']['tip']['venue']['location'])
Cowgirl
{'address': '519 Hudson St', 'crossStreet': 'at W 10th St', 'lat': 40.73373338282062, 'lng': -74.0062998849649, 'labeledLatLngs': [{'label': 'display', 'lat': 40.73373338282062, 'lng': -74.0062998849649}], 'postalCode': '10014', 'cc': 'US', 'city': 'New York', 'state': 'NY', 'country': 'United States', 'formattedAddress': ['519 Hudson St (at W 10th St)', 'New York, NY 10014', 'United States']}

Get User's friends

In [25]:
user_friends = json_normalize(user_data['friends']['groups'][0]['items'])
user_friends
Out[25]:

Interesting. Despite being very active, it turns out that Nick does not have any friends on Foursquare. This might definitely change in the future.

Retrieve the User's Profile Image

In [26]:
user_data
Out[26]:
{'id': '484542633',
 'firstName': 'Nick',
 'lastName': 'El-Tawil',
 'gender': 'male',
 'canonicalUrl': 'https://foursquare.com/nickeltawil',
 'photo': {'prefix': 'https://fastly.4sqi.net/img/user/',
  'suffix': '/484542633_mK2Yum7T_7Tn9fWpndidJsmw2Hof_6T5vJBKCHPLMK5OL-U5ZiJGj51iwBstcpDLYa3Zvhvis.jpg'},
 'friends': {'count': 0,
  'groups': [{'type': 'others',
    'name': 'Other friends',
    'count': 0,
    'items': []}]},
 'tips': {'count': 242},
 'homeCity': 'New York, NY',
 'bio': 'https://www.tawil.team/nick-el-tawil/',
 'contact': {},
 'photos': {'count': 0, 'items': []},
 'type': 'user',
 'mayorships': {'count': 0, 'items': []},
 'checkins': {'count': 1, 'items': []},
 'lists': {'count': 2,
  'groups': [{'type': 'created', 'count': 0, 'items': []},
   {'type': 'followed', 'count': 0, 'items': []},
   {'type': 'yours',
    'count': 2,
    'items': [{'id': '484542633/todos',
      'name': "Nick's Saved Places",
      'description': '',
      'type': 'todos',
      'editable': False,
      'public': True,
      'collaborative': False,
      'url': '/nickeltawil/list/todos',
      'canonicalUrl': 'https://foursquare.com/nickeltawil/list/todos',
      'listItems': {'count': 0}},
     {'id': '484542633/venuelikes',
      'name': 'Nick’s Liked Places',
      'description': '',
      'type': 'likes',
      'editable': False,
      'public': True,
      'collaborative': False,
      'url': '/nickeltawil/list/venuelikes',
      'canonicalUrl': 'https://foursquare.com/nickeltawil/list/venuelikes',
      'listItems': {'count': 0}}]}]},
 'lenses': []}
In [27]:
# 1. grab prefix of photo
# 2. grab suffix of photo
# 3. concatenate them using the image size  
Image(url='https://igx.4sqi.net/img/user/300x300/484542633_mK2Yum7T_7Tn9fWpndidJsmw2Hof_6T5vJBKCHPLMK5OL-U5ZiJGj51iwBstcpDLYa3Zvhvis.jpg')
Out[27]:

4. Explore a location

https://api.foursquare.com/v2/venues/explore?client_id=CLIENT_ID&client_secret=CLIENT_SECRET&ll=LATITUDE,LONGITUDE&v=VERSION&limit=LIMIT

In [28]:
latitude = 40.715337
longitude = -74.008848

Define URL

In [29]:
url = 'https://api.foursquare.com/v2/venues/explore?client_id={}&client_secret={}&ll={},{}&v={}&radius={}&limit={}'.format(CLIENT_ID, CLIENT_SECRET, latitude, longitude, VERSION, radius, LIMIT)
url
Out[29]:
'https://api.foursquare.com/v2/venues/explore?client_id=POLHESRKW3XHK2RRL43QDI0MTY1IMDPIQYRHVYKTHVQBOAWZ&client_secret=MQEW54YAQYCNSE2C3RMF04TGMUZASA21XCCTE4LVEC3DBJT5&ll=40.715337,-74.008848&v=20180604&radius=500&limit=30'

Send GET request and examine results

In [30]:
import requests
In [31]:
results = requests.get(url).json()
'There are {} around Ecco restaurant.'.format(len(results['response']['groups'][0]['items']))
Out[31]:
'There are 30 around Ecco restaurant.'

Get relevant part of JSON

In [32]:
items = results['response']['groups'][0]['items']
items[0]
Out[32]:
{'reasons': {'count': 0,
  'items': [{'summary': 'This spot is popular',
    'type': 'general',
    'reasonName': 'globalInteractionReason'}]},
 'venue': {'id': '4af5d65ff964a52091fd21e3',
  'name': 'Korin',
  'location': {'address': '57 Warren St',
   'crossStreet': 'Church St',
   'lat': 40.71482437714839,
   'lng': -74.00940425461492,
   'labeledLatLngs': [{'label': 'display',
     'lat': 40.71482437714839,
     'lng': -74.00940425461492}],
   'distance': 73,
   'postalCode': '10007',
   'cc': 'US',
   'neighborhood': 'Tribeca',
   'city': 'New York',
   'state': 'NY',
   'country': 'United States',
   'formattedAddress': ['57 Warren St (Church St)',
    'New York, NY 10007',
    'United States']},
  'categories': [{'id': '4bf58dd8d48988d1f8941735',
    'name': 'Furniture / Home Store',
    'pluralName': 'Furniture / Home Stores',
    'shortName': 'Furniture / Home',
    'icon': {'prefix': 'https://ss3.4sqi.net/img/categories_v2/shops/furniture_',
     'suffix': '.png'},
    'primary': True}],
  'photos': {'count': 0, 'groups': []},
  'venuePage': {'id': '33104775'}},
 'referralId': 'e-0-4af5d65ff964a52091fd21e3-0'}

Process JSON and convert it to a clean dataframe

In [33]:
dataframe = json_normalize(items) # flatten JSON

# filter columns
filtered_columns = ['venue.name', 'venue.categories'] + [col for col in dataframe.columns if col.startswith('venue.location.')] + ['venue.id']
dataframe_filtered = dataframe.loc[:, filtered_columns]

# filter the category for each row
dataframe_filtered['venue.categories'] = dataframe_filtered.apply(get_category_type, axis=1)

# clean columns
dataframe_filtered.columns = [col.split('.')[-1] for col in dataframe_filtered.columns]

dataframe_filtered.head(10)
Out[33]:
name categories address cc city country crossStreet distance formattedAddress labeledLatLngs lat lng neighborhood postalCode state id
0 Korin Furniture / Home Store 57 Warren St US New York United States Church St 73 [57 Warren St (Church St), New York, NY 10007, United States] [{'label': 'display', 'lat': 40.71482437714839, 'lng': -74.00940425461492}] 40.714824 -74.009404 Tribeca 10007 NY 4af5d65ff964a52091fd21e3
1 Takahachi Bakery Bakery 25 Murray St US New York United States at Church St 187 [25 Murray St (at Church St), New York, NY 10007, United States] [{'label': 'display', 'lat': 40.713652845301894, 'lng': -74.0088038953017}] 40.713653 -74.008804 NaN 10007 NY 4c154c9a77cea593c401d260
2 Chambers Street Wines Wine Shop 148 Chambers St US New York United States btwn West Broadway & Hudson St 88 [148 Chambers St (btwn West Broadway & Hudson St), New York, NY 10007, United States] [{'label': 'display', 'lat': 40.715773063928374, 'lng': -74.00971823312332}] 40.715773 -74.009718 NaN 10007 NY 4adcf23cf964a520cc6221e3
3 Juice Press Vegetarian / Vegan Restaurant 83 Murray St US New York United States btwn Greenwich St & W Broadway 202 [83 Murray St (btwn Greenwich St & W Broadway), New York, NY 10007, United States] [{'label': 'display', 'lat': 40.71478769908051, 'lng': -74.0111317502157}] 40.714788 -74.011132 NaN 10007 NY 54148bc6498ea7bb8c05b70a
4 Takahachi Sushi Restaurant 145 Duane St US New York United States btwn W Broadway & Church St 146 [145 Duane St (btwn W Broadway & Church St), New York, NY 10013, United States] [{'label': 'display', 'lat': 40.71652647412374, 'lng': -74.00810108466207}] 40.716526 -74.008101 NaN 10013 NY 4a8f2f39f964a520471420e3
5 Nish Nūsh Falafel Restaurant 88 Reade St US New York United States at Church St 97 [88 Reade St (at Church St), New York, NY 10013, United States] [{'label': 'display', 'lat': 40.71553710116416, 'lng': -74.00772452925565}] 40.715537 -74.007725 NaN 10013 NY 50ba9119e4b071a4bae6dc10
6 Philip Williams Posters Antique Shop 122 Chambers St US New York United States NaN 8 [122 Chambers St, New York, NY 10007, United States] [{'label': 'display', 'lat': 40.71528423132827, 'lng': -74.00878093952018}] 40.715284 -74.008781 NaN 10007 NY 4b747291f964a52042dd2de3
7 Equinox Tribeca Gym 54 Murray Street US New York United States at W Broadway 154 [54 Murray Street (at W Broadway), New York, NY 10007, United States] [{'label': 'display', 'lat': 40.71409860726041, 'lng': -74.0096857179283}] 40.714099 -74.009686 NaN 10007 NY 4a6e331af964a52031d41fe3
8 Little Park American Restaurant 85 W Broadway US New York United States at Chambers St 29 [85 W Broadway (at Chambers St), New York, NY 10007, United States] [{'label': 'display', 'lat': 40.715486585249735, 'lng': -74.00913313510836}] 40.715487 -74.009133 NaN 10007 NY 545c0436498e798e22ce4b2a
9 Mulberry & Vine Café 73 Warren St US New York United States btwn W Broadway & Greenwich St 117 [73 Warren St (btwn W Broadway & Greenwich St), New York, NY 10007, United States] [{'label': 'display', 'lat': 40.71517693966315, 'lng': -74.01022747778285}] 40.715177 -74.010227 NaN 10007 NY 5171b5cc011cef9833bbb787

Let's visualize these items on the map around our location

In [34]:
venues_map = folium.Map(location=[latitude, longitude], zoom_start=15) # generate map centred around Ecco


# add Ecco as a red circle mark
folium.features.CircleMarker(
    [latitude, longitude],
    radius=10,
    popup='Ecco',
    fill=True,
    color='red',
    fill_color='red',
    fill_opacity=0.6
    ).add_to(venues_map)


# add popular spots to the map as blue circle markers
for lat, lng, label in zip(dataframe_filtered.lat, dataframe_filtered.lng, dataframe_filtered.categories):
    folium.features.CircleMarker(
        [lat, lng],
        radius=5,
        popup=label,
        fill=True,
        color='blue',
        fill_color='blue',
        fill_opacity=0.6
        ).add_to(venues_map)

# display map
venues_map
Out[34]:

https://api.foursquare.com/v2/venues/trending?client_id=CLIENT_ID&client_secret=CLIENT_SECRET&ll=LATITUDE,LONGITUDE&v=VERSION

In [35]:
# define URL
url = 'https://api.foursquare.com/v2/venues/trending?client_id={}&client_secret={}&ll={},{}&v={}'.format(CLIENT_ID, CLIENT_SECRET, latitude, longitude, VERSION)

# send GET request and get trending venues
results = requests.get(url).json()
results
Out[35]:
{'meta': {'code': 200, 'requestId': '5ccac6b16a60712126bd7238'},
 'response': {'venues': []}}
In [36]:
if len(results['response']['venues']) == 0:
    trending_venues_df = 'No trending venues are available at the moment!'
    
else:
    trending_venues = results['response']['venues']
    trending_venues_df = json_normalize(trending_venues)

    # filter columns
    columns_filtered = ['name', 'categories'] + ['location.distance', 'location.city', 'location.postalCode', 'location.state', 'location.country', 'location.lat', 'location.lng']
    trending_venues_df = trending_venues_df.loc[:, columns_filtered]

    # filter the category for each row
    trending_venues_df['categories'] = trending_venues_df.apply(get_category_type, axis=1)
In [ ]:
# display trending venues
trending_venues_df

Now, depending on when you run the above code, you might get different venues since the venues with the highest foot traffic are fetched live.

In [37]:
if len(results['response']['venues']) == 0:
    venues_map = 'Cannot generate visual as no trending venues are available at the moment!'

else:
    venues_map = folium.Map(location=[latitude, longitude], zoom_start=15) # generate map centred around Ecco


    # add Ecco as a red circle mark
    folium.features.CircleMarker(
        [latitude, longitude],
        radius=10,
        popup='Ecco',
        fill=True,
        color='red',
        fill_color='red',
        fill_opacity=0.6
    ).add_to(venues_map)


    # add the trending venues as blue circle markers
    for lat, lng, label in zip(trending_venues_df['location.lat'], trending_venues_df['location.lng'], trending_venues_df['name']):
        folium.features.CircleMarker(
            [lat, lng],
            radius=5,
            poup=label,
            fill=True,
            color='blue',
            fill_color='blue',
            fill_opacity=0.6
        ).add_to(venues_map)
In [38]:
# display map
venues_map
Out[38]:
'Cannot generate visual as no trending venues are available at the moment!'

Thank you for completing this lab!

This notebook was created by Alex Aklson. I hope you found this lab interesting and educational. Feel free to contact me if you have any questions!

This notebook is part of a course on Coursera called Applied Data Science Capstone. If you accessed this notebook outside the course, you can take this course online by clicking here.


Copyright © 2018 Cognitive Class. This notebook and its source code are released under the terms of the MIT License.