Skip to main content
skill CX Codex

connector-weatherapi — Claude Code skill: weather data in canisters

The skill pins down the supported way to read weather in Caffeine builds: the weatherapi-client mops package instead of hand-rolled ic.http_request calls.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add caffeinelabs/skills --skill connector-weatherapi -a claude-code
Open on GitHub

What connector-weatherapi does

Nine read-only operations from WeatherAPI.com, generated from its OpenAPI spec into one module, Apis/APIsApi: current weather, forecast, history, future weather, marine, astronomy, timezone, IP lookup and location autocomplete. The skill ships Motoko bindings plus an actor example that reads the current temperature and a three-day maximum series.

The API key travels as a query-string credential, not a header, so it ends up in the request URL. Keep it in a stable variable written by an admin-only call and never log the built URL. The free tier covers current weather, 3-day forecast, astronomy, timezone, search and IP lookup; history, future, marine and 14-day forecasts need a paid plan and return 403 on a free key.

Calls are non-replicated by default, and that is a correctness requirement rather than a cost saving. Responses carry per-request clocks, so a replicated outcall would fail consensus on most calls. Every response field is optional, which makes do ? blocks the practical way to reach into them. An empty string or zero means "omit": hour = 0 does not select midnight, it drops the filter and returns the whole day.

What it helps with

  • read the current temperature from a canister
  • fetch a three-day series of daily maxima
  • wire up a weather forecast without manual HTTP calls
  • handle all-optional WeatherAPI response fields
  • tell a 401 from a 403 and a 400 by the error text

Example prompts for the agent

  • add current temperature reading to the backend with weatherapi-client
  • build a three-day max temperature forecast for Zurich
  • why does my ic.http_request to api.weatherapi.com fail consensus?

How to install connector-weatherapi

1Skills CLI (Vercel)

npx skills add caffeinelabs/skills --skill connector-weatherapi -a claude-code

2Manually: put the files in place

~/.claude/skills/connector-weatherapi/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/connector-weatherapi/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about connector-weatherapi

How do I install connector-weatherapi in Claude?

Run npx skills add caffeinelabs/skills --skill connector-weatherapi -a claude-code. The skill lands in ~/.claude/skills/connector-weatherapi/SKILL.md.

What does connector-weatherapi do?

It documents the supported weather path in Caffeine builds: the weatherapi-client mops package with nine WeatherAPI.com operations and proper API-key handling.

Why is calling api.weatherapi.com directly forbidden?

Hand-rolled ic.http_request calls skip the non-replicated-outcall safeguard: response bodies carry per-request clocks, so a replicated call cannot reach consensus.

Source text

SKILL.md · Apache-2.0
---
name: connector-weatherapi
description: >-
  MANDATORY recipe for every Caffeine build that reads weather data from a
  canister. The supported path is the `weatherapi-client` mops package
  (WeatherAPI.com) over outbound HTTPS, authenticated with a query-string API
  key. Hand-rolling `ic.http_request` calls to `api.weatherapi.com` is a
  FORBIDDEN anti-pattern — it bypasses the non-replicated-outcall safeguard
  (WeatherAPI bodies carry per-request clocks and fail consensus), the generated
  JSON decoding, and the API-key handling. Load this skill whenever the user,
  spec, or any prior task mentions weather, forecast, temperature, humidity,
  wind, rain, snow, conditions, sunrise, sunset, moon phase, astronomy,
  timezone, IP geolocation, marine or tide forecasts, or location autocomplete —
  and BEFORE writing any code that touches a weather endpoint.
version: 0.2.2
caffeineai-subscription: [none]
compatibility:
  mops:
    weatherapi-client: "~0.3.0"
---

# Weather data with `weatherapi-client`

Motoko bindings for [WeatherAPI.com](https://www.weatherapi.com/), generated
from its OpenAPI spec. All nine operations live in a single module,
**`Apis/APIsApi`**, and all nine are read-only `GET`s.

# Backend

A canister that reads the current temperature and a three-day maximum series.
Non-replicated is the default, so you only supply the key:

```motoko filepath=src/backend/main.mo
import { realtimeWeather; forecastWeather } "mo:weatherapi-client/Apis/APIsApi";
import { type Config; defaultConfig } "mo:weatherapi-client/Config";
import Array "mo:core/Array"; // in scope so `days.map(…)` dot notation resolves

actor {
  // The key is a query-string credential. Hold it in a stable variable set by
  // an admin call; never hard-code it in source.
  func config(apiKey : Text) : Config = { defaultConfig with auth = ?#apiKey apiKey };

  // `q` is any WeatherAPI location query: "Zurich", "47.37,8.55", a postcode,
  // an IATA code, or "auto:ip".
  public func currentTempC(apiKey : Text, q : Text) : async ?Float {
    let res = await* realtimeWeather(config apiKey, q, "");
    do ? { res.current!.temp_c! };
  };

  // Daily maxima for the next three days.
  public func maxTempsC(apiKey : Text, q : Text) : async [?Float] {
    let res = await* forecastWeather(config apiKey, q, #_3_, "", 0, 0, "", "no", "no", 0);
    let ?forecast = res.forecast else return [];
    let ?days = forecast.forecastday else return [];
    days.map(func(d) = do ? { d.day!.maxtemp_c! });
  };
}
```

## The nine operations

| Function | Endpoint | Returns |
|---|---|---|
| `realtimeWeather(cfg, q, lang)` | `/current.json` | `RealtimeWeather200Response` |
| `forecastWeather(cfg, q, days, dt, unixdt, hour, lang, alerts, aqi, tp)` | `/forecast.json` | `ForecastWeather200Response` |
| `historyWeather(cfg, q, dt, unixdt, endDt, unixendDt, hour, lang)` | `/history.json` | `FutureWeather200Response` |
| `futureWeather(cfg, q, dt, lang)` | `/future.json` | `FutureWeather200Response` |
| `marineWeather(cfg, q, days, dt, unixdt, hour, lang)` | `/marine.json` | `MarineWeather200Response` |
| `astronomy(cfg, q, dt)` | `/astronomy.json` | `Astronomy200Response` |
| `timeZone(cfg, q)` | `/timezone.json` | `Location` |
| `ipLookup(cfg, q)` | `/ip.json` | `Ip` |
| `searchAutocompleteWeather(cfg, q)` | `/search.json` | `[Search]` |

`days` is an enum, not a number: `ForecastWeatherDaysParameter` is `#_1_` … `#_14_`
and `MarineWeatherDaysParameter` is `#_1_` … `#_7_` (the underscores are how the
generator escapes numeric enum values — `#_3_`, not `#_3` or `3`).

## API key setup

1. Sign up at [weatherapi.com](https://www.weatherapi.com/signup.aspx) — the
   free tier covers current weather, 3-day forecast, astronomy, timezone,
   search and IP lookup. History, future, marine and 14-day forecasts need a
   paid plan and return **403** on free keys.
2. Copy the key from the dashboard and pass it as `auth = ?#apiKey key`.
3. The client appends it as `?key=…` (WeatherAPI takes no `Authorization`
   header), so **it appears in the request URL**. Keep it in a stable variable
   written by an admin-only call, and never log the built URL.

## Calls are non-replicated by default

The package ships `is_replicated = ?false` in `defaultConfig`, and that is a
*correctness* requirement here, not just a cost saving. Every response carries
per-request clocks — `Location.localtime` / `localtime_epoch`,
`Current.last_updated` / `last_updated_epoch` — which change second to second. A
*replicated* outcall has every subnet node issue its own request and demands
bit-identical bodies, so those fields would break consensus on most calls while
burning ~13× the cycles. You don't set it yourself; the default is correct.

Override with `is_replicated = ?true` only together with a `transform` that
strips the volatile fields.

## Everything is optional

WeatherAPI marks no response field required, so the generated models are
all-optional: `RealtimeWeather200Response.current : ?Current`,
`Current.temp_c : ?Float`, and so on. Reach through them with a `do ?` block
(`do ? { res.current!.temp_c! }`) rather than nested `switch`es, and decide what
an absent field means for your caller — the API omits fields your plan does not
cover (for example `air_quality` without the `aqi=yes` parameter).

## Empty string and zero mean "omit"

Optional query parameters are dropped when they are `""` or `0`, because
WeatherAPI rejects empty `lang=` and zero-valued numerics. So passing `""` for
`lang`, `dt`, `alerts`, `aqi` and `0` for `unixdt`, `tp` is how you say "not
supplied" — there is no `?Text` parameter to leave `null`.

One consequence worth knowing: **`hour = 0` does not select midnight**, it omits
the `hour` filter entirely and you get the whole day's hourly array. Filter the
returned `hour : ?[ForecastForecastdayInnerHourInner]` yourself if you need
00:00.

## Errors

Non-2xx responses and decode failures `throw Error.reject(…)`. The client is
generated with `diagnostics`, so the message is
`HTTP <status> body[<n>B]=<first 100 chars>: <reason>` — enough to tell a 401
(bad key) from a 403 (endpoint not on your plan) from a 400 (`q` not resolvable)
without extra logging. Catch with `try`/`catch` and surface
`Error.message(err)`.

© caffeinelabs · Apache-2.0 · caffeinelabs/skills

Similar skills

Everything for Backend and APIs →

From the same set25

connector-slack A skill that gets your agent posting messages to a Slack channel from a Caffeine canister through the slack-client package. The recipe is marked experimental. CX Codex +1 14,300installs extension-inference The skill spells out the only supported way to wire an LLM into a Caffeine app: the caffeineai-inference-client package with Config.fromEnv, no user-pasted key. CX Codex +1 11,900installs connector-twilio A skill that wires an agent to Twilio Messaging through the twilio-client mops package. Experimental: no call has been verified against live Twilio yet. CX Codex +1 11,200installs connector-googledrive A skill that wires Google Drive into Caffeine builds: listing files, folders, sharing and comments through ready mops packages. CX Codex +1 9,100installs connector-spotify A skill for Caffeine builds on Motoko: routes Spotify Web API work through the spotify-client mops package and rules out hand-rolled HTTP calls. CX Codex +1 6,500installs connector-tmdb This skill wires The Movie Database into Caffeine projects written in Motoko through the tmdb-client package. Load it when the app reads movies, TV shows or people data. CX Codex +1 6,200installs migrating-motoko-actors Guides actor state migrations across canister upgrades and stable schema changes through the migration chain in migrations/. CX Codex +1 4,300installs troubleshooting-motoko-migrations A reference for when the Motoko migration chain misbehaves: diagnostics that do not match main.mo, or edits to a migration file that never take effect. CX Codex +1 4,500installs

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor