> ## Documentation Index
> Fetch the complete documentation index at: https://docs.metar.ws/llms.txt
> Use this file to discover all available pages before exploring further.

# Go

> A complete, reconnecting WebSocket client using gorilla/websocket.

```go theme={null}
package main

import (
      "encoding/json"
      "log"
      "net/url"
      "time"

      "github.com/gorilla/websocket"
  )

  const key = "mts_live_YOUR_KEY"

  var channels = []string{"metar.obs.eglc", "metar.obs.rksi"}

  type frame struct {
        Type    string `json:"type"`
        Plan    string `json:"plan,omitempty"`
        Code    string `json:"code,omitempty"`
        Channel string `json:"channel,omitempty"`
        Data    struct {
                  Station    string `json:"station"`
                  TempC      string `json:"temp_c"`
                  ReportTime string `json:"report_time"`
                  Raw        string `json:"raw"`
        } `json:"data,omitempty"`
  }

  func stream() error {
        u := url.URL{Scheme: "wss", Host: "stream.metar.ws", Path: "/v1/ws",
                            RawQuery: url.Values{"key": {key}}.Encode()}
        ws, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
        if err != nil {
                  return err
        }
        defer ws.Close()

        var ack frame
        if err := ws.ReadJSON(&ack); err != nil {
                  return err
        }
        log.Printf("connected: plan=%s", ack.Plan)

        sub := map[string]any{"action": "subscribe", "channels": channels}
        if err := ws.WriteJSON(sub); err != nil {
                  return err
        }

        for {
                  var msg frame
                  if err := ws.ReadJSON(&msg); err != nil {
                                return err
                  }
                  switch msg.Type {
                  case "publication":
                                log.Printf("%s: %s degC at %s", msg.Data.Station, msg.Data.TempC, msg.Data.ReportTime)
                  case "error":
                                log.Printf("error: %s %s", msg.Code, msg.Channel)
                  }
        }
  }

  func main() {
        for { // reconnect loop
                if err := stream(); err != nil {
                              log.Printf("disconnected: %v, reconnecting in 3s", err)
                }
                  time.Sleep(3 * time.Second)
        }
  }
```

<Note>
  On the Sandbox plan, subscribe to `sandbox.demo` instead and trigger a message with **Send test alert** on the Billing page in the portal.
</Note>

<CardGroup cols={3}>
  <Card title="Python" icon="python" href="/examples/python">
    The same client using websockets
  </Card>

  <Card title="Node.js" icon="node-js" href="/examples/nodejs">
    The same client using ws
  </Card>

  <Card title="Browser" icon="window" href="/examples/browser">
    A minimal client for internal dashboards
  </Card>
</CardGroup>
