Send a POST to
https://emms.margpharma.com/api/readings with HTTP Basic Auth
using the sensor-writer credentials and a JSON body.
id (station) and timestamp
(ISO 8601) are required. All metrics are numeric and optional; values
may be sent as numbers or strings.
| Field | Metric | Unit |
|---|
The AQI shown on the Real-Time and Dashboard pages is computed client-side following the U.S. EPA Technical Assistance Document for the Reporting of Daily Air Quality (May 2026 edition, EPA‑403/B‑26‑003). It mirrors the method behind the live AirNow map, so our values line up with what AirNow reports for the same concentrations.
Each pollutant gets a sub-index from its measured concentration using the EPA piecewise-linear equation (Equation 1):
I = (I_hi − I_lo) / (C_hi − C_lo) × (C − C_lo) + I_lo
where C is the concentration and the
C_lo / C_hi / I_lo / I_hi values are the breakpoints that
bracket it (Table 6 of the document). The overall AQI is the
maximum of all pollutant sub-indices; the pollutant
that produced it is the "dominant" pollutant. See
AQI
Basics and the interactive
AQI
Calculator.
Breakpoints are defined in EPA's native units. Our sensor
reports gases in µg/m³, so those are converted to ppm/ppb
before indexing using the ideal-gas relation at EPA reference
conditions (25 °C, 1 atm):
ppb = µg/m³ × 24.45 / MW. Because the
conversion assumes reference temperature and pressure, the gas
sub-indices are approximate; the particulates (measured directly) are
usually dominant.
| Pollutant | Averaging / basis | Breakpoint unit | Source value |
|---|---|---|---|
pm25 PM2.5 | 12-hour NowCast | µg/m³ | µg/m³ (direct) |
pm10 PM10 | 12-hour NowCast | µg/m³ | µg/m³ (direct) |
o3 O₃ | max of 8-hour & 1-hour | ppm | µg/m³ → ppm |
co CO | 8-hour | ppm | µg/m³ → ppm |
so2 SO₂ | 1-hour | ppb | µg/m³ → ppb |
no2 NO₂ | 1-hour | ppb | µg/m³ → ppb |
Note — CO unit mismatch.
The field reference labels co (and co2) as
ppm, but the production data shows CO values are actually
in µg/m³ (a median around 600 and peaks over 4000, which are
only physically plausible as µg/m³ — as ppm they would
be far past lethal). The AQI treats co as µg/m³
and converts it, which is correct for this data. The ppm
label in the schema is likely wrong and should be reconciled;
co2 is not an AQI pollutant, so its label does not affect
the index.
PM2.5 and PM10 are run through EPA's 12-hour
NowCast rather than a single instantaneous reading, so
the real-time number responds quickly when air quality changes (for
example during smoke) while staying stable when it does not. The weight
factor is 1 − (max − min) / max over the last
12 hourly values, clamped to [0.5, 1]; recent hours are
weighted more heavily. This matches how AirNow reports hourly PM.
The sensor uses sentinel values for channels it cannot
read: PM reports -1 and a gas channel that is absent
reports 0. These are treated as missing
rather than real concentrations — missing PM hours are skipped by
the NowCast, and a gas with no reading is left out of the AQI instead of
scoring a misleading "Good" sub-index or becoming the dominant
pollutant.
The sub-index value maps to one of six categories (Table 1 of the document):
Values above 500 are not clamped: per Table 6 footnote 4, Equation 1 keeps being applied using the concentration anchored to an AQI of 500, so extreme readings can report above 500.