# Daily city weather forecasts > A daily snapshot of the Open-Meteo weather forecast for a fixed panel of the world’s cities — every national or territorial capital plus every city of 200,000 people or more, about 3,200 in all: temperature, precipitation, wind, UV and sunrise/sunset for every day of the forecast horizon, recorded as of the day it was published. Because each run keeps what was forecast on that day, the series can be used to study how forecasts change as the target day approaches. - Dataset id: `open-meteo-daily` - Update cadence: daily - Upstream source: https://open-meteo.com/en/docs - Tags: weather, forecast, climate, cities, open-meteo, daily - Current version: ingest `01M62KXD7RF4GPRWBYRNAQHMM1`, snapshot 2026-10-29, generated 2026-10-29T01:31:26.488Z ## Licence and attribution - Licence: Creative Commons Attribution 4.0 International (`CC-BY-4.0`) - Licence text: https://open-meteo.com/en/licence - Required attribution: Weather data by Open-Meteo.com - Notes: Open-Meteo publishes its API data under CC-BY-4.0 and asks that a link to https://open-meteo.com/ appear next to any display of the data; its source code is AGPL-3.0, which does not apply here. The free tier is non-commercial and rate limited to 600 calls per minute, 5,000 per hour, 10,000 per day and 300,000 per month, where a call is weighted by the data requested — 14 variables means 1.4 calls per city, so one daily run of this panel costs about 4,450 of the 10,000, and the panel is deliberately sized to fit that. Open-Meteo aggregates national weather services (DWD, ECMWF, NOAA, Met Norway, MeteoSwiss, KNMI, DMI, BOM, CMA, JMA and others); most are CC-BY or CC-BY-SA and some, notably NOAA and the JMA, publish under their own terms, so a reuser must honour those upstream attributions as well. The city panel itself (names, countries and coordinates) is derived from the GeoNames gazetteer (https://www.geonames.org/, CC-BY-4.0), which requires its own attribution. What we publish is a derived work: the daily aggregations listed in api.ts, for a fixed panel of city coordinates, reshaped into rows. These are model forecasts, not observations, and must not be presented as measurements. Reproduce the attribution line verbatim anywhere you republish this data. ## Manifest - Start here for how this directory works: https://date-deschise.bunnytech.app/llms.txt - This dataset: https://date-deschise-s3.bunnytech.app/public/open-meteo-daily/manifest.json - All datasets: https://date-deschise-s3.bunnytech.app/public/index.json - Human page: https://date-deschise.bunnytech.app/datasets/open-meteo-daily The manifest is the contract: file URLs, row counts, column types, min/max statistics and example queries all live there. Re-read it to discover a new version. ## Fastest path — Parquet exports Immutable, physically sorted snapshot exports. No catalog fetch, best row-group pruning, safe to cache forever. Register one view per table: ```sql INSTALL httpfs; LOAD httpfs; CREATE OR REPLACE VIEW daily_weather AS SELECT * FROM read_parquet('https://date-deschise-s3.bunnytech.app/public/open-meteo-daily/v/01M62KXD7RF4GPRWBYRNAQHMM1/daily_weather.parquet'); CREATE OR REPLACE VIEW cities AS SELECT * FROM read_parquet('https://date-deschise-s3.bunnytech.app/public/open-meteo-daily/v/01M62KXD7RF4GPRWBYRNAQHMM1/cities.parquet'); ``` Example queries: -- Today’s forecast for every city: Joins the fact table to the dimension. The snapshot_date range is what prunes: it is the leading sort column. ```sql ATTACH IF NOT EXISTS 'ducklake:https://date-deschise-s3.bunnytech.app/catalog/catalog.ducklake' AS "datadirectory" (READ_ONLY); SELECT c.name, w.forecast_date, w.temperature_min, w.temperature_max, w.precipitation_sum FROM datadirectory.open_meteo_daily.daily_weather w JOIN datadirectory.open_meteo_daily.cities c USING (city_id) WHERE w.snapshot_date = (SELECT max(snapshot_date) FROM datadirectory.open_meteo_daily.daily_weather) AND w.lead_days = 0 ORDER BY w.temperature_max DESC; ``` -- How the forecast for one day drifted as it got closer: Every run keeps its own view of the future, so a single forecast_date has one row per lead time. ```sql ATTACH IF NOT EXISTS 'ducklake:https://date-deschise-s3.bunnytech.app/catalog/catalog.ducklake' AS "datadirectory" (READ_ONLY); SELECT w.snapshot_date, w.lead_days, w.temperature_max FROM datadirectory.open_meteo_daily.daily_weather w JOIN datadirectory.open_meteo_daily.cities c USING (city_id) WHERE c.name_lc = 'tokyo' AND w.forecast_date = DATE '2026-08-28' ORDER BY w.lead_days DESC; ``` ## Live lake — DuckLake catalog with time travel Attach the published catalog read-only over plain https. This works in native DuckDB AND in duckdb-wasm in the browser — the reader catalog resolves its data files through public https URLs, so no credentials and no backend are involved. ```sql INSTALL ducklake; LOAD ducklake; ATTACH 'ducklake:https://date-deschise-s3.bunnytech.app/catalog/open-meteo-daily/catalog.ducklake' AS open_meteo_daily (READ_ONLY); SELECT * FROM open_meteo_daily.main.daily_weather LIMIT 5; USE open_meteo_daily; ``` - Latest catalog: https://date-deschise-s3.bunnytech.app/catalog/open-meteo-daily/catalog.ducklake - This exact version: https://date-deschise-s3.bunnytech.app/catalog/open-meteo-daily/v/01M62KXD7RF4GPRWBYRNAQHMM1/catalog.ducklake (sha256 80586e6a275c3148939e33ef1d3ca841d63b6353c250546478cea92ab701b72d) - Written by DuckDB v1.5.5 - Time travel: this ingest is snapshot 26 — e.g. `SELECT * FROM open_meteo_daily.daily_weather AT (VERSION => 26)` ## Wholesale — S3 access For bulk downloads, read the bucket prefix directly over S3 with httpfs: ```sql INSTALL httpfs; LOAD httpfs; CREATE SECRET hetzner ( TYPE s3, ENDPOINT 'fsn1.your-objectstorage.com', URL_STYLE 'path', KEY_ID '', SECRET '' ); SELECT * FROM read_parquet('s3://date-deschise-s3/public/open-meteo-daily/v/01M62KXD7RF4GPRWBYRNAQHMM1/**/*.parquet'); ``` ## Schema ### daily_weather One row per city per forecast day, as of the run day. A full daily snapshot of the forecast horizon: re-running a day rewrites exactly that day for exactly those cities, so the history of what was forecast when is preserved and comparable. - 267.2K rows · 3.8 MB · write mode `snapshot_history` - Physically sorted by: `snapshot_date`, `city_id`, `forecast_date` - Primary key: `snapshot_date`, `city_id`, `forecast_date` - Files: 1 - https://date-deschise-s3.bunnytech.app/public/open-meteo-daily/v/01M62KXD7RF4GPRWBYRNAQHMM1/daily_weather.parquet (267.2K rows) Columns: - `snapshot_date` DATE NOT NULL — The run day (UTC) this forecast was captured on. (range: "2026-08-22" … "2026-10-29") (~20 distinct) - `city_id` INTEGER NOT NULL — Our stable id for the city; joins to cities.city_id. (range: 1 … 3176) (~3219 distinct) - `forecast_date` DATE NOT NULL — The day (UTC) this row describes. (range: "2026-08-22" … "2026-11-04") (~39 distinct) - `lead_days` INTEGER NOT NULL [days] — forecast_date minus snapshot_date, in whole days. 0 is today, 1 is tomorrow. Lets you compare a forecast against the same day observed later. - `weather_code` INTEGER — WMO 4677 weather code for the day (0 clear … 95+ thunderstorm). - `temperature_max` DOUBLE [°C] — Highest 2 m air temperature of the day. - `temperature_min` DOUBLE [°C] — Lowest 2 m air temperature of the day. - `temperature_mean` DOUBLE [°C] — Mean 2 m air temperature of the day. - `precipitation_sum` DOUBLE [mm] — Total precipitation (rain + showers + snow water equivalent). - `rain_sum` DOUBLE [mm] — Rain only, excluding snow. - `snowfall_sum` DOUBLE [cm] — Snowfall, as fresh snow depth rather than water equivalent. - `precipitation_hours` DOUBLE [h] — Hours of the day with measurable precipitation. - `wind_speed_max` DOUBLE [km/h] — Highest 10 m wind speed of the day. - `wind_gusts_max` DOUBLE [km/h] — Highest 10 m wind gust of the day. - `wind_direction_dominant` INTEGER [°] — Dominant 10 m wind direction, meteorological convention (0 = from the north, 90 = from the east). Rounded to whole degrees. - `uv_index_max` DOUBLE — Highest UV index of the day. - `sunrise` TIMESTAMPTZ — Sunrise instant. The request pins timezone=UTC, so the naive local time the API returns is UTC and is stored as such. - `sunset` TIMESTAMPTZ — Sunset instant, on the same UTC basis as sunrise. - `ingested_at` TIMESTAMP NOT NULL — When this row was produced by the ingest run (UTC). ### cities The fixed panel of cities the forecast is collected for — every national or territorial capital plus every city of 200,000 people or more: our own stable id, the requested city-centre coordinates, and the model grid point Open-Meteo actually answered from. Reference data with no history — every run replaces it wholesale. - 3,176 rows · 117 KB · write mode `replace` - Physically sorted by: `city_id` - Primary key: `city_id` - Text search columns: `name` - Files: 1 - https://date-deschise-s3.bunnytech.app/public/open-meteo-daily/v/01M62KXD7RF4GPRWBYRNAQHMM1/cities.parquet (3,176 rows) Columns: - `city_id` INTEGER NOT NULL — Stable id of the city. Ours, not Open-Meteo’s; never reused. (range: 1 … 3176) (~3219 distinct) - `name` VARCHAR NOT NULL — City name as displayed. - `name_lc` VARCHAR NOT NULL — name, lowercased and accent-folded. The prefix-search column — “sao paulo” must find “São Paulo”. - `country` VARCHAR NOT NULL — Country name as displayed. - `country_code` VARCHAR NOT NULL — ISO 3166-1 alpha-2 country code. - `latitude` DOUBLE NOT NULL [°] — Requested city-centre latitude (WGS84). - `longitude` DOUBLE NOT NULL [°] — Requested city-centre longitude (WGS84). - `grid_latitude` DOUBLE [°] — Latitude of the model grid cell Open-Meteo actually answered from. NULL when the last run could not read it. - `grid_longitude` DOUBLE [°] — Longitude of the model grid cell that answered. - `elevation` DOUBLE [m] — Elevation of the grid cell above sea level. - `timezone` VARCHAR — Timezone the API reported for the answer. Always UTC/GMT here. ## Performance notes - There are no indexes in a lakehouse. The physical sort order plus Parquet row-group min/max statistics ARE the index. Predicates on the leading sort columns prune whole row groups; everything else is a full column scan. - `daily_weather` prunes on `snapshot_date` → `city_id` → `forecast_date`. - `cities` prunes on `city_id`. - `ILIKE '%needle%'` cannot prune: it decompresses that whole column. Prefer prefix predicates on the lowercased search companion columns where they exist. - Every file URL under `/v//` is immutable. Cache it as aggressively as you like. - Files are never rewritten in place — a new ingest writes a new version path. Re-read the manifest to pick up updates rather than polling the file URLs. - Pull only the columns you need. Parquet is columnar and the reader range-GETs what it reads. ## Do not - Do not scrape https://date-deschise.bunnytech.app HTML. Every number on those pages comes from the manifest above. - Do not hit the upstream source (https://open-meteo.com/en/docs) in bulk. This mirror exists so you do not have to. - Do not read DuckLake internal data files with a plain Parquet reader. They come with positional delete files, and ignoring those silently returns deleted rows. Use the ducklake extension, or use the published exports above. - Do not republish without the attribution line, and do not imply endorsement.