How do you get real Singapore haze readings in Python? Use NEA’s free official data.gov.sg v2 real-time /pm25 endpoint, inspect data.items and readings.pm25_one_hourly, and print the regional values with their units and timestamps. The separate /psi endpoint provides current PSI, not a substitute for the official next-day PSI forecast. Data cover Singapore’s five reporting regions—not a sensor at every Punggol flat.
This tutorial provides usable Python requests code, response fields verified against the live v2 API, date filters, pagination, error handling, graphing principles and source attribution. The examples are for education and data literacy, not a new government-authorised haze-warning service.

Navigate the tutorial: What is the live Singapore haze API? · Why Python examples need error handling · How to request readings for a particular date · A real worked calculation with fictional teaching data · What about an AI haze predictor? · A seven-step educational project plan
What is the live Singapore haze API?
Two official NEA datasets are available without scraping a website
To retrieve Singapore’s measured one-hour PM2.5 programmatically, send an HTTP GET request to api-open.data.gov.sg/v2/real-time/api/pm25. To retrieve current PSI information, use the /psi endpoint. The datasets are published by the National Environment Agency through data.gov.sg, with readings grouped across five regions. The website indicates that the data are updated at approximately 15-minute intervals. This is not the same as having a sensor mounted at every Punggol block.
Why this is worth learning
A student can use the data to practise API requests, structured JSON parsing, units, time stamps, missing values and honest graphing with real public information. A parent may appreciate knowing why one app displays an east-region number while another displays a different city or index. A developer can make a small educational dashboard that links back to NEA without pretending to create an official forecast. The code should be treated as a learning example, not an emergency alert service that overrides NEA or MOH.
Read measured data before attempting predictions
An API reading is an observation
The /pm25 endpoint returns the latest available observed PM2.5 values unless a date parameter requests other records. The one-hour PM2.5 data describe ambient fine-particle mass concentration in micrograms per cubic metre. A data record includes a timestamp, update time, region metadata and readings by region. Each field matters: a ‘North’ concentration does not mean every rooftop, bus stop and living room in the north has that exact concentration.
Tomorrow’s forecast is a separate product
The /psi endpoint returns current PSI-related readings and sub-index information rather than simply issuing the government’s forecast for tomorrow. A common coding error is to call /psi, take its current 24-hour value and label it ‘forecast tomorrow’. That is incorrect. For next-day outdoor planning, use NEA’s official forecast page and the corresponding advisory; for actual immediate conditions, use the current one-hour PM2.5 record. Measured and predicted data are different products even when both come from the same environmental authority.
How the live PM2.5 response is organised
Look inside data.items
The 2026 data.gov.sg v2 response contains a top-level code, a data object and optional error information. Within data, the regionMetadata array describes region labels and coordinate information, while items contains one or more time-stamped observations. Each item has a readings object and a pm25_one_hourly map with keys including north, south, east, west and central. The currently returned success code is 0; a robust program should also check the HTTP status and whether items exist.
Do not hardcode the present air value into a guide
A number such as 45 µg/m³ is meaningful only with its reporting region and observation timestamp. The current feed will change as NEA updates it. A WordPress article with ‘Punggol is currently at 45’ would become stale or inaccurate, and the North region is not a measurement at the exact residential location of the reader. Instead, include the API endpoint and show how to inspect the newest value responsibly. Never compare it against an old screenshot without identifying the dates.
First working Python example
Install the small dependency
You can run this example on Python 3.9 or later with the requests library installed. In a personal terminal, the ordinary command is python -m pip install requests. For a school environment with managed computers, follow the institution’s policies rather than installing packages without permission. The example requests the public feed with a sensible timeout; it does not require a secret key for an ordinary basic request at the public rate limit.
Read, validate and print the official fields
The code checks for HTTP errors, verifies that data.items exists, then prints the timestamp and the regional PM2.5 estimates. It intentionally does not label the north reading as a private Punggol monitor. If the endpoint structure changes, consult the dataset’s current API specification instead of guessing a new property name. If the request fails, report that data could not be retrieved rather than showing a made-up value or claiming the air is normal.
Runnable example 1: latest regional one-hour PM2.5
# Python 3.9+ — install with: python -m pip install requests
import requests
from datetime import datetime
from zoneinfo import ZoneInfo
URL = "https://api-open.data.gov.sg/v2/real-time/api/pm25"
response = requests.get(URL, timeout=15)
response.raise_for_status()
payload = response.json()
if payload.get("code") != 0:
raise RuntimeError("API returned an error: " + str(payload.get("errorMsg")))
items = payload.get("data", {}).get("items", [])
if not items:
raise RuntimeError("No PM2.5 observations returned")
item = items[-1]
when = datetime.fromisoformat(item["timestamp"].replace("Z", "+00:00"))
sg_time = when.astimezone(ZoneInfo("Asia/Singapore"))
print("Observation time (SGT):", sg_time.isoformat())
values = item["readings"]["pm25_one_hourly"]
for region, ug_m3 in sorted(values.items()):
print(region, ug_m3, "µg/m³")
This example prints the actual API observation timestamp converted to Singapore time and the five region values. It does not hardcode today’s haze condition. Keep the source attribution when republishing results.
Why Python examples need error handling
APIs are not always reachable
A live API can experience network interruptions, rate limiting or temporary unavailable data. data.gov.sg documents HTTP 400 for invalid requests, 404 for missing data in a particular query, and 429 for exceeding allowed request rates, in addition to successful 200 responses. A Python program using raise_for_status() and a bounded timeout can stop with a meaningful error. A trustworthy dashboard should show ‘latest data unavailable’ rather than silently reusing a stale value without its time.
Do not retry endlessly
Repeatedly querying every second can consume rate limits without adding scientific value. The public data are not a sensor stream updating continuously every millisecond. A family decision interface should refresh at a sensible interval and display the last actual observation time. If a request encounters 429, stop or follow documented retry guidance rather than launching many simultaneous requests. Developers who need higher quotas can use the site’s optional API key according to its rules.
Understanding time and time zones
ISO strings should not lose their offset
The live API returns timestamps in an ISO 8601-compatible format, commonly including a Singapore time offset such as +08:00. In some published documentation examples, a trailing Z indicates UTC. These are different representations of a moment in time, not necessarily different measurements. A coding worksheet should parse the offset and convert to Asia/Singapore before displaying the time to local readers. Copying only ’20:00′ without date or zone can be misleading when historical data are involved.
Difference between timestamp and updatedTimestamp
The timestamp field identifies the observation time, while updatedTimestamp indicates when the record was updated or published. A sensor observation can be posted after the actual observation time, so they need not match exactly. A student should label them separately rather than claim the monitor read at the update time. This becomes important when correlating PM2.5 with a photograph taken at a specified hour or when writing a responsible news caption.
Why there are five regions, not every estate
How to interpret regionMetadata
The dataset provides region labels and coordinates for North, South, East, West and Central. Those geographic points are metadata for reporting regions; they do not necessarily identify the precise laboratory location of each instrument or represent individual street readings. Punggol lies in north-east Singapore, but a script fetching the ‘north’ dictionary key is retrieving a North-region public reading, not a measurement from a particular Punggol block.
Avoid invented coordinates or local detail
A Punggol parent may ask whether the value applies exactly to a Waterway Park entrance, SIT Punggol, or a bedroom near Northshore. The API cannot provide that exact claim. Use official NEA regional information for a general public reference and explain that local conditions and indoor air may differ. Do not create a marker labelled ‘live Punggol sensor at Waterway Point’ using regionMetadata’s label position. A map pin is not proof of a sampling device at that landmark.
PSI is a different dataset and unit
The current 24-hour PSI field
The separate /psi endpoint returns readings including psi_twenty_four_hourly by region, plus other pollutant sub-indexes and rolling pollutant data. A PSI number is an index, not a concentration in micrograms per cubic metre. The PM2.5 endpoint provides one-hour concentration readings. A programmer must use the correct label and should not subtract the PM2.5 concentration from the PSI and call the difference ‘pollution improvement’.
Why a current PSI is not the forecast
Even when an API has a psi_twenty_four_hourly field, it is a reported current 24-hour index, not the government’s next-day 24-hour forecast. The official public health system makes this distinction because the two have different uses: immediate activity adjustments versus advance scheduling. A website or app that calls one field ‘tomorrow’s PSI’ without a forecast source is misleading. The Punggol haze measurement guide covers the distinction for non-programmers, while this article shows it in data field names.
How to request readings for a particular date
An ordinary query parameter is enough
The PM2.5 API documentation accepts a date parameter such as 2026-10-17 for readings from that day. A date-time form can request the most recent record at a specified moment. This makes it useful for classroom studies comparing a fixed period rather than relying on today’s moving live values. The resulting dataset may contain many items and may need pagination to retrieve the full date; consult the current API specification.
Past data are not a forecast
A historic reading from 17 October should be labelled ‘observed on 17 October’, not presented as today’s value or proof of what will happen tomorrow. When building a chart, convert timestamps consistently and handle missing hours honestly. If no data are returned, that is not evidence that the pollution was zero. A student should write ‘data unavailable for requested period’ until the issue is understood.
Runnable example 2: one-hour PM2.5 versus current PSI
# Compare two different datasets; do not mix their units.
import requests
BASE = "https://api-open.data.gov.sg/v2/real-time/api/"
def get_latest(name):
r = requests.get(BASE + name, timeout=15)
r.raise_for_status()
data = r.json()
if data.get("code") != 0 or not data.get("data", {}).get("items"):
raise RuntimeError("No usable data for " + name)
return data["data"]["items"][-1]
pm = get_latest("pm25")
psi = get_latest("psi")
# "north" is a REGION example, NOT a sensor at a Punggol address.
print("North region one-hour PM2.5 (µg/m³):",
pm["readings"]["pm25_one_hourly"]["north"])
print("North region current 24-hour PSI (index):",
psi["readings"]["psi_twenty_four_hourly"]["north"])
# Do not label the /psi response as tomorrow's forecast.
The code deliberately labels the North-region example, avoiding a false assertion that the dataset has a unique sensor at a Punggol MRT entrance. It also avoids mislabelling current PSI as tomorrow’s forecast.
Pagination for longer historical requests
What paginationToken means
Some date-filtered API requests return a paginationToken when additional pages of observations are available. To retrieve a complete series, a program must make subsequent requests with that token as documented until there is no next page. Counting only the first page as an entire day could omit observations and produce misleading statistics. The program should also keep sensible request limits and stop if the API reports an error.
Careful statistics need a complete time series
A missing afternoon could seriously distort a daily maximum, mean or chart of changing smoke. Check sample count, time coverage and whether repeated records have identical timestamps before calculating statistics. An observed mean based on 12 of 24 intended hours should not be described as an official daily mean. When data are incomplete, report the limitation plainly. A small honest dataset is more educational than a grand-looking chart with hidden missing values.
Optional API keys and security
Public requests may work without a key
data.gov.sg documents an optional x-api-key request header for higher rate limits and support. Basic public calls can work without it. A student should never paste a private API key into a WordPress article, public GitHub repository or classroom screenshot. If a key is required for a larger integration, use appropriate server-side environment configuration and restrict sharing in accordance with the provider’s requirements.
Why refresh frequency matters
A clever-looking widget that polls every second can waste resources and trigger 429 rate limits even though NEA’s public regional readings update at a slower cadence. For a public website, cache sensibly, display the source and observation time, and ensure failure handling does not silently preserve an outdated green reading. The goal is to inform people, not create misleading ‘live’ animation. A properly designed educational widget is as clear when data are unavailable as when all fields return successfully.
Runnable example 3: date filtering and pagination
# A date-filtered request: observations for one calendar day.
import requests
endpoint = "https://api-open.data.gov.sg/v2/real-time/api/pm25"
params = {"date": "2026-10-17"}
all_items = []
while True:
response = requests.get(endpoint, params=params, timeout=15)
response.raise_for_status()
obj = response.json()
if obj.get("code") != 0:
raise RuntimeError(str(obj.get("errorMsg")))
page = obj.get("data", {})
all_items.extend(page.get("items", []))
token = page.get("paginationToken")
if not token:
break
params["paginationToken"] = token
print("Records returned:", len(all_items))
# Check time coverage and duplicates before computing an average.
This shows the documented paginationToken pattern for a date query. Real applications should implement polite rate limiting and preserve time coverage and error information.
How to graph a PM2.5 series without lying
Labels need units and time
If a student plots the North-region hourly PM2.5 readings, the horizontal axis should show observation times in Singapore time and the vertical axis should show µg/m³. Each point must come from an actual observation, with missing data represented appropriately. Avoid drawing a continuous line across a long outage without indicating the gap. A smoothing algorithm may make a chart easier to see but should not conceal rapid changes that matter for immediate activity advice.
Do not overlay PSI raw numbers as though they shared units
If also graphing 24-hour PSI, use a separate panel or clearly labelled axis and explain that it is an index with a different averaging period. A student who adds a 24-hour rolling index to a one-hour particle concentration is not producing a valid combined risk score. It is reasonable to compare whether the trends rose or fell over a period, with careful timing, but never turn a visual similarity into proof they measure the same thing.
A real worked calculation with fictional teaching data
Three hourly concentration estimates
Suppose three successive hourly North-region PM2.5 values in a fictional worksheet are 20, 32 and 44 µg/m³. Their simple arithmetic mean is (20 + 32 + 44)/3 = 32 µg/m³. The maximum of the three values is 44, and the increase from the first to third is 24 µg/m³. These are valid calculations on the three numbers. They are not an official PSI, not a full-day average, and not a forecast of the next hour.
What the learner should write
A correct conclusion might say: ‘The fictional one-hour PM2.5 measurements rose across three recorded hours. The three-point mean is 32 µg/m³. Because only three hours are included, it does not describe the whole day’s conditions.’ This is a stronger answer than declaring the family can cycle tomorrow because the mean is lower than an index threshold. The example teaches mathematical transparency, source labels and the limits of averages.
How to turn raw data into a Primary or SEC learning task
Primary students can learn careful graph reading
A Primary 5 or Primary 6 pupil can practise ordering values, interpreting a column chart or finding an arithmetic difference using clearly labelled hypothetical data. The assignment should first distinguish the source, date and unit, then ask a simple question. It is inappropriate to assign the child a personal medical decision or a scientifically precise health-risk calculation based on one observation. The data are a teaching context for numeracy and critical thinking.
Secondary students can ask more demanding questions
Under Singapore’s evolving Secondary Education Certificate context, older students may work with statistics, scatter plots, ratios, temporal patterns and uncertainty. A useful comparison might ask whether higher public PM2.5 values coincide with a weather event on the same date, while recognising that correlation does not prove causation. The Punggol Geography source-based haze guide and G3 Mathematics modelling article contain further original study approaches that complement, rather than duplicate, a coding tutorial.
Build a data dictionary before writing a dashboard
Five fields every chart needs
At minimum record the dataset name, publisher, exact endpoint, pollutant or index field, unit, and observation timestamp or time zone. For regional records, keep the official region key and whether the value is measured, calculated or forecast. A UI label like ‘Punggol PSI NOW’ hides too much because it may actually display the North region’s 24-hour PSI or a PM2.5 value from another time. A more accurate label reads ‘NEA North-region one-hour PM2.5, µg/m³, observed at …’.
Regional data are a public service, not a personal sensor
Do not combine data.gov.sg coordinates with a Google map and falsely place a pollution instrument at someone’s HDB block. Metadata helps depict reporting regions but is not proof of the exact station position. If a school project wants a Punggol map, it can show Punggol as a learning location and separately show which official reporting-region value it used. This is good scientific source attribution and avoids the illusion of hyperlocal precision.
Open data licensing and attribution
Reuse is allowed, but follow the conditions
Singapore’s Open Data Licence version 1.0 allows broad personal and commercial reuse of eligible data, including derived analyses, subject to its terms. It requires conspicuous acknowledgement of the dataset’s source and a link to the licence. It also prohibits implying government endorsement of your product. A family website or school dashboard should not use the NEA logo or the title ‘Official Punggol Haze Warning App’ in a way that makes an independent experiment look like an authorised government service.
A concrete attribution for a project
A good notice could say: ‘Contains information from National Environment Agency PM2.5 data accessed on [date] via data.gov.sg, under the Singapore Open Data Licence version 1.0’, with links to the dataset and licence. Update the date when actual new data are used. A blog article linking to an API as a source is different from redistributing data through a public live dashboard, which calls for carefully implementing the full licence conditions. Read the original terms; never invent a blanket exemption for student projects.
What about an AI haze predictor?
Train and test with appropriate time periods
A student may feed historic public data into a simple model to predict future pollution categories. That project can be educational, but it must separate past training periods from future test periods to prevent data leakage. A model that ‘predicts’ a value after reading that same timestamp in its input has demonstrated memorisation rather than forecasting skill. Models should report accuracy during difficult high-pollution days, not just clean days when predicting Normal is easy.
Do not misrepresent the result
A personal model built from public PM2.5 data is not the government’s official next-day PSI forecast or a licensed medical device. If the model says ‘90% sure tomorrow is clear’, the family should still use official NEA guidance and current observations. A model may miss new fires, wind shifts and rainfall. The Punggol AI haze forecasting article explains transport models and uncertainty separately; this API guide supplies the practical underlying data literacy.
Understand privacy and location carefully
These datasets do not require household tracking
NEA’s five region metadata entries describe public ambient measurements. An app does not need a student’s precise home address, child identity or continuous GPS tracking simply to display those values. Avoid collecting private user coordinates unless a legitimate use and appropriate consent justify it. A school project can operate with the public regional labels and timestamps alone.
Keep demo code free of secrets
Never store API keys, account credentials, private student records or school messaging tokens inside published code blocks. The examples here intentionally use publicly documented endpoints with no credentials. If a developer wants to integrate with classroom systems, the school should approve data handling separately. Environmental education should not become a reason to create an unnecessary database of children’s locations or personal health.
Troubleshooting real API errors
HTTP 429 is not a pollution reading
If the server returns 429 Too Many Requests, slow down and follow the documented rate limits; do not interpret 429 as a PM2.5 concentration. A 400 request error may mean the date format or other parameter is invalid. A 404 may mean the requested data are unavailable. JSON may also return an empty items array or a documented error field even when the connection succeeded. Check the structure before indexing into it.
Never replace missing data with zero
A student who fills missing readings with zero without explanation may bias a graph downward and make the air appear cleaner than was observed. Show ‘missing’ and explain the cause when known. For dates and time zones, keep the original timestamps and standardise carefully before drawing comparisons. If a public safety decision is involved, do not rely on a broken educational script: direct readers to the actual NEA official public page.
Three practical Punggol questions for a coding lesson
Question one: what does the endpoint return?
Ask the learner to fetch the latest PM2.5 item and list its keys, timestamp, updatedTimestamp and each reporting region. They should be able to explain why the record is not a street-level sensor at Punggol Waterway Point. For a worked example they can choose the ‘north’ dictionary entry but must label it North region, not exact Punggol concentration. A source-aware table is more useful than a decorative map with fabricated pin precision.
Questions two and three: what is missing and what is not predictable?
Ask whether the /psi response includes a current reported 24-hour PSI sub-index and whether that is tomorrow’s official forecast. The correct answer is that the API provides current PSI readings; the next-day forecast must be obtained separately from the authoritative forecast source. Then ask what should appear on a dashboard if no latest reading is available. The correct answer is a conspicuous unavailable-data state, not a green Normal badge or a randomly guessed replacement. These habits teach reliability beyond Python.
A seven-step educational project plan
Build small and label everything
Step one: document the NEA dataset and v2 endpoint. Step two: make one request with a timeout. Step three: inspect the JSON data.items and timestamps. Step four: select and label the actual reporting region. Step five: graph a small historical series when complete. Step six: annotate missing observations and the difference from forecast. Step seven: publish a student explanation with the required government data attribution and a link to official NEA advice.
Finish with a quality checklist
Confirm that no live numeric value is hardcoded; that units match the pollutant; that dates use Singapore time; that no consumer app AQI is passed off as NEA PSI; that requested pages are retrieved fully where needed; and that errors never become zero. Check that the page says clearly it is an educational dashboard, not official emergency information. A small accurate chart gives readers more value than a complicated AI warning system built on misunderstood fields.
What to label in a haze data dashboard
| Field | Meaning | Correct display label |
|---|---|---|
| pm25_one_hourly | One-hour PM2.5 concentration | Region, µg/m³, observation time |
| psi_twenty_four_hourly | Current 24-hour PSI index | Region, PSI, observation time |
| regionMetadata | Reporting region labels and map metadata | Region reference, not a doorstep monitor |
| timestamp | Observation time | Formatted in Singapore time |
| updatedTimestamp | Data publication/update time | Last update, different from observation |
| paginationToken | Additional data pages available | Implementation detail, not a pollutant |
Frequently asked questions about Singapore’s haze APIs
What is the official Singapore PM2.5 API URL?
The NEA dataset’s public v2 real-time endpoint is https://api-open.data.gov.sg/v2/real-time/api/pm25. A normal GET without a date requests the latest reading. The dataset documentation explains the response structure, region metadata, optional date filter and rate limits.
Does the PM2.5 API give exact Punggol measurements?
No. It provides readings for five large Singapore reporting regions. A reading for ‘north’ is not a certified concentration inside a home at Punggol Waterway Point or a particular bus stop. Use official regional labels without inventing hyperlocal sensor locations.
Is data.gov.sg PSI the same as the forecast for tomorrow?
No. The /psi endpoint contains current PSI and sub-index readings including psi_twenty_four_hourly. The government next-day 24-hour PSI forecast is a separate public planning product available through NEA’s official haze information. Don’t label current API data as a forecast.
Can I fetch a fixed date?
Yes. The PM2.5 documentation accepts a YYYY-MM-DD date parameter or a datetime in the documented format for historical queries. Handle pagination where a date returns more records than a single response, and identify missing observations rather than filling them with zero.
Do I need an API key?
Basic public calls can work without one, while an optional x-api-key header is documented for higher rate limits and support. Do not paste secret keys in a WordPress article or public code. If rate limited, slow down and check the current developer documentation.
What does HTTP 429 mean?
It means too many requests under the current rate limit, not that pollution is 429. HTTP errors, empty data and stale timestamps require separate handling. Display an unavailable-data message rather than an invented safe air-quality number.
What unit is pm25_one_hourly?
The data.gov.sg PM2.5 dataset specifies micrograms per cubic metre (µg/m³). Those values are concentrations, not Singapore’s PSI index. Always label the reporting region and observation time as well as the unit.
Can I create a Punggol map of the regional data?
You can create an educational map using source-attributed public regional metadata, but avoid labelling a metadata point as the exact location of a Punggol sampling instrument or implying a street-level reading. A proper caption says which NEA reporting region is shown and explains coverage limitations.
Can my school project publish the data?
The Singapore Open Data Licence allows broad reuse subject to its terms, including source acknowledgement and a link to the licence, and prohibits implying official endorsement. Check the actual licence and classroom data policies before redistribution.
Can I use this API to tell parents when to send kids outdoors?
An educational script can display official public readings, but decisions require MOH’s actual health advice, individual medical needs, planned exertion and any school instructions. Don’t claim that your program overrides NEA’s dashboard or authorises a school closure. Link readers directly to official current information.
More Punggol science, Mathematics and haze learning
- Reading PM2.5 charts and official regional wind maps
- Haze prediction and AI model uncertainty
- PM2.5 concentration versus 24-hour PSI forecast
- Home PM2.5 sensors and limitations
- Primary Mathematics with Punggol haze data
- Secondary Geography case studies using haze
- G3 Mathematics statistics and modelling
- Punggol haze learning activities
- Verifying screenshot rumours and fake PSI alerts
- Complete Punggol Haze Guide 2026
- eduKateSG Things to Do in Punggol
Official API documentation and open data reuse rules
- data.gov.sg: NEA PM2.5 regional dataset and v2 API specification
- data.gov.sg: NEA Pollutant Standards Index dataset
- Direct public PM2.5 API endpoint
- Direct current PSI API endpoint
- data.gov.sg API catalogue and response example
- Singapore Open Data Licence version 1.0
- NEA official haze dashboard and next-day forecast
- MOH: 2026 health guidance
Fact-checked 18 October 2026 against the published v2 API specification and response structure. The scripts are educational examples; API schemas, rates and current measurements can change. The official NEA dashboard remains the authoritative public information source. The example code does not retrieve a Punggol-specific station, claim to forecast future PSI or provide individual health clearance. Include data.gov.sg attribution and licence terms for any reused live data.

