Finding Large Amounts of Sensor Indexes (Bounding Box API Call)

This article is related to the PurpleAir API. If you are unfamiliar with it, check out our API Landing Page to get started.

Requesting data from the API requires using a device’s sensor index. You can find a sensor index for an individual sensor using the map. However, when obtaining data across a large area, you can utilize a bounding box in conjunction with the API to obtain those sensor indexes.

In this post we’ll show how to construct a bounding box and use it in an API call to obtain a list of sensor indexes.

At the current time, there is no way to obtain sensor indexes based on geographical boundaries, such as national or provincial borders. If this is an important feature for you, please make a post in our feature request category.


A Bounding Box Example Using Colorado, USA

Finding the Corners of our Bounding Box

In this example, an API call will be made to obtain all sensor indexes in the state of Colorado. This process requires two sets of coordinates to indicate the boundaries of our box, a northeast point, and a southeast point.

The corners used MUST be the northwest and southeast points of the bounding box you wish to create. Using incorrect corners or mixing the two corners up are some of the most common errors in making bounding box API calls.

These points can be found with Google Maps. When a location on Google Maps is clicked, it will show the latitude and longitude of that location. In the image below, the red arrow is pointing at our desired location for this example. At the bottom of the image within the red circle, the the latitude and longitude for the selected point is shown. It has a latitude of 40.9971734, and a longitude of -109.045037. We’ll use these values for the northwest corner of our bounding box.

Below, we’ve selected the bottom right corner of Colorado. This gives us a latitude of 37.003282, and a longitude of -102.035759. We’ll use these values for the southeast corner of our bounding box call.

Now we have the following points for our call:

  • Northwest
    • latitude: 40.9971734
    • longitude: -109.045037
  • Southeast
    • latitude: 37.003282
    • longitude: -102.035759

Constructing an API Call Using Our Bounding Box

We can now use these to construct a bounding box call. Bounding box calls are currently not supported by the Data Download Tool, so you’ll need to make the API call directly. However, we can use the API documentation to help us by using the boxes labeled “Send a Sample Request.”

You’ll want to go to the API documentation for the Get Sensors Data endpoint. If you scroll down, you will see the following boxes. For this example, we have filled in the information we want for our request. In our case, we’ve selected the following fields to give us more information about the sensors:

  • latitude
  • longitude
  • position_rating
  • location_type
  • last_seen

However, just requesting one field per sensor is enough. If your bounding box is large, then requesting many fields will cause the cost of your bounding box call to rise dramatically.

We have also set the “max_age” parameter to 0. This is important, because the bounding box call by default will only return sensors that have reported in the past seven days. However, by setting 0 we obtain information for any sensor that has ever reported in the state of Colorado. Further details on how to use the max_age parameter are found in the API Documentation.

Sending Our API Call

Once all of the information is filled out, click “Send.” Following this, data will appear below like the following example. If you don’t receive any data back, examine the response for an error message. We also recommend verifying that you have the correct latitude and longitude for your request. The most common mistake made is accidentally mixing up latitude and longitude in the bounding box call.

Once we have successfully completed the call, we can copy the text in the “Response” window, and use it to find the sensor indexes. Looking at the “fields,” we can see that “sensor_index” is the first number listed in the data. So the sensor index for the first sensor in our request is 310, the sensor index for the second sensor is 314, etc. We can now use these sensor indexes to perform API calls.

We also have the fields we requested earlier to assist us. For example, we have the latitude and longitude for each sensor, and we can tell if these sensors are indoor or outdoor based on the location_type.


Learn More

Learn more about the PurpleAir API
Learn more about Making API Calls with the PurpleAir API.
Learn more about our API Use Guidelines.
Learn more about API History Field Descriptions

1 Like

the returned locations of the sensors are way different than the passed boundary locations. I tried nwlng=-121.75,nwlat= 37.42, selng= -114.52, selat=32.55. When I checked the locations of the returned sensors I got selected_sensors[‘latitude’].max(), selected_sensors[‘latitude’].min() = (62.34708, -40.87172). How is this possible? Can you please help?

The most common cause of bounding box errors is mixing up the coordinates for the corners. Are you able to provide the URL you used to send your API request?

Hi @Josh_PurpleAir @epcw thank you both for your reply. Here is my code

# the method
def get_sensorslist(nwlon, nwlat, selon, selat, saveloc=""):
    paheaders = {"X-API-Key": data_config.pa_params['read_key'],
                 "nwlng": str(nwlon),
                 "nwlat": str(nwlat),
                 "selng": str(selon),
                 "selat": str(selat)
                 }

    # Getting data
    try:
        # Request purple air API data
        print(f"Requesting purple air API sensors list location NW: {nwlat}, {nwlon} ... "
              f" to SE: {selat}, {selon}")

        # Perform the purple air API data request
        response = requests.get(data_config.pa_params['sensor_url'],
                                headers=paheaders,
                                params={'fields': 'name,model,location_type,private,latitude,longitude,'
                                                  'altitude,position_rating,last_modified,date_created'}
                                )
    except Exception as e:
        print(f"Unable perform purple-air API request. {e}")
        sys.exit(1)

    # writing to csv file
    if len(saveloc) > 0:
        content = json.loads(response.content)
        data = content["data"]
        columns = content["fields"]

        df = pd.DataFrame(data, columns=columns)
        df.to_csv(saveloc, index=False, header=True)

Here is the method call:
get_sensorslist(-114.52,37.42, -121.75, 32.55, saveloc=“pa_sensors3.csv”)

I appreciate your help. thanks

1 Like

-114.52 is EAST of -121.75 Ergo can’t be your NW long

When you call get_sensorslist(-121.75,37.42, -114.52, 32.55, saveloc=“pa_sensors3.csv”) it works

This tool is something I bookmark to test and make sure my API call is correct so I don’t go bonkers trying to find a typo in my python - Loading...

Thank you so much @epcw I am still confused about the lat lng. I am new in this area.

@epcw okay the problem still exists - when I do df[‘latitude’].max(), df[‘latitude’].min() I am expecting values between 32.55 abd 37.42 but I get (79.99046, -69.540985) also the query returns 25992 sensors that does not look reasonable to me. Thanks.

I also noticed that my query returns index - 53, 77, 81, 182… that looks different than your results. I am wondering how is that possible?

I’m returning 1111 sensors in your bounding box.

@epcw can you please share your url here. Thanks

Correction: 2191 sensors.
max latitude: 37.41438
min latitude: 32.57017

GET https://api.purpleair.com/v1/sensors?fields=latitude%2C%20longitude&nwlng=-121.75&nwlat=37.42&selng=-114.52&selat=32.55 HTTP/1.1

1 Like

Here it is with all of your fields https://api.purpleair.com/v1/sensors?fields=name%2Cmodel%2Clocation_type%2Cprivate%2Clatitude%2Clongitude%2Caltitude%2Cposition_rating%2Clast_modified%2Cdate_created&nwlng=-121.75&nwlat=37.42&selng=-114.52&selat=32.55

1 Like

thats extremely wired. My coordinates are same how the returned results will be different?

I run your url and get 2192 stations which is reasonable.

Is your url being constructed correctly in your code snippet? try printing the url to screen when it’s being run so you can debug

I think the conversion of lng and lat to string was causing the issue. :frowning:

1 Like

Thank you @epcw so much for your help

1 Like

Glad things worked out.

2 Likes