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 →.
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
| Parameter | Type | Default | Description |
|---|---|---|---|
token | string | — | Required |
limit | integer | 1000 | Max results per page (max: 2000) |
skip | integer | 0 | Records 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.
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
| Parameter | Type | Description |
|---|---|---|
admin_level | string | Filter by level: country, province, city, district, subcounty, county, division, parish, and other administrative levels defined for your region |
id | string | Filter by a specific Grid ID |
limit | integer | Max results per page. Default 30, max 80 |
skip | integer | Records to skip. Default 0 |
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 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
}
}
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
| Parameter | Type | Default | Description |
|---|---|---|---|
token | string | — | Required |
limit | integer | 1000 | Max results per page (max: 2000) |
skip | integer | 0 | Records 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 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
| Parameter | Type | Default | Description |
|---|---|---|---|
token | string | — | Required |
limit | integer | 1000 | Max results per page (max: 2000) |
skip | integer | 0 | Records 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
| Parameter | Type | Default | Description |
|---|---|---|---|
token | string | — | Required |
latitude | number | — | Required. Must have at least 5 decimal places of precision |
longitude | number | — | Required. Must have at least 5 decimal places of precision |
radius | number | — | Required. Search radius in kilometres |
online_status | string | — | Optional. Filter to online or offline sites only |
limit | integer | 30 | Max 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
}
]
}
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
| Parameter | Type | Default | Description |
|---|---|---|---|
token | string | — | Required |
limit | integer | 1000 | Max results per page (max: 2000) |
skip | integer | 0 | Records 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
| Category | Description |
|---|---|
lowcost | Low-cost optical particle sensors (the majority of AirQo devices) |
bam | Beta Attenuation Monitor — reference-grade instruments |
gas | Gas 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
| Parameter | Type | Description |
|---|---|---|
token | string | Required |
admin_level | string | Filter by level: country, province, state, district, county, subcounty, division, parish, village, and other administrative levels defined for your region (not an exhaustive list) |
name | string | Filter by location name (partial match) |
limit | integer | Max results per page |
skip | integer | Records 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
- Call the grid summary endpoint with
admin_level=city - Locate your city by
long_name, paging through the results if it isn't on the first page - Copy the
_idvalue — that is your Grid ID
Find all Site IDs in your Grid
- Call
GET /api/v2/devices/grids/{GRID_ID}/generate - Use the
sites_and_devices.site_idsarray directly
Find all Device IDs in your Cohort
- Call
GET /api/v2/devices/cohorts/{COHORT_ID}/generate - Use the
sites_and_devices.device_idsarray directly
See also: Finding IDs →