Government — entry 073 of 90
Open Government, Singapore
Singapore's open-data portal was rebuilt on a distinct AWS API Gateway service (api-open.data.gov.sg) hosting 4,500+ datasets from 70+ agencies, covering weather, transport, health, and housing data. Calls work anonymously out of the box with JSON responses and CORS open to any origin — a live probe confirmed a wildcard header — and a free registered API key only raises the rate-limit tier rather than unlocking access. The old /developer path from the previous site redesign now redirects to a separate GitBook-hosted developer guide.
data.gov.sg's real-time environment APIs run on a distinct AWS API Gateway host, api-open.data.gov.sg, separate from the portal's dataset-download service. A live keyless GET this run against /real-time/api/pm25 returned a genuine 623 B air-quality reading at HTTP 200 with access-control-allow-origin: * and no key of any kind.
GreatAPIs Score
Auth quickstart
- No API key required for the real-time environment endpoints — a live GET this run against
/real-time/api/pm25returned HTTP 200 on the first anonymous try, with noAuthorizationheader sent.
Your key is stored only in this browser (localStorage) and sent directly to the API — never to greatapis.
Get the current PM2.5 reading by region
GEThttps://api-open.data.gov.sg/v2/real-time/api/pm25
{"code":0,"data":{"regionMetadata":[{"name":"east","labelLocation":{"longitude":103.94,"latitude":1.35735}},{"name":"west","labelLocation":{"longitude":103.7,"latitude":1.35735}},{"name":"south","labelLocation":{"latitude":1.29587,"longitude":103.82}},{"name":"north","labelLocation":{"latitude":1.41803,"longitude":103.82}},{"name":"central","labelLocation":{"longitude":103.82,"latitude":1.35735}}],"items":[{"date":"2026-08-02","updatedTimestamp":"2026-08-02T04:00:59+08:00","timestamp":"2026-08-02T04:00:00+08:00","readings":{"pm25_one_hourly":{"south":10,"east":17,"central":19,"north":12,"west":14}}}]},"errorMsg":""}A live GET this run against /real-time/api/air-temperature?date=2026-08-01 (a full calendar day) returned a 16,509 B body covering every 1-minute station reading for that entire day, versus 2,414 B for the same endpoint with no date param (just the latest snapshot) — date selects a whole day of history, not a single point in time.
Try it
Developer reference
https://api-open.data.gov.sg/v26-30 calls per 10 seconds depending on API-key tier (no key / Dev / Prod) and endpoint group; HTTP 429 when exceeded
- GET/real-time/api/air-temperature
- GET/real-time/api/pm25
- GET/real-time/api/two-hr-forecast
Gotchas & limits
- A wrong route on this gateway looks exactly like a missing credential — a live GET this run against
/real-time/api/carpark-availabilityand/real-time/api/taxi-availability(routes that don't exist on this API version) both returned HTTP 403 with body{"message":"Missing Authentication Token"}(42 B) and headerx-amzn-errortype: MissingAuthenticationTokenException, even though no key is required anywhere on this gateway — that 403 is AWS API Gateway's unmatched-route response, not a real auth wall. - The dataset-download API lives on a completely different host than the real-time API, despite both being under data.gov.sg — a live GET this run against
https://api-open.data.gov.sg/v2/public/api/datasetsreturned the same unmatched-route 403 above, while the identical path againsthttps://api-production.data.gov.sg/v2/public/api/datasets(the host data.gov.sg's own developer guide documents) returned a real 200 with adatasetsarray.baseUrlabove only covers the real-time cluster; the dataset API needs its own base. - An invalid
datevalue is a real HTTP 400 with a typed error envelope, not a silently-ignored parameter — a live GET this run against/real-time/api/air-temperature?date=not-a-datereturned exactly{"code":4,"name":"ERROR_PARAMS","data":null,"errorMsg":"Invalid date format. Date format must be YYYY-MM-DD (2024-06-01) or YYYY-MM-DDTHH:mm:ss (2024-06-01T08:30:00)."}(168 B). - Endpoints under the same
/real-time/api/namespace return genuinely different envelope shapes — a live comparison this run showed/real-time/api/two-hr-forecastnests results underdata.area_metadata+data.items[].forecasts[](per-town strings), while/real-time/api/twenty-four-hr-forecastnests underdata.records[].general+data.records[].periods[].regions(per-region objects withcode/text), and/real-time/api/pm25nests underdata.regionMetadata+data.items[].readings— one shared parser will not work across all three.