Replacing the Dark Sky weather API: WeatherKit, 7Timer and other free APIs
Tags: weather, weatherkit, perl, api
Published: 2023-03-31 11:51:09

Today, March 31st 2023, the excellent [Dark Sky API](https://darksky.net/dev), a quite popular weather forecast service, is going... well... dark. It is now part of **Apple Weather** and replaced by [WeatherKit](https://developer.apple.com/weatherkit/get-started/).

If you used Dark Sky for iOS 16 apps, you will have probably already migrated to WeatherKit. For any other platform, including older iOS versions, you'd need something else, with various possible options starting with Apple's own [WeatherKit REST API](https://developer.apple.com/documentation/weatherkitrestapi).

Read on if you are either having issues accessing the WeatherKit REST API, or you want a completely free alternative and I'll share some tips and solutions from my experience porting my own app [Xasteria Plus](https://astro.ecuadors.net/xasteria/).


## WeatherKit REST API ##

Apple thankfully provides a REST API that is mostly a complete replacement of Dark Sky - however there are a couple of "gotchas". Firstly, we should note that part of the appeal of Dark Sky was its cost: free for up to 1000 calls/day. The WeatherKit API requires an Apple developer account, which costs $99/year. With that cost though, it allows 500k requests per month, so if you already have a developer account it's a great deal. Secondly, instead of a simple API key you can pass with your request, the service requires passing signed developer tokens (JWT) with the request header, which requires some setup.

If the first issue (the developer account) is no problem, I can assist with the second, otherwise jump to the [next section](#7timer-and-other-free-solutions). Here are the steps you will need to follow:

### Setup ###

- Register a new **WeatherKit key** in the **Certificates, IDs & Profiles** section of the Apple developer portal. Download the key in the last step and you'll have a file named like `AuthKey_YOURKEYID.p8`.
- The key is in the PKCS8 format, in most cases you'll need to convert to the PEM format. On a Mac you can convert it simply:
```
openssl pkcs8 -nocrypt -in AuthKey_YOURKEYID.p8 -out AuthKey_YOURKEYID.pem

```
- Back on **Certificates, IDs & Profiles** you'll need to provision a new **Service ID**. You'll normally use the reverse-domain notation for it (com.mycompany.myapp).
- Locate your **Team ID** - it's under **Membership Details** on the Apple developer portal.

### Accessing the API ###

You now have what you need (Team ID, PEM key, Key ID, service ID) to create a [JSON Web Token](https://jwt.io/) to access the API per the [official documentation](https://developer.apple.com/documentation/weatherkitrestapi/request_authentication_for_weatherkit_rest_api). The token is used in the header of the api request, e.g.:
```
curl -v -H 'Authorization: Bearer [developer token]' "https://weatherkit.apple.com/api/v1/availability/37.323/122.032?country=US"
```

### Easy access with Perl / Weather::WeatherKit ###

For my own app, I created the Perl module [Weather::WeatherKit](https://metacpan.org/pod/Weather::WeatherKit), which takes care of all the token creation etc. It makes it very simple if you use Perl, but even if you don't you can still make a full WeatherKit API proxy in a few lines of code as I'll demonstrate - which you can then access any way you like.

Why Perl? Despite the flak it gets from devs who don't use it, it's still a great solution for many tasks and I still prefer it over python for those tasks. Also, it's already available or readily available on most systems (Mac/Linux/BSD), but if you'd like to have one of the latest versions, a good way to get it is via [perlbrew](https://perlbrew.pl/):
```
\curl -L https://install.perlbrew.pl | bash
perlbrew install perl-5.36.0
perlbrew switch perl-5.36.0
```

Apart from Perl, you'll need a package manager to make your life easier, like [cpanm](https://metacpan.org/dist/App-cpanminus/view/bin/cpanm). On linux you'd use apt/yum to install a package called something like `cpanminus` or `perl-App-cpanminus`, while if you used perlbrew you'd do:
```
perlbrew install-cpanm
```
You can now install the two packages we'll use. I will use [Mojolicious](https://metacpan.org/pod/Mojolicious), which is a modern web app framework, so that I am not accused of using your grandpa's CGI:
``` 
cpanm Mojolicious::Lite Weather::WeatherKit
```

The full code for a proxy that handles the authentication follows:

```perl
#!/usr/bin/env perl
 
use Mojolicious::Lite -signatures;
use Weather::WeatherKit;

any '/weather' => sub ($c) {
    my %params  = map {$_ => $c->param($_)} $c->req->params->names->@*;
    my $weather = Weather::WeatherKit->new(
        team_id    => "TEAMID",
        service_id => "com.mycompany.myapp",
        key_id     => "YOURKEYID",
        key_file   => "AuthKey_YOURKEYID.pem"
    );
    my $res = $weather->get(%params);
    $c->render(text => $res);
};

app->start;
```

Assuming you saved the above as `weather-server.pl` and put it in the same directory as your `.pem` key, you can start it as a web service that listens to port 3000 with:
```
morbo cgi-bin/weather-server.pl
```
After that, simply point a browser to `http://127.0.0.1:3000/weather?lat=51.2&lon=-1.8&dataSets=forecastHourly` and you'll get a hourly forecast for the next day for [Stonehenge](https://www.youtube.com/watch?v=zg5Ovdu6bOE) in JSON format:

```json
{
   "forecastHourly":{
      "name":"HourlyForecast",
      "metadata":{
         "attributionURL":"https://weatherkit.apple.com/legal-attribution.html",
         "expireTime":"2023-03-31T10:41:14Z",
         "latitude":51.2,
         "longitude":-1.8,
         "readTime":"2023-03-31T09:41:14Z",
         "reportedTime":"2023-03-31T08:05:04Z",
         "units":"m",
         "version":1
      },
      "hours":[
         {
            "forecastStart":"2023-03-31T09:00:00Z",
            "cloudCover":0.98,
            "conditionCode":"Drizzle",
            "daylight":true,
            "humidity":0.89,
            "precipitationAmount":0.5,
            "precipitationIntensity":0.5,
            "precipitationChance":0.7,
            "precipitationType":"rain",
            "pressure":984.31,
            "pressureTrend":"rising",
            "snowfallIntensity":0,
            "snowfallAmount":0,
            "temperature":9.9,
            "temperatureApparent":6.07,
            "temperatureDewPoint":8.18,
            "uvIndex":1,
            "visibility":7558.44,
            "windDirection":242,
            "windGust":57.61,
            "windSpeed":35.96
         },
...
```


## 7Timer and other free solutions ##

### 7Timer ###

**[7Timer](https://7timer.info/)** is a completely free service based on [NOAA's GFS](https://www.ncei.noaa.gov/products/weather-climate-models/global-forecast). It's notable for calculating astronomical seeing and atmospheric transparency, which are very useful to astronomers and stargazers. I am donating the servers to the project and developing the iOS client [Xasteria](https://apps.apple.com/us/app/xasteria-astronomy-weather/id985030722) (also free).

You can check the [documentation](https://www.7timer.info/doc.php) for the available forecast products and the meaning of the output values. If you use Perl, you can use [Weather::Astro7Timer](https://metacpan.org/pod/Weather::Astro7Timer) to access the service in your code, otherwise here are a couple of example requests, one for the 3-day **ASTRO** forecast, and one for the 7-day **CIVIL** forecast:

```
http://www.7timer.info/bin/astro.php?lat=51.2&lon=-1.8&output=json
http://www.7timer.info/bin/civil.php?lat=51.2&lon=-1.8&output=json
```
Try them on your browser.

### MET Norway ###

The [Norwegian Meteorological Institute](https://www.met.no/en) offers a number of [APIs](https://api.met.no/). Their forecast API [Locationforecast 2.0](https://api.met.no/weatherapi/locationforecast/2.0/documentation) is based on the European [ECMWF](https://www.ecmwf.int/) forecast and it's a great solution, especially for users not interested in the astronomical aspects of 7Timer. It's completely free/open like 7Timer, so all you have to do to get a forecast is a request like this:

```
https://api.met.no/weatherapi/locationforecast/2.0/complete?lat=51.2&lon=-1.8
```

Personally, given that **MET Norway**, **7Timer** and **Apple Weather** are based on different forecast models, I find that consulting these three gives me a better forecast confidence (a hard thing here in the UK).

### OpenWeather ###

[OpenWeather](https://openweathermap.org/) is a bit more like Dark Sky in that you have to sign up to get a key and then you get 1000 free requests per day ([One Call API](https://openweathermap.org/api/one-call-3)). So it may be completely free if it fits your usage needs.

Their various [API options](https://openweathermap.org/api) may seem a bit confusing, but basically if you want something similar to Dark Sky, you would go with the aforementioned One Call API that provides current weather and minutely, hourly, daily forecasts in the future, or their [5 day / 3h forecast](https://openweathermap.org/forecast5). The latter has a greater free quota of 60 calls/min or 1 million per month.