BestTime beginners guide - Part 2 Software API

In Part 1 of this tutorial, we wrote how we can use the different BestTime.app website tools to add and analyze foot traffic data. In part 1 we first added bars to our user account. We used the Radar tool to filter the bars based on the amount of foot traffic on a specific day, time, and location. For example, we showed how to only show all the bars that are busy on Friday from 9 PM until 11 PM in the Mission District in San Fransisco. In Part 2 we are going to take the same steps but now using the software API.

What is BestTime.app?

A quick intro: BestTime.app is a data (API) service that forecasts how busy a public business (e.g. shop, restaurant, gym, etc) will be at any given hour of the week using foot traffic data. The data is provided for 150+ countries using anonymous phone signals. Foot traffic forecasts are based on average visits over the past weeks. Busyness for any given hour is predicted relative to the biggest peak of the week for this business. The foot traffic data is presented as percentages for each hour of the week from 0% (empty/ closed) to 100% (visitor peak of week). Additional BestTime functionality:

  • Live updates if a venue is more or less busy than normal (real-time).
  • Search venue foot traffic based on category (e.g. supermarkets in London) or name (e.g. McDonalds in San Fransisco).
  • Filter venues in a whole area based on foot traffic visitor peaks, day, time, and business type, location, and more.
  • Integrate all data directly into your applications/ research using the developer REST-API.

You could compare it with a supercharged FourSquare foot traffic data/ Google Popular Times API with more footfall data analytic functionality.


This software API tutorial is written for BestTime.app beginners, but you need to have software engineering and API knowledge (otherwise we recommend using the website tools from Part 1. The code examples in this tutorial are written in Javascript and Python. In this tutorial, we explain the basics but refer often to API endpoints in the API documentation. As an alternative to the Javascript and Python code, you can also use Postman to test the API. We created a Postman collection file that you can download and import into Postman.

API Keys

Create a new account to start with 100 free API credits. We recommend upgrading to the $9/ month 'Basic metered' plan to get started. This includes 450 API credits per month.  To save money with higher volumes of API calls we recommend the Pro metered plan, or the packages that have fixed pricing.

To start using the API you will need the private and public API keys. You can access the API Key management page from your account settings page. You can find more information on the API keys in the API documentation.

We test our API connection using the API key endpoint. API verification with the BestTime API is always done by inserting the API key in the URL. Later we will use other parameters to configure our API request. These parameters will also need to be sent through the URL.

fetch(
`https://besttime.app/api/v1/keys/pri_a00de9e302662c0217a9cf08ab304122`, {
  method: 'GET'
}).then(function(data) { 
	console.log(data); 
});
Javascript code to check your API key details
import requests

url = "https://besttime.app/api/v1/keys/pri_a00de9e302662c0217a9cf08ab304122"

response = requests.request("GET", url)

print(response.json())
Python code to check your API key details

If the API key is valid you will see a JSON response like this:

 {
    "active": true,
    "api_key_private": "pri_a00de9e302662c0217a9cf08ab304122",
    "api_key_public": "pub_e11661721b084d36b8f469a2c012e754",
    "credits_forecast": 100,
    "credits_query": 100,
    "status": "OK",
    "valid": true
  }
JSON response

Getting foot traffic data

The different methods are almost similar to using the website tools. In fact, the website tools are built on top of the same API endpoints we will discuss now. There are different methods to add venue foot-traffic data to your account using the API. Here a small overview first:

  • Single venue using the name and address
    If you have a single or a list of bars in you can add them by name and address using the New foot traffic forecast API endpoint (e.g. 'Alchemist Bar & Lounge - 679 3rd St, San Francisco, CA' ).
  • Multiple venues using the 'Venue Search' tool
    If you don't have specific bar names in mind but are looking for e.g. bars in a neighborhood or city you can use the Venue Search API endpoint. The Venue Search result returns only 20 (default) to 200 (max) venues (this number includes venues that do not have foot-traffic data). So this tool only returns a limited amount of venues with each result. You can cover bigger areas by searching multiple times in different areas. You could for example search for bars in each individual neighborhood.

BestTime only adds a venue to your account if a venue had enough foot-traffic data in the past weeks. It doesn't really matter which method you choose to add venues to your account, and you can even combine methods. Once venues are added to your account we can use the Radar tool (or Venue Filter for the software API) to filter bars in a whole area for example on foot-traffic intensity, day of the week, time, etc. Filtering existing added venues is much faster.

Add a single venue using the name and address

The New Foot Traffic Forecast API endpoint is used to add venues to your account using the name and address of the venue.

import requests

url = "https://besttime.app/api/v1/forecasts"

params = {
    'api_key_private': 'pri_50990bf1f8828f6abbf6152013113c6b',
    'venue_name': 'Novela',
    'venue_address': '662 Mission St San Francisco, CA 94105 United States'
}

response = requests.request("POST", url, params=params)
print(response.json())
Python code to add a single venue to your account by name and address
const params = new URLSearchParams({ 
 'api_key_private': 'pri_50990bf1f8828f6abbf6152013113c6b',
  'venue_name': 'Novela',
  'venue_address': '662 Mission St San Francisco, CA 94105 United States'
});

fetch(`https://besttime.app/api/v1/forecasts?${params}`, {
  method: 'POST'
}).then(function(data) { 
  console.log(data); 
});
Javascript code to add a single venue to your account by name and address

If BestTime was able to generate foot traffic data for the given venue it will look like the JSON response below. The JSON response is shortened since the whole response is too long to display here. Only limited data for Friday is shown, but normally it includes data for all days of the week. Click here for a full JSON response example. Check the API documentation for more explanation about the response data. Notice that besides foot traffic data and analyses the response also includes the venue_id. The venue_id can be used to get data from an existing forecast, without refreshing the foot traffic forecast (more discussed in Query foot traffic data).

{
    "status": "OK",
    "epoch_analysis": "1619165472",
    "venue_info": {
        "venue_id": "ven_73736634746861716337745241596841324856536453344a496843",
        "venue_name": "Novela",
        "venue_address": "662 Mission St San Francisco, CA 94105 United States",
        "venue_timezone": "America/Los_Angeles"
    },
    "analysis": [
        {
            "day_info": {
                "day_int": 4,
                "day_text": "Friday",
                "venue_open": 17,
                "venue_closed": 2,
                "day_rank_mean": 1,
                "day_rank_max": 1,
                "day_mean": 61,
                "day_max": 100
            },
            "busy_hours": [
                20,
                21,
                22,
                23,
                0
            ],
            "quiet_hours": [
                2
            ],
            "peak_hours": [
                {
                    "peak_start": 18,
                    "peak_max": 22,
                    "peak_end": 1,
                    "peak_intensity": 5,
                    "peak_delta_mean_week": 60
                }
            ],
            "surge_hours": {
                "most_people_come": 17,
                "most_people_leave": 1
            },
            "day_raw": [
              0,0,0,0,0,0,0,0,0,10,20,25,20,40,50,50,90,85,0,0,0,0,0,0
            ],
            "hour_analysis": [
                ...
                {
                    "hour": 18,
                    "intensity_txt": "Average",
                    "intensity_nr": 0
                },
                {
                    "hour": 19,
                    "intensity_txt": "Average",
                    "intensity_nr": 0
                },
                {
                    "hour": 20,
                    "intensity_txt": "Above average",
                    "intensity_nr": 1
                },
                {
                    "hour": 21,
                    "intensity_txt": "High",
                    "intensity_nr": 2
                },
                {
                    "hour": 22,
                    "intensity_txt": "High",
                    "intensity_nr": 2
                },
                {
                    "hour": 23,
                    "intensity_txt": "High",
                    "intensity_nr": 2
                },
                {
                    "hour": 0,
                    "intensity_txt": "Above average",
                    "intensity_nr": 1
                },
                {
                    "hour": 1,
                    "intensity_txt": "Average",
                    "intensity_nr": 0
                },
                ...
            ]
        }
    ],
    "api_key_private": "pri_50990bf1f8828f6abbf6152013113c6b"
}
JSON response for a new foot traffic forecast request. The response includes different analyses. In this example, only data for some hours on Friday is shown. The whole response includes data for every hour of the week.

Also checkout the HTML - Javascript examples page to build your own visualisations like the one below.

If BestTime cannot provide foot traffic data for the requested address you will get an error message. The error message could contain that the venue is found but there is not enough data to provide foot traffic data. The error response could also indicate that the venue itself could not be found. Please check the address again. It could also respond that the venue could not be found if the address is not specific enough; e.g. there are multiple venues with the same name in a city. Later in the 'Venue Search' section we discuss how to add multiple venues at the same time to our account.

If you have a list of bars by name and address we can also simply create a loop to add multiple bars to your account (Python code only)

import requests

url = "https://besttime.app/api/v1/forecasts"

# List with multiple venues in tuples
venue_list = [
    ("Alchemist Bar & Lounge","3rd street, San Fransisco"),
    ("Zeitgeist","Valencia St, San Fransisco")
]

for venue in venue_list:

    params = {
        'api_key_private': 'pri_50990bf1f8828f6abbf6152013113c6b',
        'venue_name': venue[0],
        'venue_address': venue[1]
    }

    response = requests.request("POST", url, params=params)
    print(response.json())

Query existing foot traffic data

Each time we use one of the API endpoints to get New foot-traffic data the generated foot traffic data is stored for a certain amount of days on your account. We can refresh the foot traffic by calling the same new foot traffic API endpoint. If we just need the same foot traffic data without generating fresh foot traffic predictions we could use the 'Query' API endpoints. These endpoints are cheaper (1 API credit vs 2 for new fresh foot traffic data) and respond much faster. Most query endpoints will be called using the HTTP 'GET' method as opposed to the 'New' foot traffic endpoints that require a HTTP 'POST' method'.

To query data from a venue you can use the venue_id from the 'New foot traffic data' response, or we can get a list of all the venue_id's in our account using the Venues API endpoint.

import requests

url = "https://besttime.app/api/v1/venues"

params = {
    'api_key_private': 'pri_50990bf1f8828f6abbf6152013113c6b'
}

response = requests.request("GET", url, params=params)
print(response.json())
Python code for the Venues endpoint
fetch(`https://besttime.app/api/v1/venues?api_key_private=pri_50990bf1f8828f6abbf6152013113c6b`, {
  method: 'GET'
}).then(function(data) { 
  console.log(data); 
});
Javascript code for the Venues endpoint
[
    {
        "venue_address": "199 Valencia St San Francisco, CA 94103 United States",
        "venue_forecasted": true,
        "venue_id": "ven_554a4d72466171666f517a5241346a2d426951393838554a496843",
        "venue_name": "Zeitgeist"
    },{
        "venue_address": "679 3rd St San Francisco, CA 94107 United States",
        "venue_forecasted": true,
        "venue_id": "ven_6b4458536e714c784a54435241346a5f5a4e4e716459584a496843",
        "venue_name": "Alchemist Bar & Lounge"
    }
]
Venues endpoint JSON response (only 2 venues are displayed for demonstration purposes).

Using the Query endpoints you can get specific information like when the peak of the day starts, or which hours on Wednesday are quiet. Click here to see a list of all the Query endpoints.

The query endpoints not only return existing foot traffic data from your account but also provide additional analyses based on this data. The 'New foot traffic data API endpoint provides only static forecast data for the whole week. Most of the Query endpoints also include dynamic data in the API response. When you are for example interested to know which hour is predicted to be most busy you could use the Query Peaks endpoint. This endpoint does not only tell you at what time the peak hour is but will also dynamically tell you the time left until this moment: e.g. '2 Hours and 40 Minutes' until the peak of the day. It will also tell you if the peak is still coming or did already pass.

A new foot traffic data request provides the analyses for a whole week in one response. If you, for example, would like to quickly know how busy it is predicted to be at this moment you could use the 'Now' or 'Now Raw' endpoint. The 'Raw' endpoints return foot traffic data as a percentage from 0 to 100%, while the non-raw version of the endpoint returns textual information (e.g. 'above average' foot traffic as prediction).

Remember the query endpoints do not refresh the forecast foot traffic data itself. However, as you can see in the API documentation, it is possible to get data in the format of a specific query endpoint (e.g. Query Peaks) and refresh the forecast in one API call. This can be done by calling the API endpoint using the HTTP 'POST method instead of the 'GET' method. Remember this costs more API credits and takes longer to respond.

Live data

The previous API call returned foot traffic data based on foot traffic forecasts. BestTime has also live (real-time) data for a select number of venues (i.e. not all venues with foot traffic forecast data have live data). Additionally, live data is only available during the opening hours, whereas forecasts and query data can be retrieved regardless of the opening hours. Using the Live foot traffic data API endpoint we can get real-time foot traffic information:

import requests

url = "https://besttime.app/api/v1/forecasts/live"

params = {
    'api_key_private': 'pri_50990bf1f8828f6abbf6152013113c6b',
    'venue_name': 'McDonalds',
    'venue_address': 'Ocean Ave, San Fransisco'
}

response = requests.request("POST", url, params=params)
print(response.json())
const params = new URLSearchParams({ 
  'api_key_private': 'pri_50990bf1f8828f6abbf6152013113c6b',
  'venue_name': 'McDonalds',
  'venue_address': 'Ocean Ave, San Fransisco'
});

fetch(`https://besttime.app/api/v1/forecasts/live?${params}`, {
  method: 'POST'
}).then(function(data) { 
  console.log(data); 
});
{
    "analysis": {
        "venue_forecasted_busyness": 60,
        "venue_live_busyness": 20,
        "venue_live_busyness_available": true,
        "venue_live_forecasted_delta": -40
    },
    "status": "OK",
    "venue_info": {
        "venue_current_gmttime": "Friday 2021-04-23 07:19AM",
        "venue_current_localtime": "Friday 2021-04-23 03:19PM",
        "venue_id": "ven_51387131543761435650505241346a394a6432395362654a496843",
        "venue_name": "McDonald's",
        "venue_timezone": "America/Los_Angeles"
    }
}

The venue_live_busyness value contains a percentage indicating the foot traffic intensity for this hour. The forecast foot traffic percentages only go from 0% up to 100%, while the live data ranges from 0% to well above 100%. As mentioned before, 100% is the busiest peak of the week for a venue based on venue visits from the past weeks (for a bar this could be e.g. Friday 10 PM).

If a venue has for example an event and is much more busy than normal (compared to the busiest hours from the past week), the live value indicates a value above 100%. If there are now for example 50% MORE people than the busiest hour from last week the venue_live_forecasted_delta will indicate 50% and the venue_live_busyness will indicate 150%.  

In the response above the live data venue_live_busyness indicates that it is only 20% busy at this moment (Friday 3 PM).  The forecast for Friday 3 PM indicates venue_forecasted_busyness is it is normally 60% busy. The venue_live_forecasted_delta , therefore, indicates it is 40% LESS busy than normal (60% - 20%).

When the venue is closed or does not have live data at all the API call will return with an error message that there is no live data available at this moment. BestTime cannot tell you in advance which venues do have live data. The data is generated by comparing foot traffic data from the current hour at the venue with foot traffic data from the past weeks. This automated calculation depends on traffic volumes from the past week and current hour to determine if there is enough statistical power to display foot traffic data.  You might have more luck when trying it another time or day when there is no data at this moment.

Adding multiple venues using the 'Venue Search' tool

Let's say we don't know exactly the names of the bars, but we want to add some bars in the Mission District of San Fransisco to our account.  We will use the 'Venue Search' API endpoint for this. This endpoint will accept text inputs (q parameter) like 'bars in Mission District San Fransisco' or 'Whole Foods Market San Fransisco'. As you can see from the example text inputs, you can add multiple venues by either specifying a type of venue (e.g. bar) or a name of a chain (e.g. Whole Foods Market, or McDonald's).

Text input parameter q also needs a location in combination with the type or name. This could be anything ranging from a country, or a city, to a neighborhood. So in hour bar example, we set q to bars in Mission District San Fransisco. A location in q is always required unless a coordinate lat , lng and a radius is provided in the request. Otherwise, BestTime doesn't know where to search for your desired venues. You could for example use the coordinates of the Mission District (or the user's coordinates as input) in combination with q=bars to search for bars in that neighborhood without typing Mission District in q.

Multiple API endpoints are involved from entering a venue search input until returning foot-traffic data for the found venues. The Venue Search model will look up venues in the background and will forecast them subsequently (using the New Foot Traffic Forecast endpoint). Remember that this will therefore also result in additional forecast API credit usage. The endpoint will reply with a background task URL, job_id, and a collection_id (see Collections). You can poll the Venue Search Progress endpoint to poll to background job progress. We will now search for a maximum of 20 bars in the Mission District of San Fransisco.

The higher the number of requested venues num the longer it takes for the search to complete. By default, the search speed method is set to fast=true.  To save free API credits we set the fast=fasle. Searching with the fast method is charged with more API credits (5x more at the time of writing). The fast method is limited to a maximum num of 60. Selecting a higher number will automatically use the normal speed method. Select false to save on API credits or to search for more than 60 venues at the same time. See API Credits for more info.

import requests

url = "https://besttime.app/api/v1/venues/search"

params = {
    'api_key_private': 'pri_50990bf1f8828f6abbf6152013113c6b',
    'q': 'bars in Mission District San Fransisco',
    'num': 20,
    'fast': False
}

response = requests.request("POST", url, params=params)
print(response.json())
Python code to search for 'Bars in Mission District San Fransisco' foot traffic data
const params = new URLSearchParams({ 
  'api_key_private': 'pri_50990bf1f8828f6abbf6152013113c6b',
  'q': 'bars in Mission District San Fransisco',
  'num': 20,
  'fast': false
});

fetch(`https://besttime.app/api/v1/venues/search?${params}`, {
  method: 'POST'
}).then(function(data) { 
  console.log(data); 
});
Javascript code to search for 'Bars in Mission District San Fransisco' foot traffic data
{
    "_links": {
        "venue_search_progress": "https://besttime.app/api/v1/venues/progress?job_id=e0880f28-3a19-4871-a355-4ca21f10c2c8&collection_id=col_ac734e76ad2d4696a5a66541c67587e8"
    },
    "collection_id": "col_ac734e76ad2d4696a5a66541c67587e8",
    "job_id": "e0880f28-3a19-4871-a355-4ca21f10c2c8",
    "status": "OK"
}
Venue Search JSON example response. BestTime will reply with a link to the search progress that happens in the background. 

By repeatedly checking the Venue Search progress API endpoint get information on the progress of the background venue search job. You can do that using a normal API call or for now, just open the link from the previous response in your browser.

{
    "collection_id": "col_5c546908473645c1b9bad36b7fef7765",
    "count_completed": 12,
    "count_failure": 0,
    "count_forecast": 10,
    "count_live": 5,
    "count_total": 20,
    "job_finished": false,
    "job_id": "eb6aa504-e147-4b2d-a191-03d721764279",
    "status": "OK"
}

Once the background job is complete the same Venue Search Progress endpoint will give a JSON response like this:

{
    "_links": {
        "background_progress_api": "http://besttime.app/api/v1/venues/progress?job_id=eb6aa504-e147-4b2d-a191-03d721764279&ven=False",
        "background_progress_tool": "http://besttime.app/api/v1/misc/addarea_progress?q=bars+in+mission+disctrict+san+fransisco&job_id=eb6aa504-e147-4b2d-a191-03d721764279&map_lat=37.7602249&map_lng=-122.4163186&lat_min=37.7504268&lat_max=37.770023&lng_min=-122.424317&lng_max=-122.4083202&map_zoom=15&radius=2594&collection_id=col_5c546908473645c1b9bad36b7fef7765&api_key_private=pri_50990bf1f8828f6abbf6152013113c6b&live_refresh=False&auto_continue=1",
        "job_id": "eb6aa504-e147-4b2d-a191-03d721764279",
        "radar_tool": "http://besttime.app/api/v1/radar/filter?q=bars+in+mission+disctrict+san+fransisco&map_lat=37.7602249&map_lng=-122.4163186&lat_min=37.7504268&lat_max=37.770023&lng_min=-122.424317&lng_max=-122.4083202&map_z=15&collection_id=col_5c546908473645c1b9bad36b7fef7765&api_key_private=pri_50990bf1f8828f6abbf6152013113c6b&live_refresh=False",
        "venue_filter_api": "http://besttime.app/api/v1/venues/filter?lat_min=37.7504268&lat_max=37.770023&lng_min=-122.424317&lng_max=-122.4083202&collection_id=col_5c546908473645c1b9bad36b7fef7765&api_key_private=pri_50990bf1f8828f6abbf6152013113c6b&live_refresh=False"
    },
    "bounding_box": {
        "lat": 37.7602249,
        "lat_max": 37.770023,
        "lat_min": 37.7504268,
        "lng": -122.4163186,
        "lng_max": -122.4083202,
        "lng_min": -122.424317,
        "map_zoom": 15,
        "radius": 2594
    },
    "collection_id": "col_5c546908473645c1b9bad36b7fef7765",
    "count_completed": 20,
    "count_failure": 0,
    "count_forecast": 16,
    "count_live": 12,
    "count_total": 20,
    "job_finished": true,
    "job_id": "eb6aa504-e147-4b2d-a191-03d721764279",
    "radar_circles": [],
    "status": "OK",
    "venues": [
        {
            "forecast": true,
            "processed": true,
            "venue_address": "842 Valencia St, San Francisco, CA 94110",
            "venue_lat": 37.7594112,
            "venue_lon": -122.4215672,
            "venue_name": "The Beehive"
        },
        {
            "forecast": true,
            "processed": true,
            "venue_address": "540 Valencia St, San Francisco, CA 94110",
            "venue_lat": 37.764216,
            "venue_lon": -122.4221214,
            "venue_name": "Blondie's Bar"
        },
        {'... Other venues hidden ....'}
    ],
    "venues_n": 20
}

The response includes the found venue names, how many venues were found, how many venues have foot traffic data, etc. The response also includes the coordinates of the area the venues are located in. This is useful when showing the venues on a map. Both bounding box coordinates and the center of the map coordinate are provided.

The response does not include foot traffic data itself, but it provides two different links to get the foot traffic data: the venue_filter_api and radar_tool links. As the name suggests, when you open the radar_tool link in your web browser you will see the search results in the BestTime Radar tool (we discussed the Radar tool in Part 1 of this tutorial). The venue_filter_api link requests the foot traffic data through the Venue Filter API endpoint.

http://besttime.app/api/v1/venues/filter?lat_min=37.7504268&lat_max=37.770023&lng_min=-122.424317&lng_max=-122.4083202&collection_id=col_5c546908473645c1b9bad36b7fef7765&api_key_private=pri_50990bf1f8828f6abbf6152013113c6b&live_refresh=False

In the next section, we discuss the Venue Filter itself. The Radar Tool makes also use if this API under the hood. When requesting foot traffic data through the Venue Filter you will see a response similar to this:

{
    "status": "OK",
    "venues": [
        {
            "day_info": {
                "day_int": 4,
                "day_max": 100,
                "day_mean": 59,
                "day_rank_max": 1,
                "day_rank_mean": 1,
                "day_text": "Friday",
                "venue_closed": 0,
                "venue_open": 16
            },
            "day_int": 4,
            "day_raw": [
              0,0,0,0,0,0,0,0,0,10,20,25,35,40,50,65,90,85,0,0,0,0,0,0
            ],
            "price_level": 3,
            "rating": 4.5,
            "reviews": 347,
            "venue_address": "842 Valencia St San Francisco, CA 94110 United States",
            "venue_id": "ven_383155576972546b584e775241346a2d3154712d65536b4a496843",
            "venue_lat": 37.7594112,
            "venue_lng": -122.4215672,
            "venue_name": "The Beehive",
            "venue_type": "BAR"
        },
        {
            "day_info": {
                "day_int": 4,
                "day_max": 88,
                "day_mean": 47,
                "day_rank_max": 2,
                "day_rank_mean": 2,
                "day_text": "Friday",
                "venue_closed": 0,
                "venue_open": 15
            },
            "day_int": 4,
            "day_raw": [
             0,0,0,0,0,0,0,0,5,20,30,25,40,40,50,80,100,90,0,0,0,0,0,0
            ],
            "price_level": 2,
            "rating": 4.2,
            "reviews": 603,
            "venue_address": "540 Valencia St San Francisco, CA 94110 United States",
            "venue_id": "ven_386c5a45674363687575345241346a2d4a4357395934524a496843",
            "venue_lat": 37.764216,
            "venue_lng": -122.4221214,
            "venue_name": "Blondie's Bar",
            "venue_type": "BAR"
        },
        {'...Other bars hidden to save space in this tutorial example ...'}
    "venues_n": 16,
    "window": {
        "day_window": "Friday 6AM until Saturday 5AM",
        "day_window_end_int": 5,
        "day_window_end_txt": "Saturday",
        "day_window_start_int": 4,
        "day_window_start_txt": "Friday",
        "time_local": 23,
        "time_local_12": "11PM",
        "time_local_index": 17,
        "time_window_end": 5,
        "time_window_end_12h": "5AM",
        "time_window_end_ix": 23,
        "time_window_start": 6,
        "time_window_start_12h": "6AM",
        "time_window_start_ix": 0
    }
}

In the above example, the foot traffic data is displayed under the day_raw array. The day_raw consists out of 24 hours. By default, the Venue Filter tool responds with data for the current day of the week with the BestTime day window (6 AM until 5 AM next day.

Filter venues on foot traffic data, day, location & more

In the previous section, we searched for bars and got the foot traffic data through the Venue Filter API Endpoint. The Venue Filter API endpoint basically returns the foot traffic data of all venues in a specified geographic location, but makes it possible to filter the venues on e.g. foot traffic intensity, day of the week, time of the day, venue type, personal collections, ratings, number of reviews, and price levels. Due to these filter capabilities, it is a very powerful tool to only find venues that meet your criteria.

We can use this API endpoint to find within e.g. the busy bars on Friday from 9 PM until 11 PM on. How do we define 'busy'? As discussed before, BestTime indicates foot traffic intensity per hour as a percentage from 0 to 100%, wherein 100% is the hour with foot traffic peak of the week for a specific venue. For our bar use-case we tell the Venue filter to only show bars that are at least 70% busy compared to the peak of the week. As you can see from the Venue Filter API documentation we need to set the busy_min parameter to 70 to achieve this.

By default, the Venue Filter API will return with foot-traffic data for the current day of the week.  We set the day_int to 4 (Monday 0 to Sunday 6). We set the hour_min to 21 and the hour_max to 23  to only receive foot traffic data from 9 PM until 11 PM. Note that the API inputs are in 24-hour format.

It depends on your free/subscription plan how many API credits are charged for your Venue Filter API call. At the time of writing, the Free and Basic metered plans charge 1 API credit per 10 found Venues, and the Pro charges 1 API credit per 100 found Venues.

The Venue Filter endpoint requires geographical location parameters or a collection_id. When using the location parameters either a combination of a coordinate lat, lng, with a radius or  the bounding box parameters lat_min, lng_min, lat_max, and lng_max are required. If you just want to get all venues you could set the values to cover the whole world you could set the bounding box values to the maximum possible values lat_min=-90, lat_max=90, lng_min=-180, and lng_max=180 degrees. In the example API call below we use the coordinates of San Fransisco in combination with a radius of 20000 meters (approx 65617 feet).

import requests

url = "https://besttime.app/api/v1/venues/filter"

params = {
    'api_key_private': 'pri_50990bf1f8828f6abbf6152013113c6b',
    'busy_min': 70,
    'hour_min': 21,
    'hour_max': 23,
    'lat': 37.7609314,
    'lng': -122.4480066,
    'radius': 20000
}

response = requests.request("GET", url, params=params)
print(response.json())
const params = new URLSearchParams({ 
    'api_key_private': 'pri_50990bf1f8828f6abbf6152013113c6b',
    'busy_min': 70,
    'hour_min': 21,
    'hour_max': 23,
    'lat': 37.7609314,
    'lng': -122.4480066,
    'radius': 20000
});

fetch(`https://besttime.app/api/v1/venues/filter?${params}`, {
  method: 'GET'
}).then(function(data) { 
  console.log(data); 
});

The Venue Filter will return a response like the JSON structure below. As you can see the raw_data array only includes foot traffic data for the hours from 9 PM until and including 11 PM. To be more precise, when we talk about foot traffic for 9 PM it means from 9:00 PM until 9:59 PM. So in this case the three values from the raw_data array (e.g. 75, 60, 40) are an indication of the foot traffic intensity from 9:00 PM until 11:59 PM.

{
    "status": "OK",
    "venues": [
        {
            "day_info": {
                "day_int": 4,
                "day_max": 77,
                "day_mean": 39,
                "day_rank_max": 2,
                "day_rank_mean": 2,
                "day_text": "Friday",
                "venue_closed": 2,
                "venue_open": 15
            },
            "day_int": 4,
            "day_raw": [
                75,
                60,
                40
            ],
            "price_level": 2,
            "rating": 4.3,
            "reviews": 616,
            "venue_address": "679 3rd St San Francisco, CA 94107 United States",
            "venue_id": "ven_6b4458536e714c784a54435241346a5f5a4e4e716459584a496843",
            "venue_lat": 37.7792848,
            "venue_lng": -122.3930404,
            "venue_name": "Alchemist Bar & Lounge",
            "venue_type": "BAR"
        },
        {
            "day_info": {
                "day_int": 4,
                "day_max": 100,
                "day_mean": 61,
                "day_rank_max": 1,
                "day_rank_mean": 1,
                "day_text": "Friday",
                "venue_closed": 2,
                "venue_open": 17
            },
            "day_int": 4,
            "day_raw": [
                85,
                100,
                90
            ],
            "price_level": 2,
            "rating": 4.3,
            "reviews": 1016,
            "venue_address": "662 Mission St San Francisco, CA 94105 United States",
            "venue_id": "ven_73736634746861716337745241596841324856536453344a496843",
            "venue_lat": 37.787097,
            "venue_lng": -122.40136,
            "venue_name": "Novela",
            "venue_type": "BAR"
        }
    ],
    "venues_n": 2,
    "window": {
        "day_window": "Friday 9PM until Friday 11PM",
        "day_window_end_int": 4,
        "day_window_end_txt": "Friday",
        "day_window_start_int": 4,
        "day_window_start_txt": "Friday",
        "time_local": 1,
        "time_local_12": "1AM",
        "time_local_index": 19,
        "time_window_end": 23,
        "time_window_end_12h": "11PM",
        "time_window_end_ix": 17,
        "time_window_start": 21,
        "time_window_start_12h": "9PM",
        "time_window_start_ix": 15
    }
}

By adding the now=true value to the Venue Filter API call the endpoint will return foot traffic data for the current hour of the venue. Also, all the filters (like minimum foot traffic intensity) are applied at this hour.

Sorting and paging

By default the Venue Filter API orders venues by number of reviews (descending from high to low) to place the more established venues on top of the list. This can be changed by setting order_by to one of the following values: reviews, rating, price_level, live, now, name, day_rank_max, day_rank_mean, day_max, day_mean, date,  dwell_time_min, dwell_time_max, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,14, 15, 16, 17, 18, 19, 20, 21, 22, 23.   Some values are self-explaining or can be searched in the API documentation. By default the order will be descending (from high to low). Using the order parameter the order can be set to desc (descending - from high to low) or asc (ascending - from low to high)(not to be confused with order_by).

The live , now , and numbers from 0 to 23 will order the venues based on foot traffic intensity. So when e.g. using now the venues with the highest forecasted foot traffic at this hour (local time of the geographic filter area) will be shown on top. The numbers represent the 24 hours in a day in 24 hour format, and can be used to show venues with a foot traffic at a specific hour of the day. This is for example useful to e.g. find and show venues on top that are busy late at night.

See the response attributes of week overview API documentation for information regarding the day_rank_max,day_rank_mean,day_max,day_mean order values. The dwell time values order the venues based on how long visitors stay on average at a venue.

It is also possible to order venues by two values using the comma as seperator (without space) e.g. order_by=22,reviews. This will show venues with the highest foot traffic at 22:00 hour (10 PM) on top, and uses reviews as secondary order. The order parameter accepts also two comma seperated values - e.g. order=asc,desc - in case two order_by values are given.

By default the number of returned venues by the Venue Filter endpoint is set to 5000. In a lot of application use-case you might only want to show e.g. the top 20 venues, and include some sort of paging. This can be done by setting limit=20 and set page to the desired page number. The maximum limit per page is 10.000 venues. Note: The higher the number, the slower the response and the more API credits are used.

Filter live foot traffic data

The venue filter API endpoint also supports filtering on live foot traffic data using the live=true parameter in the API call.  Instead of calling the Live foot traffic endpoint for each individual venue, you can use the venue filter endpoint to get live data of multiple venues in one API call. When filtering on e.g. the live data in combination with the busy_min value we can easily find all bars that have actually foot traffic at this moment instead of depending on a forecast based on foot traffic data from the past weeks.

By default, this live data is not refreshed in order to save API credits and increase speed significantly. It is up to the user to decide how often this data is refreshed. The live data can be updated individually for each venue using the Live foot traffic endpoint. You can also pass  live_refresh=true as an additional parameter in the Venue Filter API call to get fresh live data of multiple venues.

Only venues that meet the venue filter criteria without the live parameter will be refreshed (e.g. filtered on location, collection, rating, reviews and price level). This only does not apply to the foot traffic intensity filters ( busy_min and busy_max). So if you e.g. filter on venues in a specific neighborhood with at least 1000 reviews,  with the minimum foot traffic intensity set to 50% ( busy_min=50), then all venues found in that neighborhood with that amount of reviews will be refreshed in the background, regardless of the forecasted foot traffic data values.

Once the live data is refreshed in the background the foot traffic intensity filters busy_min and busy_max are applied on the LIVE foot traffic values.  We are planning to add additional functionality in the future to filter based on the live_delta value. This way you can e.g. filter e.g. only display bars that are 40% more busy than usual. Please send us a message if you want this feature.

Adding filters to the Venue Search tool

As you can see in the Documentation the Venue Search endpoint also accepts most filter parameters similar to the Venue Filter API endpoint (e.g. busy_min and hour_min). It must be clarified that the Venue Search itself does not filter venues based on these filter parameters. The Venue Search endpoint searches venues and will create a link to the Venue Filter API endpoint including those filters!

When you use the Venue Search endpoint to search for q=Bars in Mission District in San Fransisco in combination with busy_min=70, the will return all found bars in the mission district and add the ones with foot traffic data to your account. It will include a link to the Venue Filter API endpoint including the busy_min=70 filter.  

Besides the filter parameters, the Venue Search endpoint tries to understand textual filters from the text input q. When searching for example q=Busy bars in Mission District San Fransisco on Friday from 9 PM until 11 PM, it will search for bars in Mission Disctrict San Fransisco and converts the rest of the text into venue filters: busy_min=50 (at least 50% busy), day_int=4 (Friday), hour_min=21 (9PM), hour_max=23 (11 PM).

For more advanced options and info check Natural language in the search query as filters. As mentioned before the Venue Search tool is not extremely fast, but is useful when you don't know in advance which venues are desired. For use-cases that require faster performance, we recommend initially adding all desired venues once to your account. After that, you should only use the faster Venue Filter API endpoint to quickly filter your desired venues. We also recommend updating the foot traffic forecasts every 2 to 4 four weeks, since the Venue Filter API endpoint doesn't do this (it only filters existing foot traffic data added to your account).


Next steps

Hopefully, this API tutorial helped you understand how most commonly used API endpoints work and how they interact with each other. Using this API you will be able to build advanced applications that will be more intelligently using the BestTime foot traffic API.

We will extend this tutorial soon with other topics like 'Collections' and 'Caching API calls'. We will also show you how to add venues using Google as a geolocator by combining Google with BestTime. Feel free to contact us if you still don't understand something or have any feedback on what we can improve (we are also just humans).