# Python code using API to get historical data

**URL:** <https://community.purpleair.com/t/python-code-using-api-to-get-historical-data/956>\
**Category:** API\
**Created:** [June 14, 2022, 10:44pm UTC](https://community.purpleair.com/t/python-code-using-api-to-get-historical-data/956 "2022-06-14T22:44:58Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![Zuber\_Farooqui](https://sea2.discourse-cdn.com/flex020/user_avatar/community.purpleair.com/zuber_farooqui/32/610_2.png) [@Zuber\_Farooqui](https://community.purpleair.com/u/Zuber_Farooqui)\
**Post date:** [June 14, 2022, 10:44pm UTC](https://community.purpleair.com/t/python-code-using-api-to-get-historical-data/956/1 "2022-06-14T22:44:58Z")

</div>

Here is Python code to download historical PurpleAir data with new AQI. Test it to find bugs.  
Thank you!!

> **[GitHub - zfarooqui/py\_purpleair\_aqi: Python code to get historical data](https://github.com/zfarooqui/py_purpleair_aqi)**
>
> Python code to get historical data. Contribute to zfarooqui/py\_purpleair\_aqi development by creating an account on GitHub.

---

<div class="post-metadata">

**Author:** ![jcothran](https://avatars.discourse-cdn.com/v4/letter/j/e495f1/32.png) [@jcothran](https://community.purpleair.com/u/jcothran)\
**Post date:** [June 21, 2022, 2:53pm UTC](https://community.purpleair.com/t/python-code-using-api-to-get-historical-data/956/2 "2022-06-21T14:53:53Z")

</div>

Thanks, nice work!

Does there exist or is there an example to filter the request download for the following?

- only primary sensor (A not redundant B)
- ‘outside’ sensors (not ‘inside’)

Also, would another approach to purple air data download for larger regional analysis to download larger areas by some time interval(day/month/year/etc) and share the downloaded results on github or shared google drive than repeatedly tax the API for the same historical regional datasets?

What would the average daily amount of data collected by all purple air sensors currently be? I would guess less than a gigabyte/day.

Thanks,  
Jeremy

---

<div class="post-metadata">

**Author:** ![Zuber\_Farooqui](https://sea2.discourse-cdn.com/flex020/user_avatar/community.purpleair.com/zuber_farooqui/32/610_2.png) [@Zuber\_Farooqui](https://community.purpleair.com/u/Zuber_Farooqui)\
**Post date:** [June 22, 2022, 7:48pm UTC](https://community.purpleair.com/t/python-code-using-api-to-get-historical-data/956/3 "2022-06-22T19:48:19Z")

</div>

Hi Jeremy,

I have updated the script to reflect choices of ‘location\_type’ as indoor/outdoor/both. Please refer to the script lines: 27, 51-57, 60, 172-173.

Regarding only primary sensors data (A) then you need to provide only fields from sensor A . Please refer to the document and search ‘fields’ for more options. [Loading...](https://api.purpleair.com/#api-sensors-get-sensor-data)

The size of the data depends on minutes, hourly, daily, period of data and number of monitors.

Let me know if above works for you.  
Thanks!

---

<div class="post-metadata">

**Author:** ![fvelarde](https://sea2.discourse-cdn.com/flex020/user_avatar/community.purpleair.com/fvelarde/32/711_2.png) [@fvelarde](https://community.purpleair.com/u/fvelarde)\
**Post date:** [June 28, 2022, 9:34pm UTC](https://community.purpleair.com/t/python-code-using-api-to-get-historical-data/956/4 "2022-06-28T21:34:09Z")

</div>

Hi Zuber, I have been working with your script but I have trouble on this line  
engine = create\_engine(‘postgresql://postgres:password@location:port/database’)  
could you please tell me what user, password, location, and port this line is referring to?

thanks in advance

---

<div class="post-metadata">

**Author:** ![Zuber\_Farooqui](https://sea2.discourse-cdn.com/flex020/user_avatar/community.purpleair.com/zuber_farooqui/32/610_2.png) [@Zuber\_Farooqui](https://community.purpleair.com/u/Zuber_Farooqui)\
**Post date:** [June 28, 2022, 9:59pm UTC](https://community.purpleair.com/t/python-code-using-api-to-get-historical-data/956/5 "2022-06-28T21:59:23Z")

</div>

Hi Fernando,

The ‘engine’ is created if you are using SQL database to save downloads in a table. In my case, I am using PostgreSQL to save data in tables. More information about SQLAlchemy can be found at [Engine Configuration — SQLAlchemy 1.4 Documentation](https://docs.sqlalchemy.org/en/14/core/engines.html)

If you just need the data in CSV then comment lines #: 19, 22, 74, and 160.

Let me know if this works.

Thanks!

---

<div class="post-metadata">

**Author:** ![fvelarde](https://sea2.discourse-cdn.com/flex020/user_avatar/community.purpleair.com/fvelarde/32/711_2.png) [@fvelarde](https://community.purpleair.com/u/fvelarde)\
**Post date:** [June 29, 2022, 3:11am UTC](https://community.purpleair.com/t/python-code-using-api-to-get-historical-data/956/6 "2022-06-29T03:11:28Z")

</div>

Hi Zuber  
It runs well, Thank you.  
About the field list, I had to change ‘humidity\_a’, ‘humidity\_b’ to ‘humidity’, ‘humidity\_a’ in order to get both measurements (the same for temp ann pressure)

---

<div class="post-metadata">

**Author:** ![Zuber\_Farooqui](https://sea2.discourse-cdn.com/flex020/user_avatar/community.purpleair.com/zuber_farooqui/32/610_2.png) [@Zuber\_Farooqui](https://community.purpleair.com/u/Zuber_Farooqui)\
**Post date:** [June 29, 2022, 4:18pm UTC](https://community.purpleair.com/t/python-code-using-api-to-get-historical-data/956/7 "2022-06-29T16:18:39Z")

</div>

Hi Fernando,

I am glad that it worked for you. Heads up about humidity and other fields.

**Variations:**  
`humidity` returns average for channel A and B.  
`humidity_a` returns channel A data.  
`humidity_b` returns channel B data.

Thanks!

---

<div class="post-metadata">

**Author:** ![ppolonik](https://avatars.discourse-cdn.com/v4/letter/p/a4c791/32.png) [@ppolonik](https://community.purpleair.com/u/ppolonik)\
**Post date:** [August 10, 2022, 4:40am UTC](https://community.purpleair.com/t/python-code-using-api-to-get-historical-data/956/8 "2022-08-10T04:40:01Z")

</div>

Thanks for sharing! This is a great starting point that means I don’t have to start from zero - a huge time saver. For some reason the fields are out of order for me - regardless of the order of fields\_list, the json has ‘location\_type’ before lat/lon. So if I just move ‘location\_type’ to be the third item in fields\_list, it fixes the problem. Not sure why, but it’s an easy fix.

I am also planning to add (1) shapefile compatibility, so I can select sensors in a certain region instead of just a bounding box, and (2) grouped requests, so that I can follow API guidelines and avoid submitting many requests for many sensors.  
These would be nice features if you are hoping to expand your code in the future.

Edit addition: I agree with the first comment that it seems like historical data should be available for download without this complicated API. At least at some moderately aggregated times (60 min, 1440 min). But that’s not an issue with your code, just a general recommendation for PA that would decrease the download burden.

Thanks again!

---

<div class="post-metadata">

**Author:** ![Ethan](https://sea2.discourse-cdn.com/flex020/user_avatar/community.purpleair.com/ethan/32/177_2.png) [@Ethan](https://community.purpleair.com/u/Ethan)\
**Post date:** [August 10, 2022, 2:57pm UTC](https://community.purpleair.com/t/python-code-using-api-to-get-historical-data/956/9 "2022-08-10T14:57:18Z")

</div>

So you are aware, PurpleAir data can be downloaded without using the API through our Sensor Data Download Tool. More information is available here: [Download Sensor Data](https://community.purpleair.com/t/download-sensor-data/100).

---

<div class="post-metadata">

**Author:** ![ppolonik](https://avatars.discourse-cdn.com/v4/letter/p/a4c791/32.png) [@ppolonik](https://community.purpleair.com/u/ppolonik)\
**Post date:** [August 10, 2022, 3:07pm UTC](https://community.purpleair.com/t/python-code-using-api-to-get-historical-data/956/10 "2022-08-10T15:07:26Z")

</div>

Fair point, there is indeed an easier way already! But this method does not work for larger areas. I think once people create code through their preferred languages to easily use the API (as Zuber has kindly started to do), the barrier to use will be a lot lower.

---

<div class="post-metadata">

**Author:** ![Zuber\_Farooqui](https://sea2.discourse-cdn.com/flex020/user_avatar/community.purpleair.com/zuber_farooqui/32/610_2.png) [@Zuber\_Farooqui](https://community.purpleair.com/u/Zuber_Farooqui)\
**Post date:** [August 10, 2022, 4:06pm UTC](https://community.purpleair.com/t/python-code-using-api-to-get-historical-data/956/11 "2022-08-10T16:06:57Z")

</div>

@ppolonik your plans to include shapefile compatibility and group requests are great. Please share once you are done and I will include that in my code.

Thanks!!!

---

<div class="post-metadata">

**Author:** ![ppolonik](https://avatars.discourse-cdn.com/v4/letter/p/a4c791/32.png) [@ppolonik](https://community.purpleair.com/u/ppolonik)\
**Post date:** [August 10, 2022, 9:59pm UTC](https://community.purpleair.com/t/python-code-using-api-to-get-historical-data/956/12 "2022-08-10T21:59:21Z")

</div>

Well it sounds like it is not possible to access historical data right now without special permission. Hopefully this is temporary or permission is granted soon.

But in the meantime, I am trying to create a group. I was able to create one using:  
`requests.post(https://api.purpleair.com/v1/groups/?api_key=***&name=GroupName)`  
(where the key is now the write key)

However, when I go to add members, I get a 400 error.  
`requests.post('https://api.purpleair.com/v1/groups/GroupName/members?api_key=***&sensor_index=131075')`  
I have tried a variety of iterations on this but nothing has worked. If anyone has suggestions, please let me know!

Another unrelated code suggestion - I think as written the end date needs to be at least 14 days after the start date for 60 min averages to work. So after the creation of `date_list`, I added:

```auto
if enddate not in date_list:
    date_list = date_list.append(pd.DatetimeIndex([enddate]))

```
