Skip to content

Public REST API version 1: reads without keys, JSON by default; station data intake — POST with a station key. The OpenAPI 3.1 specification is generated from the same schemas that validate server requests and responses.

Base URL
https://havo.tj
Authentication
Not required for reads (limit 120 requests per minute per IP). Station data intake: Authorization: Bearer <station key> header, separate limit of 60 requests per minute per station.
CORS
Requests from any website are allowed (Access-Control-Allow-Origin: *).

Air quality

GET/api/v1/stationscache 60 s

List of monitoring stations

Public stations (except decommissioned) as a GeoJSON FeatureCollection with the current index level; 0 means no data or stale.

Example request

curl "https://havo.tj/api/v1/stations"
Open in browser → →

Responses

200application/jsonSuccessful response

  • type "FeatureCollection"required
  • features object[]required
    • type "Feature"required
    • id integerrequired
    • geometry objectrequired
      • type "Point"required
      • coordinates [number, number]required
    • properties objectrequired
      • id integerrequired
      • slug stringrequired
      • code stringrequired — Station code, e.g. DU-01
      • name objectrequired — Localized text: Tajik (tg), Russian (ru), English (en)
        • tg string
        • ru string
        • en string
      • kind "air" | "meteo" | "combined"required
      • status "active" | "suspended" | "decommissioned"required
      • region object | nullrequired
        • tg string
        • ru string
        • en string
      • level integerrequired — Air quality level 1–5 (1–6 for six-category scales such as US AQI); 0 — no data
      • indexValue number | nullrequired — Continuous index value (level + fraction)
      • indexTime string (date-time) | nullrequired — Time of the index value
GET/api/v1/stations/{slug}/measurementscache 60 s

Station measurement series

Hourly (24h, 7d) or daily local-time (30d, 1y) means of valid measurements with min/max and count. CSV with format=csv. Values and the unit field use the display unit chosen by the Agency (µg/m³ or mg/m³).

Parameters
ParameterInTypeDescription
slugrequiredpathstringStation address (the slug field from the station list)
rangequery24h | 7d | 30d | 1y = 24hPeriod: 24h, 7d, 30d or 1y
pollutantquerystringPollutant code (PM25, PM10, NO2, SO2, CO, O3, NO, CH2O, C6H5OH, HF, TSP, CO2, TVOC); all pollutants when omitted
formatqueryjson | csv = jsonjson or csv

Example request

curl "https://havo.tj/api/v1/stations/dushanbe-centre/measurements?range=7d&pollutant=PM25"
Open in browser → →

Responses

200application/jsonSuccessful response

  • station objectrequired
    • id integerrequired
    • slug stringrequired
    • code stringrequired
    • name objectrequired — Localized text: Tajik (tg), Russian (ru), English (en)
      • tg string
      • ru string
      • en string
  • range "24h" | "7d" | "30d" | "1y"required
  • from string (date-time)required — ISO 8601 timestamp (UTC)
  • to string (date-time)required — ISO 8601 timestamp (UTC)
  • aggregation "hour" | "day"required — hour for 24h/7d, day (local time) for 30d/1y
  • timezone "Asia/Dushanbe"required
  • series object[]required
    • pollutant objectrequired
      • id integerrequired
      • code stringrequired
      • shortName stringrequired
      • unit stringrequired — Display unit configured by the Agency: µg/m³ or mg/m³ (CO always mg/m³; CO2 ppm, TVOC ppb). Values are converted to it
      • decimals integerrequired
    • points object[]required
      • t string (date-time)required — Start of the hour/day
      • v number | nullrequired — Mean of valid measurements, in `unit`
      • min number | nullrequired
      • max number | nullrequired
      • n integerrequired — Number of measurements in the bin

200text/csvCSV (UTF-8 with BOM): time_utc, time_local, pollutant, value, min, max, n

400application/jsonInvalid parameters

  • error stringrequired

404application/jsonObject not found

  • error stringrequired
GET/api/v1/index/currentcache 60 s

Current index by station

The latest computed index of every public station: level, value, dominant pollutant, sub-indices and a stale flag (older than 6 hours). For the RATIO_TO_LIMIT scale the response includes the Agency method indicators: NO₂+SO₂ summation, maximum single concentrations with the ratio to the single MPC, high/extremely high pollution flags; units — display units per pollutant.

Parameters
ParameterInTypeDescription
scalequerystringScale code: NATIONAL_OPERATIONAL (default), WHO_24H or IZA

Example request

curl "https://havo.tj/api/v1/index/current"
Open in browser → →

Responses

200application/jsonSuccessful response

  • scale objectrequired
    • code stringrequired
    • kind "breakpoints" | "ratio_to_limit" | "iza"required
    • levels object[]required
      • level integerrequired
      • color stringrequired — Hex colour
      • label objectrequired — Localized text: Tajik (tg), Russian (ru), English (en)
        • tg string
        • ru string
        • en string
      • lo number
      • hi number | null
  • generatedAt string (date-time)required — ISO 8601 timestamp (UTC)
  • units objectrequired — Display unit per pollutant code (µg/m³ or mg/m³ as configured by the Agency; ppm, ppb)
  • stations object[]required
    • id integerrequired
    • slug stringrequired
    • code stringrequired
    • name objectrequired — Localized text: Tajik (tg), Russian (ru), English (en)
      • tg string
      • ru string
      • en string
    • level integerrequired — Air quality level 1–5 (1–6 for six-category scales such as US AQI); 0 — no data
    • value number | nullrequired
    • time string (date-time) | nullrequired
    • stale booleanrequired — Index older than 6 hours
    • dominantPollutant string | nullrequired — Pollutant code with the worst sub-index
    • subIndices objectrequired
      • value numberrequired — Concentration used for the sub-index, in the display unit
      • level integerrequired
      • index numberrequired — Continuous sub-index; for RATIO_TO_LIMIT — ratio to the daily limit
    • indicators object | nullrequired
      • no2so2Sum number | nullrequired — NO2 + SO2 summation: sum of the daily-limit ratios; null when one of them is not measured
      • highPollution booleanrequired — High pollution (ВЗ): a pollutant reached 10 single limits
      • extremeHighPollution booleanrequired — Extremely high pollution (ЭВЗ): a pollutant reached 50 single limits
      • maxSingle objectrequired
        • value numberrequired — Maximum single (hourly) concentration of the last 24 hours, in the display unit
        • ratio number | nullrequired — Ratio to the single limit; null without a limit
      • flagged objectrequired — Pollutants with high/extremely high pollution: ratio to the single limit

404application/jsonObject not found

  • error stringrequired
GET/api/v1/exceedancescache 60 s

Active threshold exceedances

Open exceedance events according to the Agency's alert rules (portal channel).

Example request

curl "https://havo.tj/api/v1/exceedances"
Open in browser → →

Responses

200application/jsonSuccessful response

  • generatedAt string (date-time)required — ISO 8601 timestamp (UTC)
  • events object[]required
    • id integerrequired
    • startedAt string (date-time)required — ISO 8601 timestamp (UTC)
    • station objectrequired
      • id integerrequired
      • slug stringrequired
      • name objectrequired — Localized text: Tajik (tg), Russian (ru), English (en)
        • tg string
        • ru string
        • en string
    • rule stringrequired
    • kind "pollutant" | "index"required
    • pollutant string | nullrequired
    • unit string | nullrequired
    • value number | nullrequired
    • threshold numberrequired
    • level integer | nullrequired

Forecasts

GET/api/v1/forecast/{slug}cache 300 s

Weather forecast for a location

Hourly series and daily aggregates from SmartMet Server; model run time and data producer.

Parameters
ParameterInTypeDescription
slugrequiredpathstringForecast location address, e.g. dushanbe
hoursqueryinteger = 72Number of hours in the hourly series (1–240)
daysqueryinteger = 10Number of days in the daily forecast (1–16)

Example request

curl "https://havo.tj/api/v1/forecast/dushanbe?hours=48&days=7"
Open in browser → →

Responses

200application/jsonSuccessful response

  • location objectrequired
    • slug stringrequired
    • name objectrequired — Localized text: Tajik (tg), Russian (ru), English (en)
      • tg string
      • ru string
      • en string
    • lat numberrequired
    • lon numberrequired
    • region object | nullrequired
      • tg string
      • ru string
      • en string
  • producer string | nullrequired — SmartMet producer, e.g. ecmwf
  • originTime string (date-time) | nullrequired — Model run time
  • fetchedAt string (date-time) | nullrequired
  • timeZone "Asia/Dushanbe"required
  • hourly object[]required
    • time string (date-time)required — ISO 8601 timestamp (UTC)
    • t number | null — Air temperature, °C
    • ws number | null — Wind speed, m/s
    • wd number | null — Wind direction, degrees
    • gust number | null — Wind gust, m/s
    • rh number | null — Relative humidity, %
    • p number | null — Pressure, hPa
    • dew number | null — Dew point, °C
    • precip number | null — Precipitation, mm/h
    • cloud number | null — Total cloud cover, %
    • symbol integer | null — FMI SmartSymbol code (1xx — night variant)
  • daily object[]required
    • date string (date-time)required — Local midnight (UTC+5) of the day
    • tmax number | null
    • tmin number | null
    • precipSum number | null — Daily precipitation, mm
    • ws number | null — Max wind speed, m/s
    • cloud number | null
    • symbol integer | null

404application/jsonObject not found

  • error stringrequired
GET/api/v1/silam/{slug}cache 600 s

SILAM air quality forecast for a location

Hourly concentrations and levels by pollutant plus a daily summary (local-day mean, level by the worst pollutant).

Parameters
ParameterInTypeDescription
slugrequiredpathstringForecast location address, e.g. dushanbe

Example request

curl "https://havo.tj/api/v1/silam/dushanbe"
Open in browser → →

Responses

200application/jsonSuccessful response

  • location objectrequired
    • slug stringrequired
    • name objectrequired — Localized text: Tajik (tg), Russian (ru), English (en)
      • tg string
      • ru string
      • en string
    • lat numberrequired
    • lon numberrequired
  • run object | nullrequired
    • originTime string (date-time)required — ISO 8601 timestamp (UTC)
    • fetchedAt string (date-time)required — ISO 8601 timestamp (UTC)
  • units objectrequired — Unit per pollutant code
  • hourly objectrequired — Hourly [time, value, level] per pollutant
  • days object[]required
    • date stringrequired — YYYY-MM-DD, local time
    • level integerrequired — Air quality level 1–5 (1–6 for six-category scales such as US AQI); 0 — no data
    • dominantPollutant string | nullrequired
    • means objectrequired — Daily mean per pollutant code

404application/jsonObject not found

  • error stringrequired
GET/api/v1/silam/layerscache 600 s

Catalogue of SILAM raster layers

Grid bounds and PNG frames by pollutant and hour of the latest run — for map overlays.

Example request

curl "https://havo.tj/api/v1/silam/layers"
Open in browser → →

Responses

200application/jsonSuccessful response

  • run object | nullrequired
    • originTime string (date-time)required — ISO 8601 timestamp (UTC)
    • fetchedAt string (date-time)required — ISO 8601 timestamp (UTC)
    • hours integerrequired
    • bounds object | nullrequired — Grid bounds in degrees for image placement
      • west numberrequired
      • south numberrequired
      • east numberrequired
      • north numberrequired
  • pollutants object[]required
    • code stringrequired
    • frames object[]required
      • time string (date-time)required — ISO 8601 timestamp (UTC)
      • url stringrequired — Relative URL of the PNG layer
      • max number | nullrequired — Maximum concentration in the domain

Warnings

GET/api/v1/alertscache 60 s

CAP warnings

Normalised list of SmartMet Alert warnings in the selected language (with fallback) or GeoJSON polygons for maps.

Parameters
ParameterInTypeDescription
activequery11 — only warnings active now
regionquerystringWarning region code (DU, SU, KT, GB, RRP)
langquerytg | ru | en = tgText language: tg, ru or en
daysqueryinteger = 7Horizon in days (1–14)
formatqueryjson | geojson = jsonjson or geojson

Example request

curl "https://havo.tj/api/v1/alerts?active=1&lang=ru"
Open in browser → →

Responses

200application/jsonSuccessful response

  • generatedAt string (date-time)required — ISO 8601 timestamp (UTC)
  • lang "tg" | "ru" | "en"required
  • count integerrequired
  • alerts object[]required
    • id integerrequired
    • identifier stringrequired — CAP identifier
    • sent string (date-time)required — ISO 8601 timestamp (UTC)
    • msgType "Alert" | "Update" | "Cancel" | "Ack" | "Error"required
    • language stringrequired
    • event stringrequired
    • headline stringrequired
    • description string | nullrequired
    • instruction string | nullrequired
    • severity "Extreme" | "Severe" | "Moderate" | "Minor" | "Unknown"required
    • color "red" | "orange" | "yellow" | "none"required
    • colorHex stringrequired
    • urgency stringrequired
    • certainty stringrequired
    • effective string (date-time) | nullrequired
    • onset string (date-time) | nullrequired
    • expires string (date-time) | nullrequired
    • start string (date-time)required — ISO 8601 timestamp (UTC)
    • end string (date-time) | nullrequired
    • active booleanrequired
    • upcoming booleanrequired
    • regions object[]required
      • code stringrequired
      • name objectrequired — Localized text: Tajik (tg), Russian (ru), English (en)
        • tg string
        • ru string
        • en string
    • areas string[]required — areaDesc values
    • web string | nullrequired
    • xml stringrequired — URL of the original CAP XML

200application/geo+jsonGeoJSON when format=geojson

  • type "FeatureCollection"required
  • generatedAt string (date-time)required — ISO 8601 timestamp (UTC)
  • features object[]required
    • type "Feature"required
    • id stringrequired
    • geometry objectrequired
      • type stringrequired
      • coordinates any[]required
    • properties objectrequired
      • id integerrequired
      • identifier stringrequired
      • event stringrequired
      • headline stringrequired
      • severity stringrequired
      • level integerrequired
      • color stringrequired
      • start string (date-time)required — ISO 8601 timestamp (UTC)
      • end string (date-time) | nullrequired
      • active booleanrequired
      • region string | nullrequired
      • areaDesc stringrequired
      • href stringrequired
GET/api/v1/alerts/{id}/xmlcache 300 s

Original CAP XML

The original CAP 1.2 message (status=Actual only).

Parameters
ParameterInTypeDescription
idrequiredpathstringWarning identifier from the list

Example request

curl "https://havo.tj/api/v1/alerts/1/xml"
Open in browser → →

Responses

200application/cap+xmlOriginal CAP 1.2 message

404application/jsonObject not found

  • error stringrequired

News

GET/api/v1/newscache 300 s

News

Published news with excerpt, category, cover and page link.

Parameters
ParameterInTypeDescription
localequerytg | ru | en = tgLanguage: tg, ru or en
pagequeryinteger = 1Page number
pageSizequeryinteger = 12Page size (1–50)
categoryquerystringCategory slug

Example request

curl "https://havo.tj/api/v1/news?locale=ru&pageSize=5"
Open in browser → →

Responses

200application/jsonSuccessful response

  • locale "tg" | "ru" | "en"required
  • page integerrequired
  • pageSize integerrequired
  • total integerrequired
  • items object[]required
    • id integerrequired
    • slug stringrequired
    • title stringrequired
    • excerpt stringrequired
    • publishedAt string (date-time) | nullrequired
    • updatedAt string (date-time)required — ISO 8601 timestamp (UTC)
    • pinned booleanrequired
    • category object | nullrequired
      • slug stringrequired
      • name stringrequired
    • cover string | nullrequired — Absolute URL of the cover image
    • url stringrequired — Absolute URL of the article
GET/api/v1/news/rss.xmlcache 300 s

News RSS

The 20 latest news items as RSS 2.0 in the selected language.

Parameters
ParameterInTypeDescription
localequerytg | ru | en = tgLanguage: tg, ru or en

Example request

curl "https://havo.tj/api/v1/news/rss.xml?locale=tg"
Open in browser → →

Responses

200application/rss+xmlRSS 2.0

Geography

GET/api/v1/regionscache 600 s

Administrative boundaries: regions

Regions, Dushanbe and the RRP with simplified polygons, centroids, station counts and the worst current index level (0 — no data). Cached for 10 minutes, ETag.

Example request

curl "https://havo.tj/api/v1/regions"
Open in browser → →

Responses

200application/jsonSuccessful response

  • type "FeatureCollection"required
  • features object[]required
    • type "Feature"required
    • id integerrequired
    • geometry objectrequired
      • type "MultiPolygon"required
      • coordinates any[]required
    • properties objectrequired
      • id integerrequired
      • code stringrequired
      • name objectrequired — Localized text: Tajik (tg), Russian (ru), English (en)
        • tg string
        • ru string
        • en string
      • stationsCount integerrequired — Public stations located inside the unit
      • level integerrequired — Worst current index level among the unit's stations; 0 — no data or stale
      • sortOrder integerrequired
      • centroid [number, number] | nullrequired
GET/api/v1/districtscache 600 s

Administrative boundaries: districts

Districts and cities of regional subordination with simplified polygons, region code, station counts and the worst current index level. Units without loaded geometry are omitted.

Example request

curl "https://havo.tj/api/v1/districts"
Open in browser → →

Responses

200application/jsonSuccessful response

  • type "FeatureCollection"required
  • features object[]required
    • type "Feature"required
    • id integerrequired
    • geometry objectrequired
      • type "MultiPolygon"required
      • coordinates any[]required
    • properties objectrequired
      • id integerrequired
      • code stringrequired
      • name objectrequired — Localized text: Tajik (tg), Russian (ru), English (en)
        • tg string
        • ru string
        • en string
      • stationsCount integerrequired — Public stations located inside the unit
      • level integerrequired — Worst current index level among the unit's stations; 0 — no data or stale
      • regionId integerrequired — Parent region id
      • regionCode stringrequired — Parent region code (DU, RRP, SU, KT, GB)

Station data intake

POST/api/v1/ingest/{code}

Measurement intake from a station

A logger, the station agent or vendor software sends a JSON array of measurements (up to 1000 per request). Time — ISO 8601 with offset or local (Asia/Dushanbe); pollutants — codes PM25, PM10, NO2, SO2, CO, O3 or synonyms; the default unit is the station's input unit. Values are converted to the pollutant unit, pass the QC range rule and are stored by the time/pollutant/averaging key — re-sending is idempotent and operator decisions are kept. The response lists accepted and rejected counts with per-record reasons. After storing, the index and alert rules are recomputed.

Parameters
ParameterInTypeDescription
coderequiredpathstringStation code from the registry (e.g. DU-01)

Authentication: Station API key from the station card in the admin panel; sent as Authorization: Bearer <key>. Only the key hash is stored in the database.

Request body

application/json

object[] | object

Example request

curl -X POST "https://havo.tj/api/v1/ingest/DU-01" \
  -H "Authorization: Bearer <station-api-key>" \
  -H "Content-Type: application/json" \
  -d '[
  {
    "time": "2026-10-18T07:00:00Z",
    "pollutant": "PM25",
    "value": 42.5
  },
  {
    "time": "2026-10-18 12:00",
    "pollutant": "pm2.5",
    "value": "12,5",
    "unit": "µg/m³"
  },
  {
    "time": "2026-10-18T07:00:00Z",
    "pollutant": "NO2",
    "value": 10,
    "unit": "ppb",
    "flag": "valid"
  },
  {
    "time": "2026-10-18T07:00:00Z",
    "pollutant": "CO",
    "value": null,
    "flag": "missing"
  }
]'

Responses

200application/jsonAt least one record accepted (reasons for rejected ones in errors)

  • station objectrequired
    • id integerrequired
    • code stringrequired
    • slug stringrequired
  • received integerrequired — Records in the request
  • accepted integerrequired — Records stored (distinct time/pollutant/averaging keys)
  • rejected integerrequired — Records rejected; see errors
  • inserted integerrequired — New rows
  • updated integerrequired — Existing rows overwritten (idempotent re-sends)
  • skippedLocked integerrequired — Rows kept as decided by an operator (qc_locked)
  • invalidRange integerrequired — Accepted values outside the QC range, stored with flag invalid
  • duplicates integerrequired — Records superseded by a later record with the same key
  • from string (date-time) | nullrequired
  • to string (date-time) | nullrequired
  • pollutants string[]required — Pollutant codes of accepted records
  • errors object[]required — Per-record reasons for rejected and superseded records
    • index integerrequired — Index of the record in the request array
    • message stringrequired — Reason: time, futureTime, tooOld, unknownPollutant:X, pollutantNotConfigured:X, emptyValue, value:X, unit:X, unitConversion:X, averagingMin:X, duplicate

400application/jsonInvalid parameters

  • error stringrequired — unauthorized | station_inactive | invalid_json | validation | payload_too_large | rate_limited
  • message string
  • issues object[]
    • path stringrequired
    • message stringrequired

401application/jsonMissing Authorization header or the key does not match the station code

  • error stringrequired — unauthorized | station_inactive | invalid_json | validation | payload_too_large | rate_limited
  • message string
  • issues object[]
    • path stringrequired
    • message stringrequired

403application/jsonStation is suspended or decommissioned

  • error stringrequired — unauthorized | station_inactive | invalid_json | validation | payload_too_large | rate_limited
  • message string
  • issues object[]
    • path stringrequired
    • message stringrequired

413application/jsonBody larger than 2 MB

  • error stringrequired — unauthorized | station_inactive | invalid_json | validation | payload_too_large | rate_limited
  • message string
  • issues object[]
    • path stringrequired
    • message stringrequired

422application/jsonNo record accepted — see errors

  • station objectrequired
    • id integerrequired
    • code stringrequired
    • slug stringrequired
  • received integerrequired — Records in the request
  • accepted integerrequired — Records stored (distinct time/pollutant/averaging keys)
  • rejected integerrequired — Records rejected; see errors
  • inserted integerrequired — New rows
  • updated integerrequired — Existing rows overwritten (idempotent re-sends)
  • skippedLocked integerrequired — Rows kept as decided by an operator (qc_locked)
  • invalidRange integerrequired — Accepted values outside the QC range, stored with flag invalid
  • duplicates integerrequired — Records superseded by a later record with the same key
  • from string (date-time) | nullrequired
  • to string (date-time) | nullrequired
  • pollutants string[]required — Pollutant codes of accepted records
  • errors object[]required — Per-record reasons for rejected and superseded records
    • index integerrequired — Index of the record in the request array
    • message stringrequired — Reason: time, futureTime, tooOld, unknownPollutant:X, pollutantNotConfigured:X, emptyValue, value:X, unit:X, unitConversion:X, averagingMin:X, duplicate

429application/jsonStation request limit exceeded (see Retry-After)

  • error stringrequired — unauthorized | station_inactive | invalid_json | validation | payload_too_large | rate_limited
  • message string
  • issues object[]
    • path stringrequired
    • message stringrequired

Data is licensed under CC BY 4.0 with attribution; FMI forecasts must credit the Finnish Meteorological Institute.

For websites without development there are iframe widgets /embed/aqi, /embed/forecast and /embed/alerts (see Open data).