If you follow daily temperature contracts on a prediction market, or anything else that pays out on "the high in New York today", you have probably seen the official number disagree with your phone. Three things cause that.
1. It is one specific station
The National Weather Service publishes a Daily Climate Report (product code CLI) for each climate station. For New York that station is Central Park (KNYC), not JFK or LaGuardia. For Chicago, contracts usually name Midway (KMDW), not O'Hare. A different station a few miles away can easily read a couple of degrees differently. Check which station your contract's rules name.
2. The day is not the day on your clock
The climate day runs midnight to midnight local standard time, all year. During daylight saving time that means 1 AM to 1 AM on the clock. A warm reading at 12:30 AM in July belongs to the previous climate day.
3. The final number comes the next morning
NWS offices issue a preliminary CLI during the day and the final one early the next morning. Until the final report is out, any number you see is provisional. The running max of the station's reported observations is a good guide, but it can differ from the official value by a degree or so, because the official value comes from the station's continuous record.
Getting the official value as JSON
The NWS Settlement Weather Actor on Apify reads the CLI products and observations from the NWS API and returns one record per station per day, with a settled flag.
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("studded_lantana/settlement-weather").call(run_input={
"locations": ["KNYC", "Chicago", "Austin"],
"startDate": "yesterday",
"endDate": "today",
})
for r in client.dataset(run.default_dataset_id).iterate_items():
if r["settled"]:
print(f"{r['date']} {r['city']}: official high {r['cli_high_f']}°F, low {r['cli_low_f']}°F")
else:
print(f"{r['date']} {r['city']}: {r['status']}; running high so far {r['obs_high_f']}°F")
Once the final report is out, yesterday's rows come back with settled: true and the final cli_high_f and cli_low_f. Run it too early in the morning and a station can still show preliminary. Today's rows come back unsettled, with status saying why and obs_high_f showing the running maximum.
Each record also carries cli_product_id, cli_issued_at and source_urls, so you can open the exact NWS report behind a value.
Limits worth knowing
- 21 stations in major US cities; other stations are refused.
- The last 7 days only, because that is how long the NWS API keeps these reports.
- Weather data only. No market prices or odds.
Cost
$0.005 per run plus $0.002 per station-day. The example above (3 stations × 2 days) costs $0.017.
All data is from US federal government sources and is in the public domain. The Actor is not affiliated with NOAA or the NWS.
Top comments (1)
Good breakdown of the station and the day boundary. There is a third ruler I hit doing exactly this app-vs-official comparison: the unit.
I pulled 10 airports, app "current" vs the latest airport report (METAR), same minute. Score wind raw and 1.852x of every "difference" is invented by km/h vs knots alone. Normalize it and 3 of 10 wind pairs flip verdict. The other channels have the same shape: the airport never reports relative humidity (you derive it from dewpoint, and 1C of rounding moves it ~6 points), cloud is a percent vs octas, and the two readings sat 3 to 69 minutes apart, so some of the gap is just weather moving between them.
Before you call the app wrong, pin the unit, the derivation and the clock. I wrote the comparison up with the chart here: dev.to/maxcarnage/wttrin-says-28-k...
n=10, one snapshot.