PHPackages                             tempi-marathon/open-meteo-php - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. tempi-marathon/open-meteo-php

ActiveLibrary

tempi-marathon/open-meteo-php
=============================

Saloon SDK for the Open-Meteo APIs

v2.0.1(1mo ago)0137MITPHPPHP ^8.3CI passing

Since Jul 11Pushed 1mo agoCompare

[ Source](https://github.com/tempi-marathon/open-meteo-php)[ Packagist](https://packagist.org/packages/tempi-marathon/open-meteo-php)[ RSS](/packages/tempi-marathon-open-meteo-php/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (4)Dependencies (20)Versions (6)Used By (0)

🌤 Open-Meteo PHP
================

[](#-open-meteo-php)

[![Tests](https://github.com/tempi-marathon/open-meteo-php/actions/workflows/test.yml/badge.svg)](https://github.com/tempi-marathon/open-meteo-php/actions/workflows/test.yml)[![PHP](https://camo.githubusercontent.com/79206c91a1c78f942da5eeaa90030dd6da410f85339450c4d1e3715f95d9a4a7/68747470733a2f2f696d672e736869656c64732e696f2f7374617469632f76313f6c6162656c3d504850266d6573736167653d253545382e3326636f6c6f723d373737424234266c6f676f3d706870266c6f676f436f6c6f723d7768697465)](https://www.php.net/)[![License: MIT](https://camo.githubusercontent.com/14e329775a2839aa1f4a8645f915713708e8fde40f9f5474f599cb08c5f09fee/68747470733a2f2f696d672e736869656c64732e696f2f7374617469632f76313f6c6162656c3d4c6963656e7365266d6573736167653d4d495426636f6c6f723d626c7565)](LICENSE)

Framework-agnostic [Saloon](https://docs.saloon.dev/) SDK for the [Open-Meteo](https://open-meteo.com/) APIs.

[Open-Meteo](https://open-meteo.com/) · [Open-Meteo GitHub](https://github.com/open-meteo/open-meteo)

**No API key required.** The free, non-commercial Open-Meteo API works out of the box — install the package and start making requests.

Requires PHP 8.3, 8.4, or 8.5.

Installation
------------

[](#installation)

```
composer require tempi-marathon/open-meteo-php
```

Quick start
-----------

[](#quick-start)

```
use TempiMarathon\OpenMeteo\Enums\HourlyVariable;
use TempiMarathon\OpenMeteo\OpenMeteo;

$forecast = (new OpenMeteo())
    ->forecast()->weather()->get(52.52, 13.41)
    ->hourly(HourlyVariable::Temperature2m)
    ->dto();
```

No environment variables, configuration files, or Laravel setup needed.

Usage
-----

[](#usage)

Chain request options on the fluent builder, then call `->dto()` for a typed response or `->send()` for the raw Saloon response.

### Facade (recommended entry point)

[](#facade-recommended-entry-point)

```
use TempiMarathon\OpenMeteo\Enums\HourlyVariable;
use TempiMarathon\OpenMeteo\Enums\Timezone;
use TempiMarathon\OpenMeteo\OpenMeteo;

$openMeteo = new OpenMeteo();

$locations = $openMeteo->geocoding()->locations()->search('Berlin')->dto();

$forecast = $openMeteo->forecast()->weather()->get(52.52, 13.41)
    ->timezone(Timezone::EuropeAmsterdam)
    ->hourly(HourlyVariable::Temperature2m, HourlyVariable::WeatherCode)
    ->forecastDays(7)
    ->dto();
```

### Connectors (direct Saloon access)

[](#connectors-direct-saloon-access)

```
use TempiMarathon\OpenMeteo\Connectors\ForecastConnector;
use TempiMarathon\OpenMeteo\Connectors\GeocodingConnector;
use TempiMarathon\OpenMeteo\Enums\Geocoding\GeocodingLanguage;
use TempiMarathon\OpenMeteo\Enums\HourlyVariable;
use TempiMarathon\OpenMeteo\Enums\Timezone;

$geocoding = new GeocodingConnector();

$locations = $geocoding->locations()->search('Berlin')
    ->count(5)
    ->language(GeocodingLanguage::English)
    ->dto();

$forecast = new ForecastConnector();

$data = $forecast->weather()->get(52.52, 13.41)
    ->timezone(Timezone::EuropeAmsterdam)
    ->hourly(HourlyVariable::Temperature2m, HourlyVariable::WeatherCode)
    ->forecastDays(7)
    ->dto();
```

Use `debugUrl()` on a resource to inspect the request URL during development. It redacts secrets such as API keys — see [SECURITY.md](SECURITY.md).

### Human-readable values

[](#human-readable-values)

Time-series data is exposed as typed `SeriesPoint` objects inside interval series — not raw arrays:

```
$point = $forecast->hourly()->at(0);
// or: foreach ($forecast->hourly() as $point) { ... }
// or: $forecast->hourly()->closestTo(new DateTimeImmutable('2026-07-11T12:00'));

$point?->get('temperature_2m');                // 21.2
$point?->get('weathercode')?->label();        // "Partly cloudy"
echo $point?->get('wind_direction_80m');       // "SW" — Stringable compass label
$point?->get('wind_direction_80m')?->getRaw();  // 225 — degrees when you need the number

$forecast->daily()->at(0)?->get('temperature_2m_max');
$forecast->minutely15()->at(0)?->get('temperature_2m');
$forecast->current()->first()?->get('wind_direction_10m')?->label(); // current snapshot
```

Each response type exposes only the intervals its API supports — for example `AirQualityResponse` implements `HasHourly` and `HasCurrent`, while `ForecastResponse` adds `HasDaily` and `HasMinutely15`. Use `instanceof HasDaily` when writing generic code.

Absolute direction fields (wind, wave, swell, ocean current, and similar) are parsed into `WindDirection` automatically. Anomaly fields such as `wind_direction_10m_anomaly` stay as numeric values.

Supported APIs
--------------

[](#supported-apis)

Facade methodAPIOpen-Meteo docs`forecast()`Weather forecast[Forecast API](https://open-meteo.com/en/docs)`historical()`Historical weather archive[Historical Weather API](https://open-meteo.com/en/docs/historical-weather-api)`geocoding()`Location search[Geocoding API](https://open-meteo.com/en/docs/geocoding-api)`airQuality()`Air quality[Air Quality API](https://open-meteo.com/en/docs/air-quality-api)`marine()`Marine weather[Marine Weather API](https://open-meteo.com/en/docs/marine-weather-api)`climate()`Climate[Climate API](https://open-meteo.com/en/docs/climate-api)`flood()`Flood[Flood API](https://open-meteo.com/en/docs/flood-api)`ensemble()`Ensemble forecast[Ensemble API](https://open-meteo.com/en/docs/ensemble-api)`seasonal()`Seasonal forecast[Seasonal Forecast API](https://open-meteo.com/en/docs/seasonal-forecast-api)`elevation()`Elevation[Elevation API](https://open-meteo.com/en/docs/elevation-api)Each connector exposes a resource before `get()` — for example `forecast()->weather()`, `historical()->archive()`, `elevation()->elevation()`.

Weather endpoints share builders like `timezone()`, `between()`, unit options, `models()`, and `withQueryParam()`. Which options apply depends on the endpoint. Elevation only accepts coordinates. Forecast-window options (`forecastDays()`, `pastDays()`, `forecastHours()`, `pastHours()`) are endpoint-specific too — `forecastHours()` and `pastHours()` work on forecast, historical, and ensemble only.

Climate and historical requests require `between($start, $end)` before sending. Seasonal supports `weekly()` in addition to hourly, daily, and monthly intervals. Air quality accepts `domains()`. Climate accepts `disableBiasCorrection()`. Ensemble accepts `temporalResolution()`. Forecast, historical, and ensemble support solar irradiance options via `tilt()` and `azimuth()`.

Each API uses endpoint-specific variable enums generated from the OpenAPI specs (for example `MarineHourlyVariable`, `HistoricalDailyVariable`, `EnsembleHourlyVariable`, `ForecastCurrentVariable`, `AirQualityCurrentVariable`). Weather model IDs are typed per endpoint (`ForecastModel`, `HistoricalModel`, etc.) and passed to `models()` where supported. Response metadata lives on `$response->metadata` (`elevation`, `generationTimeMs`, `utcOffsetSeconds`, `timezoneAbbreviation`).

Weather resources support `forPoints()` for batch lookups. Use `->dto()` for a single location; use `->dtoCollection()` for multi-location weather responses (for example `ForecastResponseCollection`). Elevation returns one `->dto()` with an array of values.

Optional configuration
----------------------

[](#optional-configuration)

### User-Agent

[](#user-agent)

Optional, but recommended in production so Open-Meteo can identify your application:

```
export OPENMETEO_USER_AGENT=my-app/1.0
```

Sent as the `User-Agent` header on every request.

### Commercial subscriptions

[](#commercial-subscriptions)

Open-Meteo API keys are only for [paid commercial subscriptions](https://open-meteo.com/en/pricing). The free API does not require a key.

When `OPENMETEO_API_KEY` is set, the SDK automatically:

1. Appends the key as an `apikey` query parameter on every request
2. Rewrites default free-tier `*.open-meteo.com` hosts to `customer-*.open-meteo.com`

```
# .env (Laravel) — all you need for commercial use
OPENMETEO_API_KEY=your-subscription-key
OPENMETEO_USER_AGENT=my-app/1.0
```

No manual host remapping required.

Non-Laravel apps can set the `OPENMETEO_API_KEY` environment variable (read by the package config) or bootstrap with:

```
use TempiMarathon\OpenMeteo\Support\OpenMeteoConfig;

OpenMeteoConfig::configure(['apikey' => 'your-subscription-key']);
```

Not every commercial plan includes every API — see the [pricing table](https://open-meteo.com/en/pricing). Standard covers forecast, marine, air quality, geocoding, elevation, and flood; Professional adds historical, climate, ensemble, seasonal, and more.

#### Advanced: explicit host overrides

[](#advanced-explicit-host-overrides)

Explicit `hosts` in config always take precedence over auto-switching. Use this for self-hosted instances, debugging, or when you need a specific endpoint.

Host overrides are validated to prevent traffic from being silently redirected to an untrusted origin. The policy depends on where the value comes from:

- **Trusted sources** — an explicit `OpenMeteoConfig::configure([...])` call, a registered resolver, or (in Laravel) `config/openmeteo.php` resolved through the container — may target **any HTTPS host**. Loopback hosts (`localhost`, `127.0.0.1`) may also use plain HTTP for local development.
- **Untrusted sources** — a bare config file loaded from disk outside a framework — may only target `open-meteo.com` (and its subdomains) over HTTPS, or a loopback host. Anything else falls back to the built-in default host.

Config keyFree (default)Commercial (auto or manual)`forecast``https://api.open-meteo.com/v1/``https://customer-api.open-meteo.com/v1/``historical``https://archive-api.open-meteo.com/v1/``https://customer-archive-api.open-meteo.com/v1/``geocoding``https://geocoding-api.open-meteo.com/v1/``https://customer-geocoding-api.open-meteo.com/v1/``air_quality``https://air-quality-api.open-meteo.com/v1/``https://customer-air-quality-api.open-meteo.com/v1/``marine``https://marine-api.open-meteo.com/v1/``https://customer-marine-api.open-meteo.com/v1/``climate``https://climate-api.open-meteo.com/v1/``https://customer-climate-api.open-meteo.com/v1/``flood``https://flood-api.open-meteo.com/v1/``https://customer-flood-api.open-meteo.com/v1/``ensemble``https://ensemble-api.open-meteo.com/v1/``https://customer-ensemble-api.open-meteo.com/v1/``seasonal``https://seasonal-api.open-meteo.com/v1/``https://customer-seasonal-api.open-meteo.com/v1/``elevation``https://api.open-meteo.com/v1/``https://customer-api.open-meteo.com/v1/`Self-hosted deployments should set custom `hosts` and leave `apikey` unset.

Laravel
-------

[](#laravel)

The package auto-registers via Laravel package discovery — no manual provider registration is required, and it works out of the box with zero configuration.

Configuration is read live from the container on every request, so the SDK stays correct under long-running runtimes such as [Octane](https://laravel.com/docs/octane) and queue workers.

Publish the config file only if you want to customise it:

```
php artisan vendor:publish --tag=openmeteo-config
```

This copies `config/openmeteo.php` into your application's `config/` directory. Until you publish, the package's bundled defaults are used (merged via `mergeConfigFrom`).

All environment variables are optional:

```
# OPENMETEO_API_KEY=your-subscription-key
# OPENMETEO_USER_AGENT=my-app/1.0
```

Resolve the entry point from the container (registered as a singleton):

```
use TempiMarathon\OpenMeteo\OpenMeteo;

$forecast = app(OpenMeteo::class)->forecast()->weather()->get(52.52, 13.41)->dto();
```

Attribution
-----------

[](#attribution)

Open-Meteo data is licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/). Geocoding data includes information from GeoNames — see [ATTRIBUTIONS.md](ATTRIBUTIONS.md).

For commercial use boundaries and licensing, see [Open-Meteo pricing](https://open-meteo.com/en/pricing).

Security
--------

[](#security)

See [SECURITY.md](SECURITY.md) for API key handling and `debugUrl()` guidance.

Quality
-------

[](#quality)

Run `composer test` for Pint, PHPStan (max), and Pest (100% coverage).

Run `composer test:mutation` for mutation testing (Pest mutate).

Contributing
------------

[](#contributing)

See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and enum regeneration (`composer generate`).

###  Health Score

44

—

FairBetter than 90% of packages

Maintenance91

Actively maintained with recent releases

Popularity14

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity52

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 92.9% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~2 days

Total

4

Last Release

44d ago

Major Versions

v1.1.0 → v2.0.02026-07-17

PHP version history (2 changes)v1.0.0PHP ^8.5

v2.0.0PHP ^8.3

### Community

Maintainers

![](https://www.gravatar.com/avatar/75da90ea64265480e20bc5e9f152c167663bb26228eb302ed957d4cfcabeadd9?d=identicon)[tempi-marathon](/maintainers/tempi-marathon)

---

Top Contributors

[![tempi-marathon](https://avatars.githubusercontent.com/u/191029268?v=4)](https://github.com/tempi-marathon "tempi-marathon (26 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (2 commits)")

---

Tags

open-meteoweatherweather-api

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/tempi-marathon-open-meteo-php/health.svg)

```
[![Health](https://phpackages.com/badges/tempi-marathon-open-meteo-php/health.svg)](https://phpackages.com/packages/tempi-marathon-open-meteo-php)
```

###  Alternatives

[jlevers/selling-partner-api

PHP client for Amazon's Selling Partner API

4425.6M2](/packages/jlevers-selling-partner-api)[saloonphp/laravel-plugin

The official Laravel plugin for Saloon

808.1M240](/packages/saloonphp-laravel-plugin)[whatsdiff/whatsdiff

See what's changed in your project's dependencies

761.9k](/packages/whatsdiff-whatsdiff)[codebar-ag/laravel-docuware

DocuWare integration with Laravel

1226.1k](/packages/codebar-ag-laravel-docuware)[myoutdeskllc/salesforce-php

salesforce library for php8+

1595.0k](/packages/myoutdeskllc-salesforce-php)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
