# PM2.5 Open Data

> Read PM2.5 Open Data, the open feed of the LASS PM2.5 project's low-cost air quality sensors, many of them AirBoxes in Taiwan: a sensor's latest reading or its last week as hourly averages, the sensors nearest a place, every sensor in a project, the Anomaly Detection Framework's flags and rankings, the Dynamic Calibration Framework's models, and how to cite it all. Readings are PM2.5, PM10 and PM1 with temperature and humidity. No account or API key required.

App · Weather & Space · 13 actions. Web page: https://docs.lodol.com/docs/integrations/pm25-open-data

## Actions

### Find sensors near a place

Find the PM2.5 sensors nearest a latitude and longitude, nearest first, each with its distance in kilometres and its latest reading where PM2.5 Open Data includes one. Use it to find a sensor's device id for the other actions. No API key required.

**Inputs**

- Latitude (`latitude`, number, required): The latitude of the place, -90 to 90, e.g. 25.0340 for Taipei 101. From -90 to 90.
- Longitude (`longitude`, number, required): The longitude of the place, -180 to 180, e.g. 121.5645 for Taipei 101. From -180 to 180.
- Maximum sensors (`limit`, number): Optional. How many sensors to return at most, 1 to 100. 5 by default. From 1 to 100. Default: `5`.

**Outputs**

- `count` (number): How many are returned here.
- `latitude` (number): The place's latitude, as given.
- `longitude` (number): The place's longitude, as given.
- `sensors` (list): The sensors, nearest first.
- `total` (number): How many sensors PM2.5 Open Data sent.

**Example output**

```json
{
  "count": 2,
  "latitude": 25.034,
  "longitude": 121.5645,
  "sensors": [
    {
      "dashboard_url": "https://pm25.lass-net.org/data/show.php?device_id=74DA38F7C4B0",
      "date": "2024-07-19",
      "device_id": "74DA38F7C4B0",
      "distance_km": 11.819,
      "fields": {
        "app": "AirBox",
        "date": "2024-07-19",
        "device_id": "74DA38F7C4B0",
        "gps_lat": 24.984,
        "gps_lon": 121.461,
        "s_d0": 8,
        "s_d1": 8,
        "s_d2": 6,
        "s_h0": 93,
        "s_t0": 33.11,
        "time": "10:17:01",
        "timestamp": "2024-07-19T10:17:01Z"
      },
      "humidity_percent": 93,
      "latitude": 24.984,
      "longitude": 121.461,
      "minutes_old": 8,
      "pm1": 6,
      "pm10": 8,
      "pm25": 8,
      "project": "AirBox",
      "site_name": null,
      "temperature_c": 33.11,
      "time": "2024-07-19T10:17:01Z"
    },
    {
      "dashboard_url": "https://pm25.lass-net.org/data/show.php?device_id=08BEAC028A6C",
      "date": "2024-07-19",
      "device_id": "08BEAC028A6C",
      "distance_km": 16.939,
      "fields": {
        "SiteName": "\u967d\u660e-1(2020)",
        "app": "AirBox",
        "date": "2024-07-19",
        "device_id": "08BEAC028A6C",
        "gps_lat": 25.1831,
        "gps_lon": 121.53,
        "s_d0": 5,
        "s_d1": 6,
        "s_d2": 4,
        "s_h0": 88,
        "s_t0": 29.4,
        "time": "10:15:22",
        "timestamp": "2024-07-19T10:15:22Z"
      },
      "humidity_percent": 88,
      "latitude": 25.1831,
      "longitude": 121.53,
      "minutes_old": 10,
      "pm1": 4,
      "pm10": 6,
      "pm25": 5,
      "project": "AirBox",
      "site_name": "\u967d\u660e-1(2020)",
      "temperature_c": 29.4,
      "time": "2024-07-19T10:15:22Z"
    }
  ],
  "total": 3
}
```

### Find the nearest calibration model

Find the calibration station nearest a latitude and longitude, and the model trained there, which a low-cost PM2.5 sensor nearby would be calibrated with, with its distance in kilometres. No API key required.

**Inputs**

- Latitude (`latitude`, number, required): The latitude of the place, -90 to 90, e.g. 25.0340 for Taipei 101. From -90 to 90.
- Longitude (`longitude`, number, required): The longitude of the place, -180 to 180, e.g. 121.5645 for Taipei 101. From -180 to 180.

**Outputs**

- `config_url` (text): A link to the model's configuration file, when the answer gives one.
- `consistency` (number): The framework's consistency score for the model, as it reports it, or null.
- `date` (text): The day the model was trained for, YYYY-MM-DD.
- `distance_km` (number): How far the station is from the place given, in kilometres. Null when it has no position.
- `feature_code` (text): The same inputs as the framework writes them, one letter each: P PM2.5, H hour, T temperature, R relative humidity, e.g. 'PHTR'.
- `features` (list): What the model reads to calibrate a reading: pm25, hour (of the day), temperature and humidity.
- `fields` (object): The model's record as PM2.5 Open Data sent it.
- `latitude` (number): Where the station is.
- `longitude` (number): Where the station is.
- `mae_calibrated` (number): The model's mean absolute error against the station's own monitor, in micrograms per cubic metre (ug/m3). Null when the framework did not record it.
- `mae_raw` (number): The uncalibrated sensor's mean absolute error against the same monitor, in micrograms per cubic metre (ug/m3), for comparison. Null when not given.
- `method` (text): The regression method, as the framework names it, e.g. 'RandomForestRegressor' or 'GAM'.
- `method_name` (text): The method in plain words, e.g. 'Random forest regression'.
- `model_url` (text): A link to the model file (a scikit-learn .joblib), when the answer gives one.
- `sensor` (text): The low-cost sensor the model calibrates: PMS5003 (the AirBox's) or SPS30. Null when the answer does not say.
- `site` (text): The air quality monitoring station the model was trained at, e.g. 'wanhua'. Get a calibration model takes it.
- `training_days` (number): How many days of readings the model learned from: 7, 14, 21 or 30.
- `version` (text): When the model was built, e.g. '2024-07-19 00:01:12'.

**Example output**

```json
{
  "config_url": "https://github.com/IISNRL/DCF-PM2.5/blob/master/2024/20240719/20240719-PMS5003-wanhua.json",
  "consistency": 0.995,
  "date": "2024-07-19",
  "distance_km": 5.873,
  "feature_code": "PT",
  "features": [
    "pm25",
    "temperature"
  ],
  "fields": {
    "Config": "https://github.com/IISNRL/DCF-PM2.5/blob/master/2024/20240719/20240719-PMS5003-wanhua.json",
    "Config_FileName": "20240719-PMS5003-wanhua.json",
    "Data_Length": 14,
    "Date": "2024-07-19",
    "Feature": "PT",
    "Latitude": 25.047,
    "Longitude": 121.508,
    "Method": "RandomForestRegressor",
    "Model": "https://github.com/IISNRL/DCF-PM2.5/blob/master/2024/20240719/20240719-PMS5003-wanhua.joblib",
    "Model_FileName": "20240719-PMS5003-wanhua.joblib",
    "Original_MAE": 2.864,
    "Valid_MAE": 1.647,
    "Version": "2024-07-19 00:01:27",
    "consistency": 0.995,
    "site": "wanhua"
  },
  "latitude": 25.047,
  "longitude": 121.508,
  "mae_calibrated": 1.647,
  "mae_raw": 2.864,
  "method": "RandomForestRegressor",
  "method_name": "Random forest regression",
  "model_url": "https://github.com/IISNRL/DCF-PM2.5/blob/master/2024/20240719/20240719-PMS5003-wanhua.joblib",
  "sensor": "PMS5003",
  "site": "wanhua",
  "training_days": 14,
  "version": "2024-07-19 00:01:27"
}
```

### Get a calibration model

Get one station's calibration model: its method, inputs, training period and error, the latest or a past day's, and save the model file itself (a scikit-learn .joblib file) to attach or pass on. No API key required.

**Inputs**

- Station (`site`, text, required): The air quality monitoring station the model was trained at, as the framework names it, such as wanhua. Pick one of the stations with a latest model, or type one. List calibration models and Find the nearest calibration model report each model's station.
- Date (`date`, date): Optional. The model of a past day instead of the latest: a date such as 2024-07-19.
- Also save the model file (`save_model_file`, true/false): Off by default. Turn it on to save the model itself, a scikit-learn .joblib file, as a document a later step can attach or upload. Default: `false`.

**Outputs**

- `config_url` (text): A link to the model's configuration file, when the answer gives one.
- `consistency` (number): The framework's consistency score for the model, as it reports it, or null.
- `content_type` (text): The saved file's type, or ''.
- `date` (text): The day the model was trained for, YYYY-MM-DD.
- `document_ref` (text): The saved file's document reference, or ''.
- `documents` (list): The saved model file as a document, ready to attach to a later step. Empty when it was not saved.
- `feature_code` (text): The same inputs as the framework writes them, one letter each: P PM2.5, H hour, T temperature, R relative humidity, e.g. 'PHTR'.
- `features` (list): What the model reads to calibrate a reading: pm25, hour (of the day), temperature and humidity.
- `fields` (object): The model's record as PM2.5 Open Data sent it.
- `file_size_bytes` (number): The saved file's size in bytes, or 0.
- `file_url` (text): A URL that serves the saved file, or ''.
- `latitude` (number): Where the station is.
- `longitude` (number): Where the station is.
- `mae_calibrated` (number): The model's mean absolute error against the station's own monitor, in micrograms per cubic metre (ug/m3). Null when the framework did not record it.
- `mae_raw` (number): The uncalibrated sensor's mean absolute error against the same monitor, in micrograms per cubic metre (ug/m3), for comparison. Null when not given.
- `method` (text): The regression method, as the framework names it, e.g. 'RandomForestRegressor' or 'GAM'.
- `method_name` (text): The method in plain words, e.g. 'Random forest regression'.
- `model_url` (text): A link to the model file (a scikit-learn .joblib), when the answer gives one.
- `saved_as_file` (true/false): True when the model file was saved (Also save the model file). The file fields below are empty otherwise.
- `sensor` (text): The low-cost sensor the model calibrates: PMS5003 (the AirBox's) or SPS30. Null when the answer does not say.
- `site` (text): The air quality monitoring station the model was trained at, e.g. 'wanhua'. Get a calibration model takes it.
- `training_days` (number): How many days of readings the model learned from: 7, 14, 21 or 30.
- `version` (text): When the model was built, e.g. '2024-07-19 00:01:12'.

**Example output**

```json
{
  "config_url": "https://github.com/IISNRL/DCF-PM2.5/blob/master/2024/20240719/20240719-PMS5003-taoyuan.json",
  "consistency": 0.152,
  "content_type": "",
  "date": "2024-07-19",
  "document_ref": "",
  "documents": [],
  "feature_code": "PHTR",
  "features": [
    "pm25",
    "hour",
    "temperature",
    "humidity"
  ],
  "fields": {
    "Data_Length": 21,
    "Date": "2024-07-19",
    "Feature": "PHTR",
    "Latitude": 24.995368,
    "Longitude": 121.304383,
    "Method": "RandomForestRegressor",
    "Site": "taoyuan",
    "Valid_MAE": 2.637,
    "Version": "2024-07-19 00:01:12"
  },
  "file_size_bytes": 0,
  "file_url": "",
  "latitude": 24.995368,
  "longitude": 121.304383,
  "mae_calibrated": 2.637,
  "mae_raw": 5.451,
  "method": "RandomForestRegressor",
  "method_name": "Random forest regression",
  "model_url": "https://github.com/IISNRL/DCF-PM2.5/blob/master/2024/20240719/20240719-PMS5003-taoyuan.joblib",
  "saved_as_file": false,
  "sensor": "PMS5003",
  "site": "taoyuan",
  "training_days": 21,
  "version": "2024-07-19 00:01:12"
}
```

### Get a project's latest readings

Get the latest reading of every sensor in a project, such as Taiwan's AirBoxes, with when the service last refreshed it. Keep only the sensors within a distance of a place or that reported recently, sort by distance, PM2.5 or time, and get the lowest, highest and average PM2.5 across them. No API key required.

**Inputs**

- Project (`project`, text, required): The project whose sensors to read. Pick from PM2.5 Open Data's projects or type one: airbox is the Edimax AirBoxes, the largest. List projects shows them all.
- Latitude (`latitude`, number): Optional. The latitude of a place, to measure distances from, -90 to 90, e.g. 25.0340 for Taipei 101. From -90 to 90.
- Longitude (`longitude`, number): Optional. The longitude of a place, to measure distances from, -180 to 180, e.g. 121.5645 for Taipei 101. From -180 to 180.
- Within (km) (`radius_km`, number): Optional. Keep only the sensors within this many kilometres of the Latitude and Longitude, up to 500. All of them by default. From 0.1 to 500.0.
- Reported within (minutes) (`max_age_minutes`, number): Optional. Keep only the sensors whose latest reading is at most this many minutes old, e.g. 60, to leave out sensors that have gone quiet. All of them by default. From 1 to 43200.
- Sort (`sort_by`, text): Optional. Nearest first when a Latitude and Longitude are given, otherwise highest PM2.5 first. Nearest first needs a location. One of: `nearest` (Nearest first), `pm25_desc` (Highest PM2.5 first), `pm25_asc` (Lowest PM2.5 first), `newest` (Most recently reported first).
- Maximum sensors (`limit`, number): Optional. How many sensors to return at most, 1 to 1000. 100 by default. From 1 to 1000. Default: `100`.

**Outputs**

- `count` (number): How many are returned here.
- `matched` (number): How many of them are within the distance and reported recently enough.
- `project` (text): The project's id, as it was found.
- `readings` (list): The matching sensors' latest readings, sorted.
- `summary` (object): Across every reading counted: how many there are, the first and last time, and each measurement's lowest, highest and average. Null where no reading has the measurement.
- `total` (number): How many sensors the project has readings for.
- `updated_at` (text): When PM2.5 Open Data last refreshed the project's readings, ISO 8601 UTC, or null.

**Example output**

```json
{
  "count": 2,
  "matched": 3,
  "project": "airbox",
  "readings": [
    {
      "dashboard_url": "https://pm25.lass-net.org/data/show.php?device_id=74DA38F7C4B0",
      "date": "2024-07-19",
      "device_id": "74DA38F7C4B0",
      "distance_km": 11.819,
      "fields": {
        "app": "AirBox",
        "date": "2024-07-19",
        "device_id": "74DA38F7C4B0",
        "gps_lat": 24.984,
        "gps_lon": 121.461,
        "s_d0": 8,
        "s_d1": 8,
        "s_d2": 6,
        "s_h0": 93,
        "s_t0": 33.11,
        "time": "10:17:01",
        "timestamp": "2024-07-19T10:17:01Z"
      },
      "humidity_percent": 93,
      "latitude": 24.984,
      "longitude": 121.461,
      "minutes_old": 8,
      "pm1": 6,
      "pm10": 8,
      "pm25": 8,
      "project": "AirBox",
      "site_name": null,
      "temperature_c": 33.11,
      "time": "2024-07-19T10:17:01Z"
    },
    {
      "dashboard_url": "https://pm25.lass-net.org/data/show.php?device_id=08BEAC028A6C",
      "date": "2024-07-19",
      "device_id": "08BEAC028A6C",
      "distance_km": 16.939,
      "fields": {
        "SiteName": "\u967d\u660e-1(2020)",
        "app": "AirBox",
        "date": "2024-07-19",
        "device_id": "08BEAC028A6C",
        "gps_lat": 25.1831,
        "gps_lon": 121.53,
        "s_d0": 5,
        "s_d1": 6,
        "s_d2": 4,
        "s_h0": 88,
        "s_t0": 29.4,
        "time": "10:15:22",
        "timestamp": "2024-07-19T10:15:22Z"
      },
      "humidity_percent": 88,
      "latitude": 25.1831,
      "longitude": 121.53,
      "minutes_old": 10,
      "pm1": 4,
      "pm10": 6,
      "pm25": 5,
      "project": "AirBox",
      "site_name": "\u967d\u660e-1(2020)",
      "temperature_c": 29.4,
      "time": "2024-07-19T10:15:22Z"
    }
  ],
  "summary": {
    "first_time": "2024-07-19T10:12:40Z",
    "humidity_percent": {
      "max": 93,
      "mean": 87.33,
      "min": 81
    },
    "last_time": "2024-07-19T10:17:01Z",
    "pm1": {
      "max": 17,
      "mean": 9.0,
      "min": 4
    },
    "pm10": {
      "max": 23,
      "mean": 12.33,
      "min": 6
    },
    "pm25": {
      "max": 21,
      "mean": 11.33,
      "min": 5
    },
    "readings": 3,
    "temperature_c": {
      "max": 33.11,
      "mean": 31.24,
      "min": 29.4
    }
  },
  "total": 4,
  "updated_at": "2024-07-19T10:20:03Z"
}
```

### Get a sensor's latest reading

Get the newest reading of one PM2.5 sensor by its device id: PM2.5, PM10 and PM1 in micrograms per cubic metre, temperature and humidity, where and when it was taken and how many minutes ago, with a link to the sensor's page. Fails, naming the likely cause, when the sensor has not reported lately. No API key required.

**Inputs**

- Sensor (`device_id`, text, required): The sensor to read. Its device id: an AirBox's is its MAC address, such as 74DA38F7C4B0. A link to the sensor's page on pm25.lass-net.org works too. Find sensors near a place and Get a project's latest readings report each sensor's id.

**Outputs**

- `dashboard_url` (text): The sensor's page on pm25.lass-net.org, which charts its readings: a link to put in a message.
- `date` (text): The UTC day the reading was taken, YYYY-MM-DD. Get a sensor's readings takes it as its Date.
- `device_id` (text): The sensor's device id, e.g. '74DA38F7C4B0'. An AirBox's is its MAC address. Get a sensor's latest reading and Get a sensor's readings take it.
- `fields` (object): The record as PM2.5 Open Data sent it, field for field, including any measurement not named above under its LASS code (s_d0 is PM2.5, s_t0 temperature, s_h0 humidity).
- `humidity_percent` (number): The relative humidity at the sensor, in percent. Low-cost PM2.5 sensors read high in very humid air.
- `latitude` (number): Where the sensor is, as it reports it. Null when it has no position.
- `longitude` (number): Where the sensor is, as it reports it. Null when it has no position.
- `minutes_old` (number): How many minutes ago the reading was taken. An AirBox reports every five or six minutes, so a much larger number means it has gone quiet.
- `pm1` (number): PM1 in micrograms per cubic metre (ug/m3). Null for sensors whose firmware reports PM2.5 only.
- `pm10` (number): PM10 in micrograms per cubic metre (ug/m3). Null for sensors whose firmware reports PM2.5 only.
- `pm25` (number): PM2.5, fine particles, in micrograms per cubic metre (ug/m3).
- `project` (text): The project the sensor reports under, e.g. 'AirBox', 'LASS' or 'MAPS'. Null when the answer does not say.
- `site_name` (text): The name its host gave the sensor's site, often a school or a district and often in Chinese, e.g. '陽明-1(2020)'. Null when it has none.
- `temperature_c` (number): The temperature at the sensor, in degrees Celsius.
- `time` (text): When the reading was taken, ISO 8601 in UTC, e.g. '2024-07-19T10:17:01Z'.

**Example output**

```json
{
  "dashboard_url": "https://pm25.lass-net.org/data/show.php?device_id=74DA38F7C4B0",
  "date": "2024-07-19",
  "device_id": "74DA38F7C4B0",
  "fields": {
    "app": "AirBox",
    "date": "2024-07-19",
    "device_id": "74DA38F7C4B0",
    "gps_lat": 24.984,
    "gps_lon": 121.461,
    "s_d0": 8,
    "s_d1": 8,
    "s_d2": 6,
    "s_h0": 93,
    "s_t0": 33.11,
    "time": "10:17:01",
    "timestamp": "2024-07-19T10:17:01Z"
  },
  "humidity_percent": 93,
  "latitude": 24.984,
  "longitude": 121.461,
  "minutes_old": 8,
  "pm1": 6,
  "pm10": 8,
  "pm25": 8,
  "project": "AirBox",
  "site_name": null,
  "temperature_c": 33.11,
  "time": "2024-07-19T10:17:01Z"
}
```

### Get a sensor's readings

Get one PM2.5 sensor's readings over its last seven days, or over one day, as hourly averages, daily averages or every reading (an AirBox sends one every five or six minutes), with the lowest, highest and average PM2.5, PM10, PM1, temperature and humidity over the period. Days and hours can be counted in any time zone. No API key required.

**Inputs**

- Sensor (`device_id`, text, required): The sensor to read. Its device id: an AirBox's is its MAC address, such as 74DA38F7C4B0. A link to the sensor's page on pm25.lass-net.org works too. Find sensors near a place and Get a project's latest readings report each sensor's id.
- Date (`date`, date): Optional. One day's readings instead of the last seven days': a date such as 2024-07-19 (a workflow value can also say today or yesterday). The day runs midnight to midnight in the Time zone. PM2.5 Open Data keeps about the last 30 days online, so older days can come back empty.
- Readings as (`interval`, text): Optional. Hourly averages (the default), daily averages, or every reading. An AirBox reports every five or six minutes, about 2,000 readings a week, so averages keep the result short. One of: `hour` (Hourly averages), `day` (Daily averages), `all` (Every reading). Default: `hour`.
- Time zone (`time_zone`, text): Optional. The time zone the Date and the hours and days of the averages are counted in, as a name such as Asia/Taipei. UTC by default, PM2.5 Open Data's own.

**Outputs**

- `count` (number): How many rows Readings holds.
- `date` (text): The day asked for, YYYY-MM-DD, or null for the last seven days.
- `device_id` (text): The sensor's device id, e.g. '74DA38F7C4B0'. An AirBox's is its MAC address. Get a sensor's latest reading and Get a sensor's readings take it.
- `interval` (text): How the readings are given: hour (hourly averages), day (daily averages) or all (every reading).
- `latitude` (number): Where the sensor was at its latest reading here.
- `longitude` (number): Where the sensor was at its latest reading here.
- `project` (text): The project the sensor reports under, e.g. 'AirBox', 'LASS' or 'MAPS'. Null when the answer does not say.
- `readings` (list): The readings or averages, oldest first. Empty when the sensor reported nothing in the period.
- `site_name` (text): The name its host gave the sensor's site, often a school or a district and often in Chinese, e.g. '陽明-1(2020)'. Null when it has none.
- `summary` (object): Across every reading counted: how many there are, the first and last time, and each measurement's lowest, highest and average. Null where no reading has the measurement.
- `time_zone` (text): The time zone the day and the averages are counted in.

**Example output**

```json
{
  "count": 3,
  "date": null,
  "device_id": "74DA38F7C4B0",
  "interval": "hour",
  "latitude": 24.984,
  "longitude": 121.461,
  "project": "AirBox",
  "readings": [
    {
      "humidity_percent": 89.83,
      "pm1": 7.67,
      "pm10": 9.83,
      "pm25": 9.83,
      "pm25_max": 11,
      "pm25_min": 9,
      "readings": 6,
      "temperature_c": 34.12,
      "time": "2024-07-19T07:00:00Z"
    },
    {
      "humidity_percent": 91.0,
      "pm1": 4.25,
      "pm10": 6.25,
      "pm25": 6.0,
      "pm25_max": 7,
      "pm25_min": 5,
      "readings": 4,
      "temperature_c": 33.46,
      "time": "2024-07-19T09:00:00Z"
    },
    {
      "humidity_percent": 93.0,
      "pm1": 6.0,
      "pm10": 8.0,
      "pm25": 8.0,
      "pm25_max": 8,
      "pm25_min": 8,
      "readings": 2,
      "temperature_c": 33.18,
      "time": "2024-07-19T10:00:00Z"
    }
  ],
  "site_name": null,
  "summary": {
    "first_time": "2024-07-19T07:02:14Z",
    "humidity_percent": {
      "max": 93,
      "mean": 90.75,
      "min": 89
    },
    "last_time": "2024-07-19T10:17:01Z",
    "pm1": {
      "max": 9,
      "mean": 6.25,
      "min": 4
    },
    "pm10": {
      "max": 11,
      "mean": 8.33,
      "min": 6
    },
    "pm25": {
      "max": 11,
      "mean": 8.25,
      "min": 5
    },
    "readings": 12,
    "temperature_c": {
      "max": 34.25,
      "mean": 33.74,
      "min": 33.11
    }
  },
  "time_zone": "UTC"
}
```

### Get emission detections

Get the sensors that PM2.5 Open Data's Anomaly Detection Framework (ADF) flagged in its latest emission detection, which looks for potential sources of PM2.5 emission, each with the rate ADF gives it, and when that analysis ran. No API key required.

**Inputs**

- Maximum sensors (`limit`, number): Optional. How many sensors to return at most, 1 to 1000. 100 by default. From 1 to 1000. Default: `100`.

**Outputs**

- `count` (number): How many are returned here, at most Maximum sensors.
- `sensors` (list): The flagged sensors, in the service's order.
- `total` (number): How many sensors the answer holds.
- `updated_at` (text): When PM2.5 Open Data made the answer, as it says it (ISO 8601 UTC), or null when it does not.

**Example output**

```json
{
  "count": 2,
  "sensors": [
    {
      "dashboard_url": "https://pm25.lass-net.org/data/show.php?device_id=74DA38F7C254",
      "date": null,
      "device_id": "74DA38F7C254",
      "fields": {
        "device_id": "74DA38F7C254",
        "rate": 0.3516182408360383
      },
      "humidity_percent": null,
      "latitude": null,
      "longitude": null,
      "minutes_old": null,
      "pm1": null,
      "pm10": null,
      "pm25": null,
      "project": null,
      "rate": 0.3516182408360383,
      "site_name": null,
      "temperature_c": null,
      "time": null
    },
    {
      "dashboard_url": "https://pm25.lass-net.org/data/show.php?device_id=08BEAC028A6C",
      "date": null,
      "device_id": "08BEAC028A6C",
      "fields": {
        "device_id": "08BEAC028A6C",
        "rate": 0.4459893048128342
      },
      "humidity_percent": null,
      "latitude": null,
      "longitude": null,
      "minutes_old": null,
      "pm1": null,
      "pm10": null,
      "pm25": null,
      "project": null,
      "rate": 0.4459893048128342,
      "site_name": null,
      "temperature_c": null,
      "time": null
    }
  ],
  "total": 2,
  "updated_at": "2024-07-19T10:05:11Z"
}
```

### Get indoor detections

Get the sensors that PM2.5 Open Data's Anomaly Detection Framework (ADF) flagged as probably installed indoors in its latest analysis, each with the rate ADF gives it, to leave them out of outdoor air quality checks. No API key required.

**Inputs**

- Maximum sensors (`limit`, number): Optional. How many sensors to return at most, 1 to 1000. 100 by default. From 1 to 1000. Default: `100`.

**Outputs**

- `count` (number): How many are returned here, at most Maximum sensors.
- `sensors` (list): The flagged sensors, in the service's order.
- `total` (number): How many sensors the answer holds.
- `updated_at` (text): When PM2.5 Open Data made the answer, as it says it (ISO 8601 UTC), or null when it does not.

**Example output**

```json
{
  "count": 1,
  "sensors": [
    {
      "dashboard_url": "https://pm25.lass-net.org/data/show.php?device_id=08BEAC028A4E",
      "date": null,
      "device_id": "08BEAC028A4E",
      "fields": {
        "device_id": "08BEAC028A4E",
        "rate": 0.2772664519098021
      },
      "humidity_percent": null,
      "latitude": null,
      "longitude": null,
      "minutes_old": null,
      "pm1": null,
      "pm10": null,
      "pm25": null,
      "project": null,
      "rate": 0.2772664519098021,
      "site_name": null,
      "temperature_c": null,
      "time": null
    }
  ],
  "total": 1,
  "updated_at": "2024-07-19T10:05:11Z"
}
```

### Get pollution detections

Get the sensors that PM2.5 Open Data's Anomaly Detection Framework (ADF) flagged in its latest pollution detection, which looks for potential regional emission sources, each with the kind of pollution and when it was found. No API key required.

**Inputs**

- Maximum sensors (`limit`, number): Optional. How many sensors to return at most, 1 to 1000. 100 by default. From 1 to 1000. Default: `100`.

**Outputs**

- `count` (number): How many are returned here, at most Maximum sensors.
- `sensors` (list): The flagged sensors, in the service's order.
- `total` (number): How many sensors the answer holds.
- `updated_at` (text): When PM2.5 Open Data made the answer, as it says it (ISO 8601 UTC), or null when it does not.

**Example output**

```json
{
  "count": 1,
  "sensors": [
    {
      "dashboard_url": "https://pm25.lass-net.org/data/show.php?device_id=74DA38F7C254",
      "date": "2024-07-19",
      "device_id": "74DA38F7C254",
      "fields": {
        "device_id": "74DA38F7C254",
        "pollution_type": "Local Pollution",
        "timestamp": "2024-07-19T10:08:00Z"
      },
      "humidity_percent": null,
      "latitude": null,
      "longitude": null,
      "minutes_old": 17,
      "pm1": null,
      "pm10": null,
      "pm25": null,
      "pollution_type": "Local Pollution",
      "project": null,
      "site_name": null,
      "temperature_c": null,
      "time": "2024-07-19T10:08:00Z"
    }
  ],
  "total": 1,
  "updated_at": "2024-07-19T10:05:11Z"
}
```

### Get sensor rankings

Get the Anomaly Detection Framework's ranking of PM2.5 sensors by the quality of their readings: every ranked sensor, one sensor's rank, or the sensors of one project, with each sensor's project and when it was ranked. No API key required.

**Inputs**

- Sensor (`device_id`, text): Optional. One sensor to get the rank of, instead of every sensor. Its device id: an AirBox's is its MAC address, such as 74DA38F7C4B0. A link to the sensor's page on pm25.lass-net.org works too. Find sensors near a place and Get a project's latest readings report each sensor's id.
- Project (`project`, text): Optional. Only the sensors of this project. Cannot be combined with a Sensor. Pick from PM2.5 Open Data's projects or type one: airbox is the Edimax AirBoxes, the largest. List projects shows them all.
- Maximum sensors (`limit`, number): Optional. How many sensors to return at most, 1 to 1000. 100 by default. From 1 to 1000. Default: `100`.

**Outputs**

- `count` (number): How many are returned here, at most Maximum sensors.
- `device_id` (text): The sensor asked about, or null.
- `project` (text): The project asked about, or null.
- `sensors` (list): The ranked sensors, in the service's order.
- `total` (number): How many sensors the answer holds.
- `updated_at` (text): When PM2.5 Open Data made the answer, as it says it (ISO 8601 UTC), or null when it does not.

**Example output**

```json
{
  "count": 2,
  "device_id": null,
  "project": null,
  "sensors": [
    {
      "dashboard_url": "https://pm25.lass-net.org/data/show.php?device_id=74DA38F7C4B0",
      "date": "2024-07-19",
      "device_id": "74DA38F7C4B0",
      "fields": {
        "device_id": "74DA38F7C4B0",
        "ranking": 1.0,
        "source": "airbox",
        "timestamp": "2024-07-19T10:11:00Z"
      },
      "humidity_percent": null,
      "latitude": null,
      "longitude": null,
      "minutes_old": 14,
      "pm1": null,
      "pm10": null,
      "pm25": null,
      "project": "airbox",
      "rank": 1.0,
      "site_name": null,
      "temperature_c": null,
      "time": "2024-07-19T10:11:00Z"
    },
    {
      "dashboard_url": "https://pm25.lass-net.org/data/show.php?device_id=08BEAC028A6C",
      "date": "2024-07-19",
      "device_id": "08BEAC028A6C",
      "fields": {
        "device_id": "08BEAC028A6C",
        "ranking": 1.0,
        "source": "airbox",
        "timestamp": "2024-07-19T10:11:00Z"
      },
      "humidity_percent": null,
      "latitude": null,
      "longitude": null,
      "minutes_old": 14,
      "pm1": null,
      "pm10": null,
      "pm25": null,
      "project": "airbox",
      "rank": 1.0,
      "site_name": null,
      "temperature_c": null,
      "time": "2024-07-19T10:11:00Z"
    }
  ],
  "total": 3,
  "updated_at": "2024-07-19T10:05:11Z"
}
```

### Get the citation

Get the reference PM2.5 Open Data asks you to cite when you use its data, or the one for its Anomaly Detection Framework (ADF), with a DOI link. No API key required.

**Inputs**

- Cite (`topic`, text): Optional. The reference for the PM2.5 open data itself (the default), or for the Anomaly Detection Framework (ADF). One of: `data` (The PM2.5 open data), `adf` (ADF, the Anomaly Detection Framework). Default: `data`.

**Outputs**

- `citation` (text): The reference as PM2.5 Open Data gives it, as plain text.
- `doi` (text): The DOI in the reference, e.g. '10.1109/ACCESS.2017.2723919', or null.
- `doi_url` (text): The DOI as a link, or null.
- `topic` (text): What the citation is for: data or adf.

**Example output**

```json
{
  "citation": "@ARTICLE{7970115,\nauthor={L. {Chen} and Y. {Ho} and H. {Lee} and H. {Wu} and H. {Liu} and H. {Hsieh} and Y. {Huang} and S. C. {Lung}},\njournal={IEEE Access},\ntitle={An Open Framework for Participatory PM2.5 Monitoring in Smart Cities},\nyear={2017},\nvolume={5},\nnumber={},\npages={14441-14454},\nabstract={As the population in cities continues to increase rapidly, air pollution becomes a serious issue from public health to social economy. Among all pollutants, fine particulate matters (PM2.5) directly related to various serious health concerns, e.g., lung cancer, premature death, asthma, and cardiovascular and respiratory diseases. To enhance the quality of urban living, sensors are deployed to create smart cities. In this paper, we present a participatory urban sensing framework for PM2.5 monitoring with more than 2500 devices deployed in Taiwan and 29 other countries. It is one of the largest deployment project for PM2.5 monitor in the world as we know until May 2017. The key feature of the framework is its open system architecture, which is based on the principles of open hardware, open source software, and open data. To facilitate the deployment of the framework, we investigate the accuracy issue of low-cost particle sensors with a comprehensive set of comparison evaluations to identify the most reliable sensor. By working closely with government authorities, industry partners, and maker communities, we can construct an effective eco-system for participatory urban sensing of PM2.5 particles. Based on our deployment achievements to date, we provide a number of data services to improve environmental awareness, trigger on-demand responses, and assist future government policymaking. The proposed framework is highly scalable and sustainable with the potential to facilitate the Internet of Things, smart cities, and citizen science in the future.},\nkeywords={air pollution;environmental monitoring (geophysics);government data processing;public administration;public domain software;smart cities;open framework;government policymaking;environmental awareness;open source software;open hardware;open system architecture;Taiwan;participatory urban sensing;health concerns;PM2.5 monitoring;fine particulate matters monitoring;smart cities;Sensors;Monitoring;Lungs;Air pollution;Hardware;Atmospheric modeling;Air pollution;crowdsourcing;environmental monitoring;Internet of Things},\ndoi={10.1109/ACCESS.2017.2723919},\nISSN={2169-3536},\nmonth={},}",
  "doi": "10.1109/ACCESS.2017.2723919",
  "doi_url": "https://doi.org/10.1109/ACCESS.2017.2723919",
  "topic": "data"
}
```

### List calibration models

List the calibration models PM2.5 Open Data's Dynamic Calibration Framework (DCF) trained at air quality monitoring stations in Taiwan to correct low-cost sensors' PM2.5: each station's method, its inputs, how many days it learned from, the day it was trained, and its error before and after calibration. The latest models, one sensor type's, or a past day's. A station listed without a model comes back with its position only; Get a calibration model reads the last model trained there. No API key required.

**Inputs**

- Sensor type (`sensor`, text): Optional. Only the latest models for one type of low-cost sensor: the Plantower PMS5003 in the AirBox, or the Sensirion SPS30. Every type by default. Cannot be combined with a Date. One of: `PMS5003` (Plantower PMS5003, the AirBox's sensor), `SPS30` (Sensirion SPS30).
- Date (`date`, date): Optional. A past day's models instead of the latest: a date such as 2024-07-19.

**Outputs**

- `count` (number): How many models there are.
- `date` (text): The day asked for, or null for the latest.
- `models` (list): The models, in the service's order.
- `sensor` (text): The sensor type asked for, or null.

**Example output**

```json
{
  "count": 3,
  "date": null,
  "models": [
    {
      "config_url": "https://github.com/IISNRL/DCF-PM2.5/blob/master/2024/20240719/20240719-PMS5003-taoyuan.json",
      "consistency": 0.152,
      "date": "2024-07-19",
      "feature_code": "PHTR",
      "features": [
        "pm25",
        "hour",
        "temperature",
        "humidity"
      ],
      "fields": {
        "Config": "https://github.com/IISNRL/DCF-PM2.5/blob/master/2024/20240719/20240719-PMS5003-taoyuan.json",
        "Config_FileName": "20240719-PMS5003-taoyuan.json",
        "Data_Length": 21,
        "Date": "2024-07-19",
        "Feature": "PHTR",
        "Latitude": 24.995,
        "Longitude": 121.304,
        "Method": "RandomForestRegressor",
        "Model": "https://github.com/IISNRL/DCF-PM2.5/blob/master/2024/20240719/20240719-PMS5003-taoyuan.joblib",
        "Model_FileName": "20240719-PMS5003-taoyuan.joblib",
        "Original_MAE": 5.451,
        "Valid_MAE": 2.637,
        "Version": "2024-07-19 00:01:12",
        "consistency": 0.152,
        "site": "taoyuan"
      },
      "latitude": 24.995,
      "longitude": 121.304,
      "mae_calibrated": 2.637,
      "mae_raw": 5.451,
      "method": "RandomForestRegressor",
      "method_name": "Random forest regression",
      "model_url": "https://github.com/IISNRL/DCF-PM2.5/blob/master/2024/20240719/20240719-PMS5003-taoyuan.joblib",
      "sensor": "PMS5003",
      "site": "taoyuan",
      "training_days": 21,
      "version": "2024-07-19 00:01:12"
    },
    {
      "config_url": "https://github.com/IISNRL/DCF-PM2.5/blob/master/2024/20240719/20240719-PMS5003-zhongming.json",
      "consistency": 0.997,
      "date": "2024-07-19",
      "feature_code": "PHR",
      "features": [
        "pm25",
        "hour",
        "humidity"
      ],
      "fields": {
        "Config": "https://github.com/IISNRL/DCF-PM2.5/blob/master/2024/20240719/20240719-PMS5003-zhongming.json",
        "Config_FileName": "20240719-PMS5003-zhongming.json",
        "Data_Length": 14,
        "Date": "2024-07-19",
        "Feature": "PHR",
        "Latitude": 24.152,
        "Longitude": 120.641,
        "Method": "GAM",
        "Model": "https://github.com/IISNRL/DCF-PM2.5/blob/master/2024/20240719/20240719-PMS5003-zhongming.joblib",
        "Model_FileName": "20240719-PMS5003-zhongming.joblib",
        "Original_MAE": 2.321,
        "Valid_MAE": 1.62,
        "Version": "2024-07-19 00:01:23",
        "consistency": 0.997,
        "site": "zhongming"
      },
      "latitude": 24.152,
      "longitude": 120.641,
      "mae_calibrated": 1.62,
      "mae_raw": 2.321,
      "method": "GAM",
      "method_name": "Generalized additive model",
      "model_url": "https://github.com/IISNRL/DCF-PM2.5/blob/master/2024/20240719/20240719-PMS5003-zhongming.joblib",
      "sensor": "PMS5003",
      "site": "zhongming",
      "training_days": 14,
      "version": "2024-07-19 00:01:23"
    },
    {
      "config_url": "https://github.com/IISNRL/DCF-PM2.5/blob/master/2024/20240719/20240719-PMS5003-wanhua.json",
      "consistency": 0.995,
      "date": "2024-07-19",
      "feature_code": "PT",
      "features": [
        "pm25",
        "temperature"
      ],
      "fields": {
        "Config": "https://github.com/IISNRL/DCF-PM2.5/blob/master/2024/20240719/20240719-PMS5003-wanhua.json",
        "Config_FileName": "20240719-PMS5003-wanhua.json",
        "Data_Length": 14,
        "Date": "2024-07-19",
        "Feature": "PT",
        "Latitude": 25.047,
        "Longitude": 121.508,
        "Method": "RandomForestRegressor",
        "Model": "https://github.com/IISNRL/DCF-PM2.5/blob/master/2024/20240719/20240719-PMS5003-wanhua.joblib",
        "Model_FileName": "20240719-PMS5003-wanhua.joblib",
        "Original_MAE": 2.864,
        "Valid_MAE": 1.647,
        "Version": "2024-07-19 00:01:27",
        "consistency": 0.995,
        "site": "wanhua"
      },
      "latitude": 25.047,
      "longitude": 121.508,
      "mae_calibrated": 1.647,
      "mae_raw": 2.864,
      "method": "RandomForestRegressor",
      "method_name": "Random forest regression",
      "model_url": "https://github.com/IISNRL/DCF-PM2.5/blob/master/2024/20240719/20240719-PMS5003-wanhua.joblib",
      "sensor": "PMS5003",
      "site": "wanhua",
      "training_days": 14,
      "version": "2024-07-19 00:01:27"
    }
  ],
  "sensor": null
}
```

### List projects

List the projects PM2.5 Open Data files its sensors under, such as airbox for the Edimax AirBoxes and lass for the LASS community's own devices. Get a project's latest readings takes one. No API key required.

**Inputs**: None.

**Outputs**

- `count` (number): How many projects there are.
- `projects` (list): The projects, in the service's order.

**Example output**

```json
{
  "count": 13,
  "projects": [
    {
      "fields": {},
      "id": "airbox",
      "name": "airbox"
    },
    {
      "fields": {},
      "id": "maps",
      "name": "maps"
    },
    {
      "fields": {},
      "id": "fw_sensor",
      "name": "fw_sensor"
    },
    {
      "fields": {},
      "id": "rescue_tw",
      "name": "rescue_tw"
    },
    {
      "fields": {},
      "id": "harvard_iaq",
      "name": "harvard_iaq"
    },
    {
      "fields": {},
      "id": "itri_pm25",
      "name": "itri_pm25"
    },
    {
      "fields": {},
      "id": "lass",
      "name": "lass"
    },
    {
      "fields": {},
      "id": "lass4u",
      "name": "lass4u"
    },
    {
      "fields": {},
      "id": "iaq_tw",
      "name": "iaq_tw"
    },
    {
      "fields": {},
      "id": "pim25",
      "name": "pim25"
    },
    {
      "fields": {},
      "id": "ntu4aq",
      "name": "ntu4aq"
    },
    {
      "fields": {},
      "id": "epaiot",
      "name": "epaiot"
    },
    {
      "fields": {},
      "id": "aot",
      "name": "aot"
    }
  ]
}
```
