# havatahmin API

> Free static JSON API: 7-day temperature forecast (tmax / tmin / tmean with calibrated 90% intervals) for every Turkish province (il) and district (ilçe), and by elevation offset for ~54,000 villages and neighbourhoods; plus, on the same days, raw ECMWF HRES fields (wind, cloud, humidity, pressure, snow) and rain probability/amount, an hourly temperature curve (/v1/hourly/) and air quality at monitoring stations (/v1/aq/). Updated up to twice a day when the operator's machine runs (no fixed schedule; check `stale` in /v1/status.json); coverage per run: 81 of 81 provinces (76 with their own station model, 5 by transfer from the nearest station: bitlis, kahramanmaras, karabuk, sirnak, sivas, which have no /v1/verification file) and 911 of 973 districts answer in the latest run. No authentication for forecasts. Latest run: 2026-10-10T12.

Base URL: https://havatahmin.abso.net . UTF-8 JSON, CORS open. Slugs are lowercase ASCII (ankara, ankara/cankaya, sanliurfa/birecik): take `path` from /v1/places.json or a search entry; never guess from a Turkish spelling. All dates are Istanbul (UTC+3, no DST). `issued` and `valid` are calendar dates; to answer for a date D pick the day whose `valid` equals D; `lead` = `valid` - `issued` in days (lead 1 = the day after `issued`). `produced_at`, `status.time` and `next_run_expected` carry +03:00; `generated_at` (places.json, index.json) is UTC with offset. `cycle` (00Z/12Z) is the UTC hour of the ECMWF run.

Freshness: `stale` = today_istanbul > `issued` + 1 day, evaluated when the file was written; the file is static, so compare now with `stale_after` (issued + 2 days, 00:00 Istanbul): at or past it, treat the run as stale even if `stale` reads false. `produced_at` is when the calculator computed the run (data time, +03:00), usually hours after the ECMWF cycle named by `issued` + `cycle`; `age_hours` = hours since `produced_at` at export; `status.time` is when the files were rendered. There is no fixed schedule: a run is published when the operator's machine runs the update, usually twice a day (the 00Z run between ~10:30 and 11:00, the 12Z run between ~20:30 and 23:00 Istanbul). `next_run_expected` is the usual earliest time of the following run, an expectation, not a promise: in the past means not published yet, so rely on `stale` and `age_hours`. `hres_latest_run` is the newest ECMWF HRES input per cycle (YYYYMMDD), not a forecast. `missing.provinces`/`missing.districts` list the slugs that 404 in this run.

Rate limit: 300 requests per client IP per 5 minutes, keyed or not; above that a JSON 403 whose `error` starts with "rate limit exceeded" and carries `key_request_url` (CloudFront cannot emit 429). Cache responses, they change at most twice a day. A 404 `{"error","hint","docs"}` means an unknown slug or no forecast in the latest run (it comes before the key check; a wrong key is 403 everywhere, a missing key 401 on key endpoints). License: CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/); credit havatahmin.abso.net.

## Docs
- [Full reference (Markdown): recipes, time and freshness rules, live examples, field notes](https://havatahmin.abso.net/llms-full.txt): also served for `Accept: text/markdown` on https://havatahmin.abso.net/
- [OpenAPI 3.1 description with real examples](https://havatahmin.abso.net/openapi.json): import as a tool definition
- [index.json: endpoints, errors, limits, license, province slugs](https://havatahmin.abso.net/index.json)
- [Human documentation (Turkish) and how to get a free API key](https://havatahmin.abso.net/api/)

## Free endpoints
- [/v1/status.json](https://havatahmin.abso.net/v1/status.json): latest run, model version, freshness (stale, age_hours, next_run_expected, missing)
- [/index.json](https://havatahmin.abso.net/index.json): endpoint list, coverage, errors, limits, license and province slugs in one document
- [/v1/places.json](https://havatahmin.abso.net/v1/places.json): every province + district: name, slug, path, lat, lon, elev_m (~112 KB)
- [/v1/places.min.json](https://havatahmin.abso.net/v1/places.min.json): compact place index: [path, name, lat, lon] rows for provinces and districts (~45 KB)
- [/v1/forecast/{il}.json](https://havatahmin.abso.net/v1/forecast/adana.json): 7-day forecast for a province (point forecast at its main station), plus each anchoring station's own days
- [/v1/forecast/{il}/{ilce}.json](https://havatahmin.abso.net/v1/forecast/adana/aladag.json): 7-day forecast for a district
- [/v1/hourly/{il}.json](https://havatahmin.abso.net/v1/hourly/adana.json): hourly temperature curve for a province: 24 local hours for each of the 7 days (t = corrected, t_raw = ECMWF HRES)
- [/v1/hourly/{il}/{ilce}.json](https://havatahmin.abso.net/v1/hourly/adana/aladag.json): hourly temperature curve for a district
- [/v1/aq/{il}.json](https://havatahmin.abso.net/v1/aq/adana.json): air quality: daily mean PM2.5, PM10, NO2, O3, SO2, CO (µg/m³) for D+1..D+3 at the province's monitoring stations
- [/v1/aq/stations.json](https://havatahmin.abso.net/v1/aq/stations.json): every air-quality station with its coordinates, district and the province file that holds its forecast
- [/v1/districts/{il}.json](https://havatahmin.abso.net/v1/districts/adana.json): districts of a province, each with slug, path, display_name and has_forecast
- [/v1/search/{prefix}.json](https://havatahmin.abso.net/v1/search/yas.json): name search shard: provinces, districts and ~54,000 villages/neighbourhoods whose folded name starts with {prefix}
- [/v1/search/index.json](https://havatahmin.abso.net/v1/search/index.json): search rules, lapse rates and the list of existing shard prefixes

## Resolve any place name
- [/v1/search/{abc}.json](https://havatahmin.abso.net/v1/search/yas.json): fold the name (lowercase; ç→c ğ→g ı/İ→i ö→o ş→s ü→u, NFKD-strip â/î/û, keep a-z0-9), take the first 3 characters (2 if that is all), GET the shard, pick the entry whose `slug` matches, then GET /v1/forecast/{path}.json and add `offset` to value/p05/p95. Works for provinces, districts, villages and neighbourhoods; coordinates: nearest district row (path with a "/") in /v1/places.min.json

## API-key endpoints
- [/v1/alerts/{il}.json](https://havatahmin.abso.net/v1/alerts/adana.json): frost / heat / sudden-change flags for the week at the province's main station (API key)
- [/v1/alerts/{il}/{ilce}.json](https://havatahmin.abso.net/llms-full.txt): same flags for a district (API key)
- [/v1/verification/{il}.json](https://havatahmin.abso.net/v1/verification/adana.json): MAE vs raw HRES per lead over the last 30 days, against the station thermometer; 404 until a run is verified or for a province without a station (API key)
- [/v1/verification/all.json](https://havatahmin.abso.net/llms-full.txt): same, across all served places (API key)

## Optional
- [Landing page with search (Turkish)](https://havatahmin.abso.net/)
- [Pre-rendered pages per province and district: /il/{il}/ and /il/{il}/{ilce}/](https://havatahmin.abso.net/il/ankara/cankaya/): quotable summary sentence, table, Dataset JSON-LD
- [sitemap.xml: every province and district page](https://havatahmin.abso.net/sitemap.xml)
- [RFC 9727 API catalog](https://havatahmin.abso.net/.well-known/api-catalog): also /.well-known/ard.json (ARD 0.91) and /.well-known/ai-catalog.json
