A Go client for the Open-Meteo weather API. Supports forecasts, historical data, and ensemble forecasts through the library's typed metric catalog.
Important
v0.2.x is a significant refactor of v0.1.x and not backwards compatible. Once stable, this is likely to be promoted to v1.0.0. Please see the migration guide at the bottom of this readme.
go get github.com/hectormalot/omgoRequires Go 1.21 or later.
package main
import (
"context"
"fmt"
"log"
"github.com/hectormalot/omgo"
)
func main() {
client := omgo.NewClient()
// Create a forecast request for Amsterdam
req, err := omgo.NewForecastRequest(52.3738, 4.8910)
if err != nil {
log.Fatal(err)
}
// Request temperature and precipitation
req.WithHourly(omgo.HourlyTemperature2m, omgo.HourlyPrecipitation).
WithDaily(omgo.DailyTemperature2mMax, omgo.DailyTemperature2mMin).
WithTimezone("Europe/Berlin")
weather, err := client.Forecast(context.Background(), req)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Current temperature: %.1f%s\n",
weather.Hourly.Temperature2m[0],
weather.HourlyUnits.Temperature2m)
}- Type-safe metrics: All weather metrics are typed constants with autocomplete support
- Builder pattern: Fluent API for building requests
- 15-minutely data: High-resolution data for supported regions
- Historical data: Access to historical weather archives
- Ensemble forecasts: Dynamic members from one Ensemble API model per request
- Units: Full control over temperature, wind speed, and precipitation units
req, _ := omgo.NewForecastRequest(40.7128, -74.0060) // New York
req.WithCurrent(
omgo.CurrentTemperature2m,
omgo.CurrentWeatherCode,
omgo.CurrentWindSpeed10m,
)
weather, _ := client.Forecast(context.Background(), req)
fmt.Printf("Temperature: %.1f°C\n", *weather.Current.Temperature2m)
fmt.Printf("Conditions: %s\n", weather.Current.WeatherCode.String())
fmt.Printf("Is daytime: %v\n", weather.Current.IsDaytime())req, _ := omgo.NewForecastRequest(52.52, 13.41)
req.WithHourly(
omgo.HourlyTemperature2m,
omgo.HourlyRelativeHumidity2m,
omgo.HourlyPrecipitation,
omgo.HourlyWeatherCode,
).WithForecastDays(7).
WithTimezone("Europe/Berlin")
weather, _ := client.Forecast(context.Background(), req)
for i, t := range weather.Hourly.Times {
fmt.Printf("%s: %.1f°C, %s\n",
t.Format("Mon 15:04"),
weather.Hourly.Temperature2m[i],
weather.Hourly.WeatherCode[i].String())
}req, _ := omgo.NewForecastRequest(52.52, 13.41)
req.WithDaily(
omgo.DailyTemperature2mMax,
omgo.DailyTemperature2mMin,
omgo.DailySunrise,
omgo.DailySunset,
omgo.DailyPrecipitationSum,
).WithTimezone("Europe/Berlin")
weather, _ := client.Forecast(context.Background(), req)
for i, t := range weather.Daily.Times {
fmt.Printf("%s: %.0f°C to %.0f°C, Sunrise: %s\n",
t.Format("Mon Jan 2"),
weather.Daily.Temperature2mMin[i],
weather.Daily.Temperature2mMax[i],
weather.Daily.Sunrise[i].Format("15:04"))
}req, _ := omgo.NewForecastRequest(52.52, 13.41)
req.WithMinutely15(
omgo.Minutely15Temperature2m,
omgo.Minutely15Precipitation,
)
weather, _ := client.Forecast(context.Background(), req)
// High-resolution data for the next hours
for i, t := range weather.Minutely15.Times {
fmt.Printf("%s: %.1f°C\n", t.Format("15:04"), weather.Minutely15.Temperature2m[i])
}req, err := omgo.NewForecastRequest(52.52, 13.41)
if err != nil {
// handle error
}
req.WithModels("ecmwf_ifs", "gfs_global").
WithHourly(omgo.HourlyTemperature2m).
WithDaily(omgo.DailyTemperature2mMax)
weather, err := client.Forecast(context.Background(), req)
if err != nil {
// handle error
}
ecmwf, hasECMWF := weather.HourlyByModel["ecmwf_ifs"]
gfs, hasGFS := weather.HourlyByModel["gfs_global"]
if hasECMWF && hasGFS {
fmt.Println(ecmwf.Temperature2m[0], gfs.Temperature2m[0])
}
// Existing fields select the first requested model when it can be identified.
fmt.Println(weather.PrimaryModel) // ecmwf_ifsUse the *ByModel maps when comparing deterministic model forecasts. The
existing Hourly, Minutely15, and Daily fields remain compatibility views
of the first explicitly requested model whenever that model has data for the
cadence. If a model is outside its coverage area, Open-Meteo may collapse the
response to unsuffixed fields. The *ByModel maps are then nil,
PrimaryModel is empty, and the compatibility fields contain the surviving
model's unattributed data.
Open-Meteo uses nulls for unavailable variables and shorter model horizons.
The library's existing non-nullable typed slices decode those values as zero
(and null timestamps as zero time.Time values), rather than truncating the
series. Unit fields describe PrimaryModel only and may be "undefined" for
unsupported model-variable combinations.
Deterministic multi-model forecasts above use WithModels and *ByModel.
Ensembles are distinct: construct one EnsembleRequest with exactly one
ensemble model and read its dynamically discovered *ByMember maps.
req, err := omgo.NewEnsembleRequest(52.52, 13.41, "icon_seamless")
if err != nil {
// handle error
}
req.WithHourly(omgo.HourlyTemperature2m).
WithDaily(omgo.DailyTemperature2mMax).
WithTimezone("Europe/Berlin")
weather, err := client.Ensemble(context.Background(), req)
if err != nil {
// handle error
}
member0 := weather.HourlyByMember["member00"]
member1 := weather.HourlyByMember["member01"]
fmt.Println(member0.Temperature2m[0], member1.Temperature2m[0])member00 represents unsuffixed member-valued series; member counts vary by
model, and hourly and daily members are discovered independently. Calculated
fields such as is_day, sunrise, sunset, and daylight duration are shared with
each normalized member. This first Ensemble API surface accepts one model per
request. As with other omgo responses, unavailable values returned as null are
decoded as zero values by the existing non-nullable typed slices; a field whose
entire JSON value is null remains a nil slice.
req, _ := omgo.NewHistoricalRequest(52.52, 13.41, "2023-06-01", "2023-06-30")
req.WithHourly(omgo.HourlyTemperature2m, omgo.HourlyPrecipitation).
WithDaily(omgo.DailyTemperature2mMax).
WithTimezone("Europe/Berlin")
weather, _ := client.Historical(context.Background(), req)req, _ := omgo.NewForecastRequest(40.7128, -74.0060) // New York
req.WithHourly(omgo.HourlyTemperature2m, omgo.HourlyWindSpeed10m, omgo.HourlyPrecipitation).
WithTemperatureUnit(omgo.Fahrenheit).
WithWindSpeedUnit(omgo.MilesPerHour).
WithPrecipitationUnit(omgo.Inches)
weather, _ := client.Forecast(context.Background(), req)
fmt.Printf("Temperature: %.1f%s\n",
weather.Hourly.Temperature2m[0],
weather.HourlyUnits.Temperature2m) // "°F"client := omgo.NewClient(
omgo.WithForecastURL("https://customer-api.open-meteo.com/v1/forecast"),
omgo.WithAPIKey("your-api-key"),
)For commercial Ensemble API access, configure its endpoint explicitly too:
client := omgo.NewClient(
omgo.WithEnsembleURL("https://customer-ensemble-api.open-meteo.com/v1/ensemble"),
omgo.WithAPIKey("your-api-key"),
)- Temperature, apparent temperature, humidity
- Precipitation, rain, showers, snowfall
- Weather code, cloud cover
- Pressure (MSL and surface)
- Wind speed, direction, gusts
- Is day/night
- Basic: temperature, humidity, dew point, apparent temperature
- Precipitation: rain, snow, showers, probability
- Cloud cover: total, low, mid, high
- Wind: speed and direction at multiple heights (10m, 80m, 120m, 180m)
- Solar radiation: shortwave, direct, diffuse, global tilted
- Soil: temperature and moisture at multiple depths
- Pressure levels: temperature, humidity, wind, cloud cover at 19 pressure levels
- Temperature: max, min, mean
- Apparent temperature: max, min, mean
- Precipitation: sum, hours, probability
- Sun: sunrise, sunset, sunshine duration, daylight duration
- Wind: max speed, max gusts, dominant direction
- UV index
- Temperature, humidity, apparent temperature
- Precipitation, rain, snow
- Solar radiation
- Wind speed and direction
- Lightning potential (regional)
The WeatherCode type includes all WMO weather interpretation codes:
code := omgo.RainModerate // 63
fmt.Println(code.String()) // "Moderate rain"
fmt.Println(code.Description()) // "Moderate rain"weather, err := client.Forecast(context.Background(), req)
if err != nil {
var apiErr *omgo.APIError
if errors.As(err, &apiErr) {
fmt.Printf("API error %d: %s\n", apiErr.StatusCode, apiErr.Reason)
}
}Version 0.2.0 is a complete rewrite with breaking changes:
c, _ := omgo.NewClient()
loc, _ := omgo.NewLocation(52.52, 13.41)
opts := omgo.Options{
HourlyMetrics: []string{"temperature_2m", "precipitation"},
}
res, _ := c.Forecast(ctx, loc, &opts)
temps := res.HourlyMetrics["temperature_2m"]c := omgo.NewClient()
req, _ := omgo.NewForecastRequest(52.52, 13.41)
req.WithHourly(omgo.HourlyTemperature2m, omgo.HourlyPrecipitation)
weather, _ := c.Forecast(ctx, req)
temps := weather.Hourly.Temperature2m
unit := weather.HourlyUnits.Temperature2m // "°C"NewClient()no longer returns an errorOptionsstruct replaced by builder pattern (NewForecastRequest,NewHistoricalRequest)Forecastresponse type renamed toWeather- Metrics are now typed constants instead of strings
- Response data accessed via typed struct fields instead of maps
- Units available via parallel
*Unitsstructs
MIT License.
This library uses the free Open-Meteo API. Consider supporting their work if you use it commercially.