Community. Driven. Weather. Data. | Always Ad-Free | Developer FriendlyChecking statusDiscord
Become a supporter
Sign in
DocsDevelopersReference

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
Host and port mqtt.wxalerts.org, 443
Transport MQTT over WebSocket, path /mqtt, TLS
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.

Flash batches are published at QoS 0.

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)

The payload

Each message is a batch: every flash one 20-second GLM granule put in one five-character geohash cell, oldest first. A busy cell is split across several messages of at most 500 flashes each.

{
  "geohash": "9vg4m",
  "satellite": "goes19",
  "window_start": "2026-11-09T19:09:20+00:00",
  "window_end": "2026-11-09T19:09:40+00:00",
  "count": 2,
  "flashes": [
    { "t": "2026-11-09T19:09:19.127184+00:00", "lat": 32.7731, "lon": -96.8075, "energy_j": 7.53e-14, "area_m2": 412000000.0 },
    { "t": "2026-11-09T19:09:31.840211+00:00", "lat": 32.7802, "lon": -96.7911, "energy_j": 2.1e-13, "area_m2": 655000000.0 }
  ]
}

t is the flash’s observation time. lat and lon are rounded to four decimals, more than the instrument’s few kilometres of accuracy. energy_j is the optical energy seen at the cloud top, not a measure of what reaches the ground; the flash rate is the more honest signal of a storm’s strength.

Retention and timing

Flashes are not 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. The status heartbeat is the only retained topic in this tree.

A batch reaches the broker about 20 seconds after its granule’s window closes, and the window itself is 20 seconds long, so a flash is typically 20 to 40 seconds old when it reaches you.

Broker limits

There is a 20-subscription cap per client. The 21st subscribe is refused with a failure code in the SUBACK.

History

The flash table behind GET /lightning/flashes keeps 24 hours, and one request covers at most 180 minutes of it. Per-day counts are at GET /v1/stats/lightning/daily, which needs an API key.

Feed health

wxalerts/glm/v1/status is a retained heartbeat, refreshed every 30 seconds, so a quiet sky can be told apart from a dead feed:

{
  "at": "2026-11-09T19:09:39.387311+00:00",
  "healthy": true,
  "feed": true,
  "mqtt": true,
  "last_granule_age_seconds": 19,
  "stale_after_seconds": 900,
  "satellite": "goes19",
  "geohash_precision": 5,
  "backfill": true
}

Judge it by at, not by when it arrived, because a retained message can be old. If the publisher drops off the broker, the broker replaces it with {"healthy": false, "reason": "publisher disconnected"}. The geohash wildcards above never match this topic, so subscribe to it on its own.

To write a client for this feed, see Build a reliable MQTT client.

View this page as Markdown

May we use Microsoft Clarity to see how pages are used (clicks, scrolling and replays of a visit, with anything you type hidden), plus a random ID linking that to crash reports? Only to find bugs and confusing screens. Never advertising, never sold.