pyopenmeteo 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. pyopenmeteo-0.1.0/LICENSE.md +7 -0
  2. pyopenmeteo-0.1.0/PKG-INFO +548 -0
  3. pyopenmeteo-0.1.0/README.md +505 -0
  4. pyopenmeteo-0.1.0/pyopenmeteo/__init__.py +78 -0
  5. pyopenmeteo-0.1.0/pyopenmeteo/api/__init__.py +26 -0
  6. pyopenmeteo-0.1.0/pyopenmeteo/api/air_quality.py +142 -0
  7. pyopenmeteo-0.1.0/pyopenmeteo/api/climate.py +125 -0
  8. pyopenmeteo-0.1.0/pyopenmeteo/api/ensemble.py +161 -0
  9. pyopenmeteo-0.1.0/pyopenmeteo/api/flood.py +123 -0
  10. pyopenmeteo-0.1.0/pyopenmeteo/api/forecast.py +220 -0
  11. pyopenmeteo-0.1.0/pyopenmeteo/api/geo.py +213 -0
  12. pyopenmeteo-0.1.0/pyopenmeteo/api/historical.py +153 -0
  13. pyopenmeteo-0.1.0/pyopenmeteo/api/marine.py +174 -0
  14. pyopenmeteo-0.1.0/pyopenmeteo/api/satellite.py +158 -0
  15. pyopenmeteo-0.1.0/pyopenmeteo/api/seasonal.py +165 -0
  16. pyopenmeteo-0.1.0/pyopenmeteo/core/__init__.py +27 -0
  17. pyopenmeteo-0.1.0/pyopenmeteo/core/base_api.py +105 -0
  18. pyopenmeteo-0.1.0/pyopenmeteo/core/exceptions.py +47 -0
  19. pyopenmeteo-0.1.0/pyopenmeteo/core/http_client.py +76 -0
  20. pyopenmeteo-0.1.0/pyopenmeteo/core/request_builder.py +33 -0
  21. pyopenmeteo-0.1.0/pyopenmeteo/core/response.py +250 -0
  22. pyopenmeteo-0.1.0/pyopenmeteo/formatters/__init__.py +8 -0
  23. pyopenmeteo-0.1.0/pyopenmeteo/formatters/csv_fmt.py +39 -0
  24. pyopenmeteo-0.1.0/pyopenmeteo/formatters/numpy_fmt.py +36 -0
  25. pyopenmeteo-0.1.0/pyopenmeteo/formatters/pandas_fmt.py +60 -0
  26. pyopenmeteo-0.1.0/pyopenmeteo/formatters/xarray_fmt.py +111 -0
  27. pyopenmeteo-0.1.0/pyopenmeteo/params/__init__.py +17 -0
  28. pyopenmeteo-0.1.0/pyopenmeteo/params/airquality_vars.py +55 -0
  29. pyopenmeteo-0.1.0/pyopenmeteo/params/archive_vars.py +147 -0
  30. pyopenmeteo-0.1.0/pyopenmeteo/params/climate_vars.py +31 -0
  31. pyopenmeteo-0.1.0/pyopenmeteo/params/ensemble_vars.py +222 -0
  32. pyopenmeteo-0.1.0/pyopenmeteo/params/flood_vars.py +19 -0
  33. pyopenmeteo-0.1.0/pyopenmeteo/params/forecast_vars.py +236 -0
  34. pyopenmeteo-0.1.0/pyopenmeteo/params/marine_vars.py +80 -0
  35. pyopenmeteo-0.1.0/pyopenmeteo/params/pressure_levels.py +98 -0
  36. pyopenmeteo-0.1.0/pyopenmeteo/params/satellite_vars.py +36 -0
  37. pyopenmeteo-0.1.0/pyopenmeteo/params/seasonal_vars.py +256 -0
  38. pyopenmeteo-0.1.0/pyopenmeteo/params/solar.py +57 -0
  39. pyopenmeteo-0.1.0/pyopenmeteo/params/units.py +34 -0
  40. pyopenmeteo-0.1.0/pyopenmeteo.egg-info/PKG-INFO +548 -0
  41. pyopenmeteo-0.1.0/pyopenmeteo.egg-info/SOURCES.txt +53 -0
  42. pyopenmeteo-0.1.0/pyopenmeteo.egg-info/dependency_links.txt +1 -0
  43. pyopenmeteo-0.1.0/pyopenmeteo.egg-info/requires.txt +25 -0
  44. pyopenmeteo-0.1.0/pyopenmeteo.egg-info/top_level.txt +2 -0
  45. pyopenmeteo-0.1.0/pyproject.toml +52 -0
  46. pyopenmeteo-0.1.0/setup.cfg +4 -0
  47. pyopenmeteo-0.1.0/tests/test_exceptions.py +120 -0
  48. pyopenmeteo-0.1.0/tests/test_forecast.py +234 -0
  49. pyopenmeteo-0.1.0/tests/test_geo.py +178 -0
  50. pyopenmeteo-0.1.0/tests/test_historical.py +144 -0
  51. pyopenmeteo-0.1.0/tests/test_http_client.py +182 -0
  52. pyopenmeteo-0.1.0/tests/test_pressure_levels.py +85 -0
  53. pyopenmeteo-0.1.0/tests/test_request_builder.py +82 -0
  54. pyopenmeteo-0.1.0/tests/test_response.py +240 -0
  55. pyopenmeteo-0.1.0/tests/test_solar.py +90 -0
@@ -0,0 +1,7 @@
1
+ Copyright (c) 2026
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,548 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyopenmeteo
3
+ Version: 0.1.0
4
+ Summary: A Python wrapper for the Open-Meteo weather API family
5
+ Author: jania
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/jania/pyopenmeteo
8
+ Project-URL: Issues, https://github.com/jania/pyopenmeteo/issues
9
+ Keywords: weather,forecast,open-meteo,climate,meteorology
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
18
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
+ Requires-Python: >=3.11
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE.md
22
+ Requires-Dist: requests>=2.28
23
+ Provides-Extra: pandas
24
+ Requires-Dist: pandas>=1.5; extra == "pandas"
25
+ Requires-Dist: openpyxl>=3.0; extra == "pandas"
26
+ Provides-Extra: numpy
27
+ Requires-Dist: numpy>=1.24; extra == "numpy"
28
+ Provides-Extra: xarray
29
+ Requires-Dist: xarray>=2023.1; extra == "xarray"
30
+ Requires-Dist: numpy>=1.24; extra == "xarray"
31
+ Provides-Extra: all
32
+ Requires-Dist: pandas>=1.5; extra == "all"
33
+ Requires-Dist: openpyxl>=3.0; extra == "all"
34
+ Requires-Dist: numpy>=1.24; extra == "all"
35
+ Requires-Dist: xarray>=2023.1; extra == "all"
36
+ Provides-Extra: dev
37
+ Requires-Dist: pytest>=7; extra == "dev"
38
+ Requires-Dist: pandas>=1.5; extra == "dev"
39
+ Requires-Dist: numpy>=1.24; extra == "dev"
40
+ Requires-Dist: xarray>=2023.1; extra == "dev"
41
+ Requires-Dist: openpyxl>=3.0; extra == "dev"
42
+ Dynamic: license-file
43
+
44
+ # pyopenmeteo
45
+
46
+ A Python wrapper for the [Open-Meteo](https://open-meteo.com/) family of weather APIs.
47
+
48
+ Open-Meteo is one of the best free weather data sources available. It has global coverage, hourly resolution, 80+ years of historical data, ensemble forecasts, climate projections, air quality, marine data, and more -- all with no API key.
49
+
50
+ pyopenmeteo wraps the API so you don't have to hand-build query strings, deal with raw JSON, or look up parameter names every time. Pass a city name or coordinates, get back a pandas DataFrame.
51
+
52
+ ---
53
+
54
+ ## Contents
55
+
56
+ - [What's included](#whats-included)
57
+ - [Installation](#installation)
58
+ - [Quick start](#quick-start)
59
+ - [APIs included](#apis-included)
60
+ - [ForecastAPI](#forecastapi)
61
+ - [HistoricalAPI](#historicalapi)
62
+ - [MarineAPI](#marineapi)
63
+ - [AirQualityAPI](#airqualityapi)
64
+ - [ClimateAPI](#climateapi)
65
+ - [EnsembleAPI](#ensembleapi)
66
+ - [FloodAPI](#floodapi)
67
+ - [SeasonalAPI](#seasonalapi)
68
+ - [GeocodingAPI & ElevationAPI](#geocodingapi--elevationapi)
69
+ - [Working with responses](#working-with-responses)
70
+ - [Params](#params)
71
+ - [Units and options](#units-and-options)
72
+ - [Error handling](#error-handling)
73
+ - [Commercial API key](#commercial-api-key)
74
+ - [License](#license)
75
+
76
+ ### Full documentation
77
+
78
+ | Doc | Description |
79
+ |-----|-------------|
80
+ | [Quick start](docs/quickstart.md) | Minimal working examples for each API |
81
+ | [Forecast](docs/forecast.md) | ForecastAPI reference |
82
+ | [Historical](docs/historical.md) | HistoricalAPI reference |
83
+ | [Marine](docs/marine.md) | MarineAPI reference |
84
+ | [Air quality](docs/air_quality.md) | AirQualityAPI reference |
85
+ | [Climate](docs/climate.md) | ClimateAPI reference |
86
+ | [Ensemble](docs/ensemble.md) | EnsembleAPI reference |
87
+ | [Flood](docs/flood.md) | FloodAPI reference |
88
+ | [Seasonal](docs/seasonal.md) | SeasonalAPI reference |
89
+ | [Satellite](docs/satellite.md) | SatelliteRadiationAPI reference |
90
+ | [Geocoding & elevation](docs/geo.md) | GeocodingAPI and ElevationAPI reference |
91
+ | [Response](docs/response.md) | WeatherResponse methods and formatters |
92
+ | [Params](docs/params.md) | Variable enums and units |
93
+ | [Exceptions](docs/exceptions.md) | Exception hierarchy |
94
+
95
+ ---
96
+
97
+ ## What's included
98
+
99
+ | API | What it gives you | Horizon |
100
+ |-----|-------------------|---------|
101
+ | `ForecastAPI` | Hourly, 15-min, daily, current-conditions forecasts | 16 days |
102
+ | `HistoricalAPI` | Reanalysis data back to 1940 | any date range |
103
+ | `HistoricalForecastAPI` | Archived model runs | any date range |
104
+ | `MarineAPI` | Wave height, swell, currents | 16 days |
105
+ | `ClimateAPI` | CMIP6 climate projections | 1950–2100 |
106
+ | `AirQualityAPI` | Pollutants, pollen, AQI | 7 days |
107
+ | `EnsembleAPI` | Probabilistic ensemble runs | 35 days |
108
+ | `FloodAPI` | River discharge forecasts | 210 days |
109
+ | `SatelliteRadiationAPI` | Solar irradiance from satellites | historical + near-realtime |
110
+ | `SeasonalAPI` | Long-range seasonal forecasts | 274 days |
111
+ | `GeocodingAPI` | Fuzzy place-name → coordinates | - |
112
+ | `ElevationAPI` | Coordinates → elevation (Copernicus DEM) | - |
113
+
114
+ ---
115
+
116
+ ## Installation
117
+
118
+ ```bash
119
+ pip install pyopenmeteo
120
+ ```
121
+
122
+ Optional extras for data conversion:
123
+
124
+ ```bash
125
+ pip install pyopenmeteo[pandas] # to_pandas(), to_csv(), to_excel()
126
+ pip install pyopenmeteo[numpy] # to_numpy()
127
+ pip install pyopenmeteo[xarray] # formatters.to_dataset()
128
+ pip install pyopenmeteo[all] # everything above
129
+ ```
130
+
131
+ ---
132
+
133
+ ## Quick start
134
+
135
+ ```python
136
+ from pyopenmeteo.api.forecast import ForecastAPI
137
+ from pyopenmeteo.params.forecast_vars import HourlyForecastVar, DailyForecastVar
138
+
139
+ api = ForecastAPI()
140
+
141
+ # Pass a place name - geocoding is handled automatically
142
+ resp = api.get(
143
+ "Toronto",
144
+ hourly=[HourlyForecastVar.TEMPERATURE_2M, HourlyForecastVar.PRECIPITATION],
145
+ daily=[DailyForecastVar.TEMPERATURE_2M_MAX, DailyForecastVar.PRECIPITATION_SUM],
146
+ timezone="America/Toronto",
147
+ )
148
+
149
+ print(resp)
150
+ # WeatherResponse(loc=43.7001,-79.4163; hourly×168, daily×7)
151
+
152
+ df = resp.to_pandas("hourly")
153
+ print(df.head())
154
+ ```
155
+
156
+ Or pass coordinates directly:
157
+
158
+ ```python
159
+ resp = api.get(
160
+ (43.7001, -79.4163),
161
+ hourly=[HourlyForecastVar.WIND_SPEED_10M],
162
+ forecast_days=3,
163
+ )
164
+ ```
165
+
166
+ ---
167
+
168
+ ## APIs included
169
+
170
+ ### ForecastAPI
171
+
172
+ Up to 16 days of forecast data at any location.
173
+
174
+ ```python
175
+ from pyopenmeteo.api.forecast import ForecastAPI
176
+ from pyopenmeteo.params.forecast_vars import HourlyForecastVar, DailyForecastVar, CurrentForecastVar
177
+ from pyopenmeteo.params.units import TemperatureUnit, WindSpeedUnit
178
+
179
+ api = ForecastAPI()
180
+
181
+ resp = api.get(
182
+ "London",
183
+ hourly=[
184
+ HourlyForecastVar.TEMPERATURE_2M,
185
+ HourlyForecastVar.RELATIVE_HUMIDITY_2M,
186
+ HourlyForecastVar.WIND_SPEED_10M,
187
+ HourlyForecastVar.PRECIPITATION_PROBABILITY,
188
+ ],
189
+ daily=[
190
+ DailyForecastVar.TEMPERATURE_2M_MAX,
191
+ DailyForecastVar.TEMPERATURE_2M_MIN,
192
+ DailyForecastVar.PRECIPITATION_SUM,
193
+ DailyForecastVar.SUNRISE,
194
+ DailyForecastVar.SUNSET,
195
+ ],
196
+ current=[CurrentForecastVar.TEMPERATURE_2M],
197
+ temperature_unit=TemperatureUnit.FAHRENHEIT,
198
+ wind_speed_unit=WindSpeedUnit.MPH,
199
+ timezone="Europe/London",
200
+ forecast_days=7,
201
+ )
202
+ ```
203
+
204
+ **Pressure-level variables** (upper atmosphere data):
205
+
206
+ ```python
207
+ from pyopenmeteo.params.pressure_levels import PressureVar, pressure_level, pressure_levels_range
208
+
209
+ resp = api.get(
210
+ (43.65, -79.38),
211
+ hourly=[
212
+ pressure_level(PressureVar.TEMPERATURE, 850), # "temperature_850hPa"
213
+ pressure_level(PressureVar.WIND_SPEED, 500), # "wind_speed_500hPa"
214
+ *pressure_levels_range(PressureVar.GEOPOTENTIAL_HEIGHT, 500, 1000),
215
+ ],
216
+ )
217
+ ```
218
+
219
+ Valid pressure levels: 30, 50, 70, 100, 150, 200, 250, 300, 400, 500, 600, 700, 800, 850, 900, 925, 950, 975, 1000 hPa.
220
+
221
+ **Solar panel irradiance** (GTI variables require a panel orientation):
222
+
223
+ ```python
224
+ from pyopenmeteo.params.solar import PanelOrientation
225
+ from pyopenmeteo.params.forecast_vars import HourlyForecastVar
226
+
227
+ resp = api.get(
228
+ (43.65, -79.38),
229
+ hourly=[HourlyForecastVar.GLOBAL_TILTED_IRRADIANCE],
230
+ panel=PanelOrientation(tilt=35, azimuth=0), # south-facing, 35° tilt
231
+ )
232
+
233
+ # Single-axis tracker: pass float("nan") for the tracked axis
234
+ panel = PanelOrientation(tilt=35, azimuth=float("nan"))
235
+ ```
236
+
237
+ ---
238
+
239
+ ### HistoricalAPI
240
+
241
+ Reanalysis data from 1940 onward. Requires explicit date range and at least one variable group.
242
+
243
+ ```python
244
+ from pyopenmeteo.api.historical import HistoricalAPI
245
+ from pyopenmeteo.params.archive_vars import HourlyArchiveVars, DailyArchiveVars
246
+
247
+ api = HistoricalAPI()
248
+ resp = api.get(
249
+ "Berlin",
250
+ start_date="2020-01-01",
251
+ end_date="2020-12-31",
252
+ hourly=[HourlyArchiveVars.TEMPERATURE_2M, HourlyArchiveVars.PRECIPITATION],
253
+ daily=[DailyArchiveVars.TEMPERATURE_2M_MAX],
254
+ timezone="Europe/Berlin",
255
+ )
256
+ ```
257
+
258
+ ---
259
+
260
+ ### MarineAPI
261
+
262
+ Wave and swell forecasts up to 16 days.
263
+
264
+ ```python
265
+ from pyopenmeteo.api.marine import MarineAPI
266
+ from pyopenmeteo.params.marine_vars import HourlyMarineVars, DailyMarineVars
267
+
268
+ api = MarineAPI()
269
+ resp = api.get(
270
+ (51.5, -1.8), # English Channel
271
+ hourly=[HourlyMarineVars.WAVE_HEIGHT, HourlyMarineVars.WAVE_DIRECTION],
272
+ daily=[DailyMarineVars.WAVE_HEIGHT_MAX],
273
+ )
274
+ ```
275
+
276
+ ---
277
+
278
+ ### AirQualityAPI
279
+
280
+ Pollutant concentrations, pollen levels, and AQI up to 7 days ahead.
281
+
282
+ ```python
283
+ from pyopenmeteo.api.air_quality import AirQualityAPI
284
+ from pyopenmeteo.params.airquality_vars import HourlyAirQualityVars
285
+
286
+ api = AirQualityAPI()
287
+ resp = api.get(
288
+ "Beijing",
289
+ hourly=[
290
+ HourlyAirQualityVars.PM2_5,
291
+ HourlyAirQualityVars.PM10,
292
+ HourlyAirQualityVars.EUROPEAN_AQI,
293
+ ],
294
+ timezone="Asia/Shanghai",
295
+ )
296
+ ```
297
+
298
+ ---
299
+
300
+ ### ClimateAPI
301
+
302
+ CMIP6 climate projections at 10 km resolution from 1950 to 2100. Requires a date range and daily variables.
303
+
304
+ ```python
305
+ from pyopenmeteo.api.climate import ClimateAPI
306
+ from pyopenmeteo.params.climate_vars import DailyClimateVars, ClimateModels
307
+
308
+ api = ClimateAPI()
309
+ resp = api.get(
310
+ "New York",
311
+ start_date="2050-01-01",
312
+ end_date="2050-12-31",
313
+ daily=[DailyClimateVars.TEMPERATURE_2M_MAX, DailyClimateVars.PRECIPITATION_SUM],
314
+ models=[ClimateModels.MRI_AGCM3_2_S, ClimateModels.EC_EARTH3P_HR],
315
+ )
316
+ ```
317
+
318
+ ---
319
+
320
+ ### EnsembleAPI
321
+
322
+ Probabilistic forecasts from multiple ensemble members, up to 35 days.
323
+
324
+ ```python
325
+ from pyopenmeteo.api.ensemble import EnsembleAPI
326
+ from pyopenmeteo.params.ensemble_vars import HourlyEnsembleVars, EnsembleModels
327
+
328
+ api = EnsembleAPI()
329
+ resp = api.get(
330
+ "Tokyo",
331
+ hourly=[HourlyEnsembleVars.TEMPERATURE_2M, HourlyEnsembleVars.PRECIPITATION],
332
+ models=[EnsembleModels.ICON_EU_EPS, EnsembleModels.NCEP_GEFS_SEAMLESS],
333
+ )
334
+ ```
335
+
336
+ ---
337
+
338
+ ### FloodAPI
339
+
340
+ River discharge forecasts up to 210 days.
341
+
342
+ ```python
343
+ from pyopenmeteo.api.flood import FloodAPI
344
+ from pyopenmeteo.params.flood_vars import DailyFloodVars
345
+
346
+ api = FloodAPI()
347
+ resp = api.get(
348
+ (48.2, 16.4), # near Vienna on the Danube
349
+ daily=[DailyFloodVars.RIVER_DISCHARGE],
350
+ ensemble=True,
351
+ )
352
+ ```
353
+
354
+ ---
355
+
356
+ ### SeasonalAPI
357
+
358
+ Long-range seasonal forecasts at weekly or monthly resolution up to 274 days.
359
+
360
+ ```python
361
+ from pyopenmeteo.api.seasonal import SeasonalAPI
362
+ from pyopenmeteo.params.seasonal_vars import WeeklySeasonalVars
363
+
364
+ api = SeasonalAPI()
365
+ resp = api.get(
366
+ "Lagos",
367
+ weekly=[WeeklySeasonalVars.TEMPERATURE_2M_MEAN],
368
+ timezone="Africa/Lagos",
369
+ )
370
+ ```
371
+
372
+ ---
373
+
374
+ ### GeocodingAPI & ElevationAPI
375
+
376
+ ```python
377
+ from pyopenmeteo.api.geo import GeocodingAPI, ElevationAPI
378
+
379
+ geo = GeocodingAPI()
380
+
381
+ # Fuzzy search - returns the top result
382
+ loc = geo.search("paris")
383
+ print(loc.name, loc.latitude, loc.longitude, loc.country)
384
+ # Paris 48.8534 2.3488 France
385
+
386
+ # All results
387
+ results = geo.search_all("paris", count=5)
388
+
389
+ # Look up by GeoNames ID
390
+ loc = geo.get_by_id(6455259)
391
+
392
+ # Elevation lookup
393
+ elev = ElevationAPI()
394
+ altitude = elev.get(48.85, 2.35) # scalar → float
395
+ altitudes = elev.get([48.85, 51.5], [2.35, -0.12]) # lists → list[float]
396
+ ```
397
+
398
+ ---
399
+
400
+ ## Working with responses
401
+
402
+ Every API returns a `WeatherResponse` object.
403
+
404
+ ```python
405
+ resp = api.get(...)
406
+
407
+ # Inspect what's available
408
+ resp.has_hourly() # True / False
409
+ resp.has_daily()
410
+ resp.has_minutely_15()
411
+ resp.has_current()
412
+
413
+ resp.get_hourly() # ["temperature_2m", "precipitation", ...]
414
+ resp.get_daily()
415
+
416
+ # Metadata
417
+ resp.latitude
418
+ resp.longitude
419
+ resp.elevation
420
+ resp.timezone
421
+
422
+ # Raw JSON
423
+ resp.to_dict()
424
+
425
+ # pandas DataFrame (indexed by DatetimeIndex)
426
+ df = resp.to_pandas("hourly")
427
+ df = resp.to_pandas("daily")
428
+ df = resp.to_pandas("current")
429
+
430
+ # NumPy arrays
431
+ arrays = resp.to_numpy("hourly")
432
+ # {"time": array(['2024-01-01T00:00:00', ...], dtype='datetime64[s]'),
433
+ # "temperature_2m": array([...], dtype=float64)}
434
+
435
+ # Write to disk
436
+ resp.to_csv("weather.csv", section="hourly")
437
+ resp.to_excel("weather.xlsx", section="daily")
438
+ ```
439
+
440
+ ### Standalone formatters
441
+
442
+ The `formatters` module provides functions that work with any response:
443
+
444
+ ```python
445
+ from pyopenmeteo.formatters import to_dataframe, to_arrays, to_csv, to_dataset
446
+
447
+ df = to_dataframe(resp, section="hourly")
448
+ arrays = to_arrays(resp, section="daily")
449
+ to_csv(resp, "out.csv", section="hourly")
450
+ ds = to_dataset(resp, section="hourly") # xarray Dataset
451
+ ```
452
+
453
+ ---
454
+
455
+ ## Params
456
+
457
+ Every variable you can request from an API is defined as a `StrEnum` in `pyopenmeteo.params`. Using these instead of raw strings gives you IDE autocomplete, catches typos at import time, and makes it easy to discover what's available without reading the Open-Meteo docs.
458
+
459
+ Each enum member's value is exactly the string the API expects, so you can always drop down to a raw string if you need a variable that isn't in the enum yet.
460
+
461
+ | Module | Classes |
462
+ |--------|---------|
463
+ | `params.forecast_vars` | `HourlyForecastVar`, `Minutely15ForecastVar`, `DailyForecastVar`, `CurrentForecastVar`, `ForecastModels` |
464
+ | `params.archive_vars` | `HourlyArchiveVars`, `DailyArchiveVars`, `ArchiveModels` |
465
+ | `params.marine_vars` | `HourlyMarineVars`, `Minutely15MarineVars`, `DailyMarineVars`, `CurrentMarineVars`, `MarineModels` |
466
+ | `params.airquality_vars` | `HourlyAirQualityVars`, `CurrentAirQualityVars` |
467
+ | `params.climate_vars` | `DailyClimateVars`, `ClimateModels` |
468
+ | `params.ensemble_vars` | `HourlyEnsembleVars`, `DailyEnsembleVars`, `EnsembleModels` |
469
+ | `params.flood_vars` | `DailyFloodVars`, `FloodModels` |
470
+ | `params.satellite_vars` | `HourlySatelliteVars`, `DailySatelliteVars`, `SatelliteModels` |
471
+ | `params.seasonal_vars` | `HourlyVars`, `DailySeasonalVars`, `WeeklySeasonalVars`, `MonthlySeasonalVars`, `SeasonalModels` |
472
+ | `params.pressure_levels` | `PressureVar`, `pressure_level()`, `pressure_levels_range()` |
473
+ | `params.solar` | `PanelOrientation` |
474
+ | `params.units` | `TemperatureUnit`, `WindSpeedUnit`, `PrecipitationUnit`, `TimeFormat`, `CellSelection`, `LengthUnit` |
475
+
476
+ ```python
477
+ from pyopenmeteo.params.forecast_vars import HourlyForecastVar
478
+
479
+ # Autocomplete shows every available variable
480
+ HourlyForecastVar.TEMPERATURE_2M # "temperature_2m"
481
+ HourlyForecastVar.WIND_SPEED_10M # "wind_speed_10m"
482
+ HourlyForecastVar.PRECIPITATION # "precipitation"
483
+
484
+ # Raw strings work anywhere an enum is accepted
485
+ resp = api.get("Oslo", hourly=["temperature_2m", HourlyForecastVar.PRECIPITATION])
486
+ ```
487
+
488
+ ---
489
+
490
+ ## Units and options
491
+
492
+ ```python
493
+ from pyopenmeteo.params.units import (
494
+ TemperatureUnit, # CELSIUS, FAHRENHEIT
495
+ WindSpeedUnit, # KMH, MS, MPH, KN
496
+ PrecipitationUnit, # MM, INCH
497
+ TimeFormat, # ISO8601, UNIXTIME
498
+ CellSelection, # LAND, SEA, NEAREST
499
+ LengthUnit, # METRIC, IMPERIAL
500
+ )
501
+ ```
502
+
503
+ ---
504
+
505
+ ## Error handling
506
+
507
+ ```python
508
+ from pyopenmeteo.core.exceptions import (
509
+ MeteoPyError, # catch-all base class
510
+ APIError, # 4xx from the API (has .status_code, .reason)
511
+ RateLimitError, # 429 - subclass of APIError
512
+ ServerError, # 5xx - subclass of APIError
513
+ GeocodingError, # base for location errors
514
+ LocationNotFoundError, # place name returned zero results (has .query)
515
+ ValidationError, # bad parameters before any HTTP call
516
+ ConnectionError, # network unreachable
517
+ )
518
+
519
+ from pyopenmeteo.api.forecast import ForecastAPI
520
+
521
+ api = ForecastAPI()
522
+ try:
523
+ resp = api.get("Atlantis", hourly=["temperature_2m"])
524
+ except LocationNotFoundError as e:
525
+ print(f"Could not find: {e.query}")
526
+ except RateLimitError:
527
+ print("Hit the rate limit - add an API key or slow down")
528
+ except APIError as e:
529
+ print(f"API error {e.status_code}: {e.reason}")
530
+ except MeteoPyError as e:
531
+ print(f"Something went wrong: {e}")
532
+ ```
533
+
534
+ ---
535
+
536
+ ## Commercial API key
537
+
538
+ Open-Meteo offers a [commercial API](https://open-meteo.com/en/pricing) for higher rate limits. Pass the key to any API client:
539
+
540
+ ```python
541
+ api = ForecastAPI(apikey="your-key-here")
542
+ ```
543
+
544
+ ---
545
+
546
+ ## License
547
+
548
+ This project is licensed under the [MIT License](LICENSE).