{"openapi":"3.1.0","jsonSchemaDialect":"https://json-schema.org/draft/2020-12/schema","info":{"title":"Haze Monitor Public API","summary":"Read-only air-quality, weather, place-search and map data for Malaysia and Singapore.","description":"The Haze Monitor API exposes public observations used by hazemonitor.com. No API key is required. Check timestamps before using readings, respect response Cache-Control headers, and use official DOE or NEA sources for public-health guidance.","version":"1.0.0","contact":{"name":"Haze Monitor","url":"https://hazemonitor.com/contact","email":"mohtashammurshid@gmail.com"}},"servers":[{"url":"https://hazemonitor.com","description":"Production, read-only public data"}],"externalDocs":{"description":"Developer guide and quickstart","url":"https://hazemonitor.com/developers"},"tags":[{"name":"Air quality","description":"Current and historical pollutant index observations."},{"name":"Context","description":"Optional place, weather and boundary context for air-quality displays."}],"security":[],"paths":{"/api/air-quality":{"get":{"operationId":"getMalaysiaAirQuality","summary":"Get current Malaysia API/IPU readings","description":"Returns the latest available Department of Environment Malaysia observations for all reporting stations. Each station includes its own observation time; the response is not a forecast.","tags":["Air quality"],"responses":{"200":{"description":"Current station observations.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AirQualitySnapshot"}}}},"405":{"description":"The endpoint was called with a method other than GET or HEAD.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"502":{"description":"The current or archived DOE observations could not be loaded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/air-quality/history":{"get":{"operationId":"getMalaysiaAirQualityHistory","summary":"Get Malaysia API/IPU history","description":"With no query parameters, returns recent hourly readings for all stations. With both station and range, returns a 30-day or 12-month aggregate series for one station.","tags":["Air quality"],"parameters":[{"name":"station","in":"query","required":false,"description":"DOE station identifier. Required when range is supplied.","schema":{"type":"string","minLength":1,"maxLength":40,"pattern":"^[a-zA-Z0-9_-]+$"},"example":"CA16W"},{"name":"range","in":"query","required":false,"description":"Aggregate period. Supply station with this parameter. Omit both parameters for recent hourly history.","schema":{"type":"string","enum":["month","year"]}}],"responses":{"200":{"description":"Recent hourly history or a station aggregate series.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/AirQualityHistory"},{"$ref":"#/components/schemas/ArchiveSeries"}]}}}},"400":{"description":"The station and range parameter combination is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"405":{"description":"The endpoint was called with a method other than GET or HEAD.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"502":{"description":"Historical observations could not be loaded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/singapore":{"get":{"operationId":"getSingaporeAirQuality","summary":"Get Singapore PSI and PM2.5 readings","description":"Returns NEA observations for all five Singapore regions. Omit range for current data or set range=24h for recent observations. PSI and PM2.5 are separate measurements.","tags":["Air quality"],"parameters":[{"name":"range","in":"query","required":false,"description":"Include recent observations when set to 24h.","schema":{"type":"string","enum":["24h"]}}],"responses":{"200":{"description":"Current or recent Singapore regional observations.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SingaporeSnapshot"}}}},"400":{"description":"The range parameter is unsupported.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"405":{"description":"The endpoint was called with a method other than GET or HEAD.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"502":{"description":"Singapore observations could not be loaded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/geocode":{"get":{"operationId":"searchLocations","summary":"Search for a place","description":"Searches public geocoding providers and returns up to 14 merged place results. Use the coordinates with the weather endpoint or to compare a place with monitoring stations.","tags":["Context"],"parameters":[{"name":"query","in":"query","required":true,"description":"Place name to search for.","schema":{"type":"string","minLength":2,"maxLength":100},"example":"Kuala Lumpur"}],"responses":{"200":{"description":"Matching places, possibly an empty list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LocationSearchResponse"}}}},"400":{"description":"The query is missing or outside the accepted length.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"405":{"description":"The endpoint was called with a method other than GET or HEAD.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"502":{"description":"All configured geocoding providers failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/weather":{"get":{"operationId":"getWeatherContext","summary":"Get weather context for coordinates","description":"Returns current Open-Meteo weather context for one coordinate pair. This data can help explain local conditions but does not change the reported air-quality index.","tags":["Context"],"parameters":[{"name":"latitude","in":"query","required":true,"description":"Latitude in decimal degrees.","schema":{"type":"number","minimum":-90,"maximum":90},"example":3.139},{"name":"longitude","in":"query","required":true,"description":"Longitude in decimal degrees.","schema":{"type":"number","minimum":-180,"maximum":180},"example":101.687}],"responses":{"200":{"description":"Current weather context for the coordinates.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Weather"}}}},"400":{"description":"One or both coordinates are missing or invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"405":{"description":"The endpoint was called with a method other than GET or HEAD.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"502":{"description":"Weather context could not be loaded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/state-boundaries":{"get":{"operationId":"getMalaysiaStateBoundaries","summary":"Get Malaysia state boundaries","description":"Returns simplified GeoJSON Polygon and MultiPolygon features for Malaysian administrative level 1 boundaries. This endpoint contains map geometry, not air-quality readings.","tags":["Context"],"responses":{"200":{"description":"A GeoJSON FeatureCollection of state boundaries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StateBoundaryCollection"}}}},"405":{"description":"The endpoint was called with a method other than GET or HEAD.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"502":{"description":"State boundary geometry could not be loaded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}}},"components":{"schemas":{"ApiError":{"type":"object","additionalProperties":false,"required":["error","code","message","resolution","requestId"],"properties":{"error":{"type":"string","description":"Backward-compatible copy of message."},"code":{"type":"string","description":"Stable machine-readable error code.","enum":["AIR_QUALITY_UNAVAILABLE","GEOCODING_UNAVAILABLE","HISTORY_UNAVAILABLE","INVALID_COORDINATES","INVALID_HISTORY_QUERY","INVALID_QUERY","INVALID_RANGE","METHOD_NOT_ALLOWED","NOT_FOUND","SINGAPORE_DATA_UNAVAILABLE","STATE_BOUNDARIES_UNAVAILABLE","WEATHER_UNAVAILABLE"]},"message":{"type":"string","description":"Short explanation of the failure."},"resolution":{"type":"string","description":"Specific next action the caller can take."},"requestId":{"type":"string","format":"uuid","description":"Request identifier for support and log correlation."},"fetchedAt":{"type":"string","format":"date-time","description":"When the failed data fetch was attempted, when applicable."}}},"AirQualityCategory":{"type":"string","enum":["Good","Moderate","Unhealthy","Very Unhealthy","Hazardous","Unavailable"]},"AirQualityStation":{"type":"object","additionalProperties":false,"required":["id","name","fullName","state","region","latitude","longitude","api","pm10Api","category","pollutant","observedAt"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"fullName":{"type":"string"},"state":{"type":"string"},"region":{"type":"string"},"latitude":{"type":"number","minimum":-90,"maximum":90},"longitude":{"type":"number","minimum":-180,"maximum":180},"api":{"type":["number","null"],"minimum":0},"pm10Api":{"type":["number","null"],"minimum":0},"category":{"$ref":"#/components/schemas/AirQualityCategory"},"pollutant":{"type":["string","null"]},"observedAt":{"type":["string","null"],"format":"date-time"},"isFallback":{"type":"boolean"}}},"AirQualitySnapshot":{"type":"object","additionalProperties":false,"required":["source","sourceUrl","fetchedAt","observedAt","stationCount","reportingCount","stations"],"properties":{"source":{"type":"string","const":"Department of Environment Malaysia"},"sourceUrl":{"type":"string","format":"uri"},"fetchedAt":{"type":"string","format":"date-time"},"observedAt":{"type":["string","null"],"format":"date-time"},"stationCount":{"type":"integer","minimum":0},"reportingCount":{"type":"integer","minimum":0},"stations":{"type":"array","items":{"$ref":"#/components/schemas/AirQualityStation"}}}},"HistoryReading":{"type":"object","additionalProperties":false,"required":["observedAt","api"],"properties":{"observedAt":{"type":"string","format":"date-time"},"api":{"type":["number","null"],"minimum":0}}},"AirQualityHistory":{"type":"object","additionalProperties":false,"required":["source","fetchedAt","observedAt","stations"],"properties":{"source":{"type":"string","const":"Department of Environment Malaysia"},"fetchedAt":{"type":"string","format":"date-time"},"observedAt":{"type":["string","null"],"format":"date-time"},"stations":{"type":"array","items":{"type":"object","additionalProperties":false,"required":["id","readings"],"properties":{"id":{"type":"string"},"readings":{"type":"array","items":{"$ref":"#/components/schemas/HistoryReading"}}}}}}},"ArchivePoint":{"type":"object","additionalProperties":false,"required":["observedAt","api","low","high","count"],"properties":{"observedAt":{"type":"string","format":"date-time"},"api":{"type":["number","null"],"minimum":0},"low":{"type":["number","null"],"minimum":0},"high":{"type":["number","null"],"minimum":0},"count":{"type":"integer","minimum":0}}},"ArchiveSeries":{"type":"object","additionalProperties":false,"required":["source","stationId","range","fetchedAt","observedAt","availableFrom","points"],"properties":{"source":{"type":"string","const":"Department of Environment Malaysia"},"stationId":{"type":"string"},"range":{"type":"string","enum":["month","year"]},"fetchedAt":{"type":"string","format":"date-time"},"observedAt":{"type":["string","null"],"format":"date-time"},"availableFrom":{"type":["string","null"],"format":"date-time"},"points":{"type":"array","items":{"$ref":"#/components/schemas/ArchivePoint"}}}},"RegionalValues":{"type":"object","additionalProperties":false,"required":["central","north","south","east","west"],"properties":{"central":{"type":["number","null"],"minimum":0},"north":{"type":["number","null"],"minimum":0},"south":{"type":["number","null"],"minimum":0},"east":{"type":["number","null"],"minimum":0},"west":{"type":["number","null"],"minimum":0}}},"SingaporeObservation":{"type":"object","additionalProperties":false,"required":["observedAt","values"],"properties":{"observedAt":{"type":"string","format":"date-time"},"values":{"$ref":"#/components/schemas/RegionalValues"},"archivedRegions":{"type":"array","items":{"type":"string","enum":["central","north","south","east","west"]}}}},"SingaporeSnapshot":{"type":"object","additionalProperties":false,"required":["source","fetchedAt","psi","pm25","partial"],"properties":{"source":{"type":"string","const":"National Environment Agency Singapore"},"fetchedAt":{"type":"string","format":"date-time"},"psi":{"type":"array","items":{"$ref":"#/components/schemas/SingaporeObservation"}},"pm25":{"type":"array","items":{"$ref":"#/components/schemas/SingaporeObservation"}},"partial":{"type":"boolean"}}},"LocationSearchResponse":{"type":"object","additionalProperties":false,"required":["results"],"properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/Location"},"maxItems":14}}},"Location":{"type":"object","additionalProperties":false,"required":["id","name","latitude","longitude","countryCode","country","admin1","admin2"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"latitude":{"type":"number"},"longitude":{"type":"number"},"countryCode":{"type":["string","null"]},"country":{"type":["string","null"]},"admin1":{"type":["string","null"]},"admin2":{"type":["string","null"]}}},"Weather":{"type":"object","additionalProperties":false,"required":["source","latitude","longitude","observedAt","temperature","humidity","windSpeed","windDirection","visibilityKm","weatherCode"],"properties":{"source":{"type":"string","const":"Open-Meteo"},"latitude":{"type":"number"},"longitude":{"type":"number"},"observedAt":{"type":"string"},"temperature":{"type":"number"},"humidity":{"type":"number"},"windSpeed":{"type":"number"},"windDirection":{"type":"number"},"visibilityKm":{"type":"number"},"weatherCode":{"type":"number"}}},"StateBoundaryCollection":{"type":"object","additionalProperties":false,"required":["type","features"],"properties":{"type":{"type":"string","const":"FeatureCollection"},"features":{"type":"array","items":{"type":"object","required":["type","geometry","properties"],"properties":{"type":{"type":"string","const":"Feature"},"geometry":{"oneOf":[{"type":"object","required":["type","coordinates"],"properties":{"type":{"type":"string","const":"Polygon"},"coordinates":{"type":"array","items":{"type":"array","items":{"$ref":"#/components/schemas/Position"}}}}},{"type":"object","required":["type","coordinates"],"properties":{"type":{"type":"string","const":"MultiPolygon"},"coordinates":{"type":"array","items":{"type":"array","items":{"type":"array","items":{"$ref":"#/components/schemas/Position"}}}}}}]},"properties":{"type":"object","required":["shapeName","shapeISO","shapeID","shapeGroup","shapeType"],"properties":{"shapeName":{"type":"string"},"shapeISO":{"type":"string"},"shapeID":{"type":"string"},"shapeGroup":{"type":"string"},"shapeType":{"type":"string"}},"additionalProperties":true}}}}}},"Position":{"type":"array","prefixItems":[{"type":"number"},{"type":"number"}],"minItems":2}}}}