--- name: skyviewor-metar description: Integrate Skyviewor METAR aviation weather into applications using its authenticated HTTP queries and live WebSocket subscriptions. Use for implementing, testing or troubleshooting Skyviewor data.skyviewor.io clients, not unrelated weather providers or account and billing automation. metadata: version: "1.1.0" reviewed: "2026-10-13" --- # Skyviewor METAR integration Build a client for the user's requested airports and stack. Python examples are provided, not a requirement to replace an existing stack. This bundle describes the public customer API, not the data-source API. ## Choose the relevant resources - For HTTP raw/parsed queries, snapshots or latency statistics, read [references/http.md](references/http.md). Adapt [scripts/http_query.py](scripts/http_query.py) if helpful. - For live delivery, read [references/websocket.md](references/websocket.md) before implementation. The bounded [scripts/verify_stream.py](scripts/verify_stream.py) tests actual business delivery and heartbeat responsiveness. - Validate requested codes against [references/airports.json](references/airports.json). It is a versioned supported-code list, not a guarantee that every station has a current report. - For auth failures, gaps or connection stalls, read [references/troubleshooting.md](references/troubleshooting.md). ## Public contract and integration decisions - HTTP origin: `https://data.skyviewor.io`; WebSocket origin: `wss://data.skyviewor.io`. Use `Authorization: Bearer ` for both. Never place a key in a URL. Read `SKYVIEWOR_API_KEY` from a backend/local environment, not browser-side code or committed files. - Use HTTP to initialize or recover latest state; prefer a persistent WebSocket for ongoing changes rather than rapid polling. Shared defaults: 2 HTTP requests/second per key, 3 WebSocket connections, 100 distinct airports across connections. A multi-airport query is one request. - An observation's `obs_time` is not its receipt time. `arrived_at` is when the data source received the report, not when our service or your client received it. Latency statistics describe observation-to-service receipt, not service-to-client forwarding. - Preserve METAR and SPECI reports. Deduplicate by `(icao, obs_time, raw_metar)`, not observation time alone: same-time corrected text is meaningful. Missing weather is not zero weather. - WebSocket does not send an initial snapshot or replay every missed report. On start/reconnect, listen and buffer updates while fetching `/v1/metar/latest`; merge without overwriting newer live observations with an older snapshot. Snapshot `versions` are per-airport integers; live messages currently contain observations, not those versions. - Send application-level `{"action":"ping"}` every 20 seconds and supervise the `{"type":"pong"}` response with a 15-second timeout. A connection with no application heartbeat or valid subscription operation for 60 seconds closes with code 4000. WebSocket protocol ping/pong (including a library's `ping_interval`) does not satisfy this requirement. On heartbeat timeout or 4000, close the old socket, reconnect with backoff/jitter, restore subscriptions and reconcile the latest snapshot. Do not wait 60 seconds between pings or use incoming weather as proof of client liveness. - Browser WebSocket cannot set the required Authorization header. Use a server-side client/relay; do not invent URL-token auth. - Serialize reconnects per logical client: cancel and await the old reader/heartbeat tasks, await the old socket's bounded close, then open a replacement. The 3-connection limit counts all processes/servers sharing the key. Normal closure actively releases a slot but is not an atomic close-and-replace guarantee; abrupt network loss or a brief release race can still produce handshake HTTP 429. Do not promise immediate reuse or a guaranteed recovery within 60 seconds. Retry 429 with capped backoff/jitter and an explicit attempt/time budget, then show an actionable quota/support error. See the reconnect procedure in references/websocket.md; never spawn parallel retry loops or retry 401/403 indefinitely. - Scope is METAR/SPECI. Do not invent SURF endpoints, historical-query features or runway datasets. Raw reports can contain runway visual range groups; those do not imply a runway-data API. ## Implementation and validation 1. Determine the airports and required raw/parsed/live behavior; inspect the existing project before introducing dependencies. 2. Implement the documented request/message shapes, bounded IO timeouts, heartbeat failure detection and appropriate error handling. Retry only recoverable failures, with backoff and jitter; credential failures require a valid key, not an infinite loop. 3. Test parsing, corrections, duplicate delivery, snapshot races, missing data and reconnects using synthetic fixtures first. Default tests must not contact the live service. 4. Run real requests only when the user authorizes them and supplies credentials through the environment. The included scripts require `--live`; a stream observation window is at most 1800 seconds. No load tests, purchases, key rotation or deployment are implied by this skill. 5. Report HTTP success, heartbeat/handshake success and business-update success separately. A live stream check passes only after a valid subscribed-airport `new_metar_arrived` observation. No update before deadline is inconclusive/unverified, not success. Label simulated tests and do not represent example reports as current weather. ## Example local verification Install Python 3.11+ dependencies in the user's chosen environment: `httpx` and `websockets>=15`. Set the key privately before running: ```bash python scripts/http_query.py --live --airports ZBAA,ZSPD --endpoint metar --mode raw python scripts/http_query.py --live --airports ZBAA,ZSPD --endpoint latest python scripts/http_query.py --live --airports ZBAA,ZSPD --endpoint latency python scripts/verify_stream.py --live --airports ZBAA,ZSPD --seconds 300 ``` Paths are relative to this skill folder. No command installs this bundle or publishes it. For contract changes, consult `https://www.skyviewor.io/docs/metar/` and update this bundle's references and tests together; document any compatibility change.