Skip to main content

Metadata API

The Metadata API lets you browse AirQo's own monitoring infrastructure — grids, cohorts, sites, devices, and geographic boundaries — so you can discover the right identifiers before querying measurement or forecast data.

All endpoints require your token query parameter, with one exception noted below. See Authentication →.

Looking for monitors beyond AirQo's own fleet?

This page is scoped to infrastructure AirQo directly manages. For a broader, cross-manufacturer registry of monitors across Africa — including reference-grade instruments and equipment run by other operators (ministries, embassies, universities, other sensor networks) — see the Africa Network Coverage API →. AirQo's own public sites appear in both; Africa Network Coverage additionally includes monitors that were never registered as AirQo sites/devices at all.


Grids​

Grids are geographically-defined groupings of monitoring sites, typically aligned to a city or administrative district.

List all grids​

GET /api/v2/devices/metadata/grids?token={SECRET_TOKEN}

Returns all publicly visible grids.

Query parameters

ParameterTypeDefaultDescription
tokenstring—Required
limitinteger1000Max results per page (max: 2000)
skipinteger0Records to skip

Example response

{
"success": true,
"grids": [
{
"_id": "64b7ac8fd7249f0029feca80",
"name": "nairobi_city",
"long_name": "Nairobi City",
"admin_level": "city",
"visibility": true,
"network": "airqo",
"createdAt": "2023-07-19T10:25:00.000Z"
}
],
"meta": {
"total": 42,
"skip": 0,
"limit": 1000,
"page": 1,
"pages": 1
}
}

Grid summary with site details​

GET /api/v2/devices/grids/summary

Returns grids with their constituent site details nested inside. This is the most useful endpoint for discovering Grid IDs and the sites within them.

No authentication required

Unlike most endpoints on this page, grid summary is publicly accessible without a token — it's the recommended way to pull AirQo's monitoring station locations into a public research or mapping project.

Query parameters

ParameterTypeDescription
admin_levelstringFilter by level: country, province, city, district, subcounty, county, division, parish, and other administrative levels defined for your region
idstringFilter by a specific Grid ID
limitintegerMax results per page. Default 30, max 80
skipintegerRecords to skip. Default 0
note

admin_level isn't a fixed list — it reflects whatever administrative levels exist for the regions AirQo covers, so you may encounter values beyond the ones listed above.

A default call doesn't return every grid

A default call returns up to 30 grids, so a grid missing from the first page may be on a later one. Use limit and skip to page through until you have fetched meta.total grids.

Example — page through grids in blocks of 80

# Page 1
curl "https://api.airqo.net/api/v2/devices/grids/summary?limit=80&skip=0"

# Page 2
curl "https://api.airqo.net/api/v2/devices/grids/summary?limit=80&skip=80"

Example — filter to city-level grids

curl "https://api.airqo.net/api/v2/devices/grids/summary?admin_level=city"

Example response

{
"success": true,
"message": "Successfully retrieved grids",
"grids": [
{
"_id": "67c96c4771c7b0001383dde1",
"name": "kampala_city",
"long_name": "Kampala City",
"admin_level": "city",
"visibility": true,
"network": "airqo",
"createdAt": "2025-03-06T09:35:03.672Z",
"sites": [
{
"_id": "60d058c8048305120d2d6145",
"name": "Civic Centre, Kampala Central",
"country": "Uganda",
"district": "Kampala",
"city": "Kampala",
"formatted_name": "5 Jinja Road, Kampala, Uganda",
"approximate_latitude": 0.317725206350973,
"approximate_longitude": 32.5925094863702,
"data_provider": "AirQo"
}
]
}
],
"meta": {
"total": 43,
"limit": 30,
"skip": 0,
"page": 1,
"totalPages": 2
}
}
Coordinates are approximated

approximate_latitude and approximate_longitude are intentionally offset by up to ~0.5km from each site's true physical location, to protect the privacy of hosting institutions. The offset is consistent per site, so it's safe for temporal and area-based analysis, but proximity queries should use a widened radius. See Location Approximation for the full policy and research best practices.

Example — extract coordinates for mapping (JavaScript)

const limit = 80;
const grids = [];
let total = 0;
let page = [];

do {
const response = await fetch(
`https://api.airqo.net/api/v2/devices/grids/summary?limit=${limit}&skip=${grids.length}`
);
const data = await response.json();
page = data.grids;
total = data.meta.total;
grids.push(...page);
} while (page.length > 0 && grids.length < total);

const stationCoordinates = grids
.flatMap((grid) => grid.sites || [])
.map((site) => ({
id: site._id,
name: site.name,
lat: site.approximate_latitude,
lng: site.approximate_longitude,
}));

Example — find stations near a point of interest, with an adjusted radius (Python)

import requests
import geopy.distance # pip install geopy

grids = []
while True:
data = requests.get(
'https://api.airqo.net/api/v2/devices/grids/summary',
params={'limit': 80, 'skip': len(grids)},
).json()
grids.extend(data['grids'])
if not data['grids'] or len(grids) >= data['meta']['total']:
break

all_sites = [site for grid in grids for site in grid.get('sites', [])]

poi = (0.33500, 32.57140) # latitude, longitude

# Use 500m rather than 100m to account for the ~0.5km coordinate offset
nearby_sites = [
site for site in all_sites
if geopy.distance.distance(
poi, (site['approximate_latitude'], site['approximate_longitude'])
).meters <= 500
]

Get a single grid​

GET /api/v2/devices/metadata/grids/{GRID_ID}?token={SECRET_TOKEN}

Returns full details for one grid.


Get all site and device IDs for a grid​

GET /api/v2/devices/grids/{GRID_ID}/generate?token={SECRET_TOKEN}

Returns every site ID and device ID belonging to the grid, as two flat lists. Use these to build sites/grid_ids and device_ids filters for Analytics API queries.

Example response

{
"success": true,
"message": "Successfully returned the Site IDs and the Device IDs",
"sites_and_devices": {
"site_ids": ["64f7b3e8c9d25a0013f2d456"],
"device_ids": ["65c8d4a2f1b45c0012a3e789"]
}
}

List countries​

GET /api/v2/devices/grids/countries?token={SECRET_TOKEN}

Returns all countries that have AirQo monitoring coverage. Useful for filtering the grid summary by country before drilling down to a city.


Cohorts​

Cohorts are organisationally-defined groups of devices, managed by the AirQo team on behalf of partner organisations.

List all public cohorts​

GET /api/v2/devices/metadata/cohorts?token={SECRET_TOKEN}

Returns all publicly visible cohorts.

Query parameters

ParameterTypeDefaultDescription
tokenstring—Required
limitinteger1000Max results per page (max: 2000)
skipinteger0Records to skip

Example response

{
"success": true,
"cohorts": [
{
"_id": "64b9c7d5e3f82b0014c5a123",
"name": "partner_network",
"long_name": "Partner Monitoring Network",
"visibility": true,
"network": "airqo",
"createdAt": "2023-08-01T00:00:00.000Z"
}
],
"meta": {
"total": 5,
"skip": 0,
"limit": 1000,
"page": 1,
"pages": 1
}
}
Private Cohorts

Private cohorts do not appear in this list. If your organisation has a private Cohort, contact support@airqo.net to obtain your Cohort ID.


Get a single cohort​

GET /api/v2/devices/metadata/cohorts/{COHORT_ID}?token={SECRET_TOKEN}

Returns full details for one cohort.


Get all site and device IDs for a cohort​

GET /api/v2/devices/cohorts/{COHORT_ID}/generate?token={SECRET_TOKEN}

Returns every site ID and device ID belonging to the cohort, as two flat lists. The structure matches the grid generate endpoint.

Example response

{
"success": true,
"message": "Successfully returned the Site IDs and the Device IDs",
"sites_and_devices": {
"site_ids": ["64f7b3e8c9d25a0013f2d456"],
"device_ids": ["65c8d4a2f1b45c0012a3e789"]
}
}

Sites​

Sites are fixed physical locations where sensors are deployed.

List all public sites​

GET /api/v2/devices/metadata/sites?token={SECRET_TOKEN}

Returns all publicly visible monitoring sites.

Query parameters

ParameterTypeDefaultDescription
tokenstring—Required
limitinteger1000Max results per page (max: 2000)
skipinteger0Records to skip

Example response

{
"success": true,
"sites": [
{
"_id": "64f7b3e8c9d25a0013f2d456",
"name": "kampala_road",
"long_name": "Kampala Road, Kampala",
"generated_name": "Kampala Road",
"latitude": 0.3476,
"longitude": 32.5825,
"network": "airqo",
"visibility": true,
"isOnline": true
}
],
"meta": {
"total": 250,
"skip": 0,
"limit": 1000,
"page": 1,
"pages": 1
}
}

Get a single site​

GET /api/v2/devices/metadata/sites/{SITE_ID}?token={SECRET_TOKEN}

Returns full details for one monitoring site, including coordinates, administrative context, and current online status.


Find the nearest sites to a coordinate​

GET /api/v2/devices/sites/nearest?token={SECRET_TOKEN}

Returns monitoring sites near a given point, sorted by distance (closest first). Useful when the place you're interested in doesn't correspond to a named site — see 3.3 Finding the Nearest Monitor to a Specific Location.

Query parameters

ParameterTypeDefaultDescription
tokenstring—Required
latitudenumber—Required. Must have at least 5 decimal places of precision
longitudenumber—Required. Must have at least 5 decimal places of precision
radiusnumber—Required. Search radius in kilometres
online_statusstring—Optional. Filter to online or offline sites only
limitinteger30Max results to return (max: 80)

Example — sites within 5 km, online only

curl "https://api.airqo.net/api/v2/devices/sites/nearest?latitude=0.34760&longitude=32.58250&radius=5&online_status=online&token={SECRET_TOKEN}"

Example response

{
"success": true,
"message": "successfully retrieved the nearest sites",
"sites": [
{
"_id": "64f7b3e8c9d25a0013f2d456",
"name": "kampala_road",
"long_name": "Kampala Road, Kampala",
"generated_name": "Kampala Road",
"latitude": 0.3476,
"longitude": 32.5825,
"network": "airqo",
"isOnline": true,
"distance_km": 0.42
}
]
}
Coordinate approximation applies here too

Like every other endpoint, returned site coordinates are subject to the ~0.5 km privacy approximation — factor that into how you interpret distance_km for very short radii.


Devices​

Devices are individual sensor units. Each device is deployed at a site.

List all public devices​

GET /api/v2/devices/metadata/devices?token={SECRET_TOKEN}

Returns all publicly visible devices.

Query parameters

ParameterTypeDefaultDescription
tokenstring—Required
limitinteger1000Max results per page (max: 2000)
skipinteger0Records to skip

Example response

{
"success": true,
"devices": [
{
"_id": "65c8d4a2f1b45c0012a3e789",
"name": "airqo_bx2847",
"category": "lowcost",
"status": "deployed",
"site_id": "64f7b3e8c9d25a0013f2d456",
"network": "airqo",
"isOnline": true,
"lastActive": "2025-09-28T08:45:00.000Z"
}
],
"meta": {
"total": 850,
"skip": 0,
"limit": 1000,
"page": 1,
"pages": 1
}
}

Device categories

CategoryDescription
lowcostLow-cost optical particle sensors (the majority of AirQo devices)
bamBeta Attenuation Monitor — reference-grade instruments
gasGas sensor units

Get a single device​

GET /api/v2/devices/metadata/devices/{DEVICE_ID}?token={SECRET_TOKEN}

Returns full details for one device, including its deployment site and online status.


Locations​

The locations endpoint lets you browse administrative boundaries at different geographic levels — useful for understanding what areas AirQo covers before querying a grid.

List locations​

GET /api/v2/devices/locations?token={SECRET_TOKEN}

Query parameters

ParameterTypeDescription
tokenstringRequired
admin_levelstringFilter by level: country, province, state, district, county, subcounty, division, parish, village, and other administrative levels defined for your region (not an exhaustive list)
namestringFilter by location name (partial match)
limitintegerMax results per page
skipintegerRecords to skip

Example — list all countries

curl "https://api.airqo.net/api/v2/devices/locations?admin_level=country&token=YOUR_SECRET_TOKEN"

Pagination​

All list endpoints support limit and skip parameters for pagination. The meta object in every response includes total, page, and the number of pages to help you navigate large result sets. The number of pages is pages on most endpoints and totalPages on grid summary.

Example — page through all sites in blocks of 100

# Page 1
curl "https://api.airqo.net/api/v2/devices/metadata/sites?limit=100&skip=0&token=YOUR_SECRET_TOKEN"

# Page 2
curl "https://api.airqo.net/api/v2/devices/metadata/sites?limit=100&skip=100&token=YOUR_SECRET_TOKEN"

Common workflows​

Find a Grid ID for a city​

  1. Call the grid summary endpoint with admin_level=city
  2. Locate your city by long_name, paging through the results if it isn't on the first page
  3. Copy the _id value — that is your Grid ID

Find all Site IDs in your Grid​

  1. Call GET /api/v2/devices/grids/{GRID_ID}/generate
  2. Use the sites_and_devices.site_ids array directly

Find all Device IDs in your Cohort​

  1. Call GET /api/v2/devices/cohorts/{COHORT_ID}/generate
  2. Use the sites_and_devices.device_ids array directly

See also: Finding IDs →