Skip to main content

Raw Sensor Data

Retrieve uncalibrated, minute-level sensor readings directly from AirQo devices. This endpoint is suited for advanced users who need high-resolution data or want to apply their own calibration.

Tier requirement

Requires a Standard Tier subscription or above.


Grid and Cohort ID filtering — coming soon

This endpoint does not yet accept grid_id or cohort_id as parameters. Filter by device_names (array of device names) instead. Use the Metadata API to discover device identifiers for your Grid or Cohort.

Endpoint

POST https://api.airqo.net/api/v3/public/analytics/raw-data?token=YOUR_SECRET_TOKEN
Content-Type: application/json

Request parameters

ParameterTypeRequiredDescription
networkstringYesAlways "airqo"
startDateTimestringYesStart timestamp (ISO 8601)
endDateTimestringYesEnd timestamp (ISO 8601)
pollutantsarrayYesPollutants to retrieve (e.g. ["pm2_5", "pm10", "no2"])
device_categorystringNoFilter by device type, e.g. "lowcost" or "mobile"
device_namesarrayNoFilter to specific device identifiers
metaDataFieldsarrayNoAdditional metadata (e.g. ["latitude", "longitude"])
weatherFieldsarrayNoWeather data (e.g. ["temperature", "humidity"])
frequencystringNo"raw" (default) or "aggregated"
cursorstringNoPagination cursor from previous response

Available pollutants

Pollutant fieldDescription
pm2_5Fine particulate matter (calibrated channel 1)
pm10Coarse particulate matter
no2Nitrogen dioxide
s1_pm2_5Raw sensor channel 1 PM2.5
s2_pm2_5Raw sensor channel 2 PM2.5
s1_pm10Raw sensor channel 1 PM10
s2_pm10Raw sensor channel 2 PM10

Example request

curl -X POST "https://api.airqo.net/api/v3/public/analytics/raw-data?token=YOUR_SECRET_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"network": "airqo",
"startDateTime": "2025-01-01T00:00:00Z",
"endDateTime": "2025-01-01T06:00:00Z",
"device_names": ["airqo_bx2847"],
"pollutants": ["pm2_5", "pm10", "no2"],
"metaDataFields": ["latitude", "longitude"],
"weatherFields": ["temperature", "humidity"],
"frequency": "raw"
}'

Python:

import requests

token = 'YOUR_SECRET_TOKEN'

payload = {
"network": "airqo",
"startDateTime": "2025-01-01T00:00:00Z",
"endDateTime": "2025-01-01T06:00:00Z",
"device_names": ["airqo_bx2847"],
"pollutants": ["pm2_5", "pm10"],
"metaDataFields": ["latitude", "longitude"],
"weatherFields": ["temperature", "humidity"],
"frequency": "raw"
}

response = requests.post(
f"https://api.airqo.net/api/v3/public/analytics/raw-data?token={token}",
json=payload
)
data = response.json()

print(f"Retrieved {len(data['data'])} raw readings")

JavaScript:

const response = await fetch(
`https://api.airqo.net/api/v3/public/analytics/raw-data?token=${token}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
network: 'airqo',
startDateTime: '2025-01-01T00:00:00Z',
endDateTime: '2025-01-01T06:00:00Z',
device_names: ['airqo_bx2847'],
pollutants: ['pm2_5', 'pm10'],
metaDataFields: ['latitude', 'longitude'],
frequency: 'raw'
})
}
);
const data = await response.json();

Example response

{
"status": "success",
"message": "Data downloaded successfully",
"data": [
{
"device_name": "airqo_bx2847",
"datetime": "2025-01-01 00:05:00Z",
"s1_pm2_5": 14.2,
"s2_pm2_5": 14.8,
"s1_pm10": 18.4,
"s2_pm10": 18.9,
"latitude": 0.3476,
"longitude": 32.5825,
"temperature": 24.1,
"humidity": 68.5,
"network": "airqo",
"frequency": "raw"
}
],
"metadata": {
"total_count": 2000,
"has_more": true,
"next": "eyJsYXN0SWQiOiI2NWM4Z..."
}
}

Response fields

FieldDescription
device_nameDevice identifier
datetimeTimestamp of reading
s1_pm2_5 / s2_pm2_5Raw dual-channel PM2.5 readings in μg/m³
s1_pm10 / s2_pm10Raw dual-channel PM10 readings in μg/m³
latitude / longitudeDevice location
temperatureAmbient temperature (°C)
humidityRelative humidity (%)
frequencyAlways "raw" for this endpoint
metadata.has_moreWhether more pages exist
metadata.nextCursor to pass in the next request

Pagination

Raw data queries can return millions of rows. Always paginate:

all_records = []
cursor = None

while True:
payload = {**your_base_payload}
if cursor:
payload['cursor'] = cursor

response = requests.post(
f"https://api.airqo.net/api/v3/public/analytics/raw-data?token={token}",
json=payload
).json()

all_records.extend(response['data'])

if not response['metadata']['has_more']:
break

cursor = response['metadata']['next']

Tips

  • Limit date ranges — raw data is high-volume. Use windows of 1–6 hours for exploratory queries.
  • Specify only the pollutants you need — omitting unused fields speeds up the response.
  • Use mobile category ("device_category": "mobile") to query data from mobile monitoring units.