Community. Driven. Weather. Data. | Always Ad-Free | Developer FriendlyChecking statusDiscord
WxAlerts.org, Community Driven Weather Data
Support us
Sign in
API

MQTT lightning feed

Live GLM flashes over MQTT-on-WebSocket, with a geohash topic tree that doubles as the spatial index.


GOES-19 GLM flashes are published to a public broker as they arrive. This is the same feed the live map draws from, and the browser subscribes directly, with no API in the middle.

The same broker also carries NWS hazards on a separate topic tree, documented on the MQTT alerts feed page.

Connecting

URL wss://mqtt.wxalerts.org/mqtt
Transport MQTT over WebSocket
Username wxalerts
Password wxalerts

The credentials are published on purpose. The account is subscribe-only on the public trees and the broker’s ACL denies publishing.

The topic tree is the index

Flashes publish to one topic level per geohash character:

wxalerts/glm/v1/{g}/{g}/{g}/{g}/{g}

So you pick your own resolution with a wildcard, and the message rate follows:

wxalerts/glm/v1/9/#            ~5000 km
wxalerts/glm/v1/9/x/#          ~1250 km
wxalerts/glm/v1/9/x/j/#         ~156 km
wxalerts/glm/v1/9/x/j/5/#        ~39 km
wxalerts/glm/v1/9/x/j/5/e         ~4.9 km   (the published leaf)

Reading the feed

import json
import paho.mqtt.client as mqtt

def on_connect(client, userdata, flags, rc):
    # Roughly the Dallas–Fort Worth area.
    client.subscribe("wxalerts/glm/v1/9/v/g/#")

def on_message(client, userdata, msg):
    flash = json.loads(msg.payload)
    print(flash["lat"], flash["lon"], flash["at"])

client = mqtt.Client(transport="websockets")
client.username_pw_set("wxalerts", "wxalerts")
client.tls_set()
client.on_connect = on_connect
client.on_message = on_message
client.connect("mqtt.wxalerts.org", 443)
client.loop_forever()

Things that will bite you

Nothing is retained. Lightning is an event, not a state, so there is no opening burst when you subscribe and no tombstone when a flash is over. Age flashes out yourself, and ten minutes is a reasonable ceiling.

Observation time is not arrival time. Flash-to-broker latency is around 17 seconds and granules cover a 20-second window, so a flash is already older than any sensible animation window by the time it reaches you. Fade on the observation timestamp; animate on arrival.

Retention is hours, not days. glm_flashes keeps about three hours. If you need a daily total, roll it up yourself as it arrives.