# openmeteo-rs
Typed async Rust client for the [Open-Meteo](https://open-meteo.com/) API.
`openmeteo-rs` builds Open-Meteo requests with Rust enums instead of raw query
strings, validates common parameter mistakes before sending, and decodes
weather responses into nullable column-oriented time series.
## Quickstart
```toml
[dependencies]
openmeteo-rs = "1"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
```
```rust
use openmeteo_rs::{Client, HourlyVar, Result};
#[tokio::main]
async fn main() -> Result<()> {
let client = Client::new();
let response = client
.forecast(52.52, 13.41)
.hourly([HourlyVar::Temperature2m, HourlyVar::Precipitation])
.forecast_days(1)
.send()
.await?;
if let Some(hourly) = response.hourly {
for row in hourly.iter_rows().take(6) {
println!(
"{} temp={:?} precipitation={:?}",
row.time(),
row.temperature_2m(),
row.precipitation()
);
}
}
Ok(())
}
```
## Supported APIs
- Forecast, including multi-location forecast requests
- Historical archive
- Historical forecast
- Previous model runs
- Ensemble forecast
- Seasonal forecast
- Climate projections
- Marine forecast
- Air quality
- Satellite radiation
- Flood forecast
- Geocoding
- Elevation lookup
Most weather-like endpoints support typed variable selection, units, timezones,
cell selection, fixed or relative time windows, JSON decoding, nullable values,
and exact-token escape hatches. When Open-Meteo adds a token before the crate
exposes it as a typed variant, use the relevant `other(...)` constructor, for
example `WeatherModel::other("exact_model_token")`.
## Multi-Location Forecast
```rust
use openmeteo_rs::{Client, HourlyVar, Result};
# async fn example() -> Result<()> {
let client = Client::new();
let responses = client
.forecast_batch([(52.52, 13.41), (47.3769, 8.5417)])
.hourly([HourlyVar::Temperature2m])
.forecast_days(1)
.send()
.await?;
for response in responses {
println!("{},{}", response.latitude, response.longitude);
}
# Ok(())
# }
```
## Geocode Then Forecast
```rust
use openmeteo_rs::{Client, HourlyVar, Result};
# async fn example() -> Result<()> {
let client = Client::new();
let Some(location) = client.geocode("Zurich").count(1).send().await?.into_iter().next() else {
return Ok(());
};
let forecast = client
.forecast(location.latitude, location.longitude)
.hourly([HourlyVar::Temperature2m])
.forecast_days(1)
.send()
.await?;
println!("{:?}", forecast.hourly);
# Ok(())
# }
```
## Examples
Examples live under `examples/<endpoint>/` but keep stable Cargo target names.
```sh
cargo run --example forecast_basic
cargo run --example forecast_multi_location
cargo run --example geocode_then_forecast
cargo run --example archive_basic
cargo run --example historical_forecast_basic
cargo run --example previous_runs_basic
cargo run --example ensemble_basic
cargo run --example seasonal_basic
cargo run --example climate_basic
cargo run --example marine_basic
cargo run --example air_quality_basic
cargo run --example satellite_radiation_basic
cargo run --example flood_basic
cargo run --example elevation
```
## Limitations
These capabilities are intentionally not part of the first stable JSON release:
- FlatBuffers responses
- Polars or Arrow exports
- blocking client API
- batch builders for every endpoint family
- reverse geocoding, which Open-Meteo does not currently provide
- CSV/XLSX decoding; request JSON and export from your application instead
## Attribution
Open-Meteo data is licensed under CC BY 4.0. Applications using the API should
include attribution such as `Weather data by Open-Meteo.com`.
This crate is MIT licensed.