mqtt.pskreporter.info republishes every reception report sent to PSK Reporter as an MQTT message, as it arrives. The spot’s details are in the topic, so you can use MQTT wildcards to subscribe to just the spots you want: by band, mode, callsign, grid square, DXCC entity, or any mix of those. It also carries WSPR spots and the details receiving stations publish about themselves.
There’s no account and no API key. The feed comes from Philip Gladstone at PSK Reporter, used with his permission. Part of the hosting is donated.
- mqtt.pskreporter.info: connection details, a topic builder, the payload format and live views built from the feed.
- An example listener in C#: M0LTE/pskr-mqtt-listener-example
- Discussion: groups.io pskr-mqtt
Connecting
Point any MQTT client at mqtt.pskreporter.info. There are four ports:
| Port | Transport |
|---|---|
| 1883 | MQTT over TCP |
| 1884 | MQTT over TLS |
| 1885 | MQTT over WebSockets |
| 1886 | MQTT over WebSockets with TLS, for pages served over https |
MQTT 3.1, 3.1.1 and 5 all work. Connect without a username or password. Use a client id without + or # in it, and a keepalive of something like 60 seconds. WebSocket clients have to offer the mqtt subprotocol, which the usual MQTT libraries do.
To try it from a terminal with the Mosquitto client:
mosquitto_sub -h mqtt.pskreporter.info -t "pskr/filter/v2/20m/FT8/#"
Topics
flowchart LR
P["pskr/"] --> V2["filter/v2/ (8 levels)"]
P --> VR["filter/v2raw/ (8 levels)"]
P --> V1["filter/v2raw_1pc/ (8 levels)"]
P --> VF["filter/v2_field/ (4 levels)"]
P --> H["heard"]
P --> R["rxinfo/{rx call}"]
W["wspr/"] --> WF["filter/ (8 levels)"]
W --> WH["heard"]Most people want pskr/filter/v2. Each spot is published to a topic with eight levels after that prefix:
pskr/filter/v2/{band}/{mode}/{sender call}/{receiver call}/{sender grid}/{receiver grid}/{sender DXCC}/{receiver DXCC}
For example, a 20m FT8 signal from G4ABC in IO91 heard by K1XYZ in FN42 goes to pskr/filter/v2/20m/FT8/G4ABC/K1XYZ/IO91/FN42/223/291. The grids in the topic are the 4 character square, in upper case; the payload has the full locator. DXCC entities are ADIF entity numbers. A / in a callsign would add a topic level, so it is written as . in the topic: G4ABC/P becomes G4ABC.P.
+ matches one level and # matches the rest. Some filters:
| Filter | Gets you |
|---|---|
pskr/filter/v2/20m/FT8/# | every 20m FT8 spot |
pskr/filter/v2/+/+/G4ABC/# | everyone who heard G4ABC |
pskr/filter/v2/+/+/+/G4ABC/# | everyone G4ABC heard |
pskr/filter/v2/+/+/+/+/+/IO91/# | everything heard in square IO91 |
pskr/filter/v2/+/CW/+/+/+/+/+/223 | CW heard by stations in England |
The other topic trees:
pskr/filter/v2only has spots where every field is present and valid. PSK Reporter’s data has gaps, so some spots are left out.pskr/filter/v2rawhas every spot with both callsigns. A missing field is written asUNKNOWNin the topic, and grids are as reported rather than cut to 4 characters.pskr/filter/v2raw_1pcis a random 1% ofv2raw. It’s handy for testing, and the live views on the website use it.pskr/filter/v2_field/{band}/{mode}/{sender field}/{receiver field}is keyed on the 2 character Maidenhead field, sopskr/filter/v2_field/+/+/IO/+is every spot sent from the IO field.pskr/heardgets the sender’s callsign, as plain text, for every spot.pskr/rxinfo/{receiver call}has each active receiver’s antenna, rig, decoding software and locator, taken from PSK Reporter’s list of active receivers. These are retained, so subscribing topskr/rxinfo/#gives you the whole current set straight away, then changes as they happen.wspr/filterandwspr/heardare the same thing for WSPR, with the same eight levels.
If a spot matches more than one of your filters, you get one copy per matching filter. Each pskr spot has a unique sequence number, sq, which you can use to drop repeats.
Message format
Each spot is one JSON object:
{
"sq": 30142870791,
"f": 21074653,
"md": "FT8",
"rp": -5,
"t": 1662407712,
"t_tx": 1662407700,
"sc": "SP2EWQ",
"sl": "JO93fn42",
"rc": "CU3AT",
"rl": "HM68jp36",
"sa": 269,
"ra": 149,
"b": "15m"
}
| Field | Meaning |
|---|---|
sq | PSK Reporter’s sequence number |
f | frequency, Hz |
md | mode |
rp | signal report, dB |
t | time the receiver reported, Unix seconds |
t_tx | when the transmission started, worked out by the service; can be absent |
sc, sl | sender callsign and locator |
rc, rl | receiver callsign and locator |
sa, ra | sender and receiver ADIF DXCC entity |
b | band |
t means different things for different software. Some programs report when the transmission started and others when they decoded it, which for FT8 is 11 to 14 seconds later. PSK Reporter keeps a list of about 2,000 software versions saying which is which. The service reads it every hour and uses it to fill in t_tx: the reported time if the software reports the start, the start of the 15 second slot for FT8 software that reports the decode time, and nothing otherwise. If you want one time per spot, use t_tx and fall back to t. The spot timing page has the details.
WSPR spots have the same fields apart from sq and t_tx, plus pw (claimed power, dBm), dt (time offset), dr (drift) and src (where the spot came from).
How it works
flowchart TD
S["PSK Reporter<br/>live spot stream"] --> I
L["PSK Reporter<br/>software and receiver lists"] -. hourly .-> I
W["WSPR sources"] --> I
I["Ingest service<br/>check, enrich, build topics"] --> H["Central broker<br/>not open to the public"]
H --> F["Fan-out brokers"]
F --> C["Your MQTT client"]
F --> B["Web pages<br/>in your browser"]There’s one ingest service. It holds a single streaming connection to PSK Reporter and reads spots off it as they arrive. For each spot it checks the fields, upper-cases the callsigns, works out t_tx, builds the topics and publishes the spot to each tree it qualifies for. One spot becomes four or five MQTT messages.
The same service fetches PSK Reporter’s software list and active receiver list every hour. Changes to a receiver are published to pskr/rxinfo when they’re seen, and every receiver is republished once a day. When a receiver drops off PSK Reporter’s list its retained message is cleared. Each of those messages also expires after 7 days, in case a clear is missed.
WSPR has no live stream to subscribe to, so the ingest service collects WSPR spots from wsprnet.org, wsprnet.eu, wspr.live and the WSPR spots already on the PSK Reporter stream, and keeps whichever copy of each decode arrives first. WSPR spots don’t come with DXCC entities, so those are worked out from the callsigns using the Big CTY country file.
flowchart TD
I["Ingest service"] -->|publishes| H["Central broker"]
H -->|bridge| A["Fan-out broker A"]
H -->|bridge| B["Fan-out broker B"]
A --> C["Clients of<br/>mqtt.pskreporter.info"]
B --> CThe ingest service publishes to a central broker that no public client connects to. Two fan-out brokers, on separate machines, each connect to the central broker over an MQTT bridge, subscribe to the public topic trees and republish everything locally. The name mqtt.pskreporter.info points at both, so clients are split between them. The central broker only ever has a handful of subscribers, and the fan-out brokers do the work of delivering to thousands of clients. Adding capacity means adding another fan-out broker and pointing DNS at it; nothing changes on the central machine.
The website is static HTML and JavaScript. The pages connect to the broker from your browser over WebSockets and build their maps and tables from the feed itself, mostly from the 1% sample. Besides the landing page there are views for a station, a Maidenhead field, a DXCC entity and a band, plus a propagation radar centred on one station.
Everything is QoS 0 from end to end: the ingest service publishes at QoS 0, the bridges carry QoS 0, and clients receive QoS 0. Nothing is retransmitted, so a dropped message is gone rather than late. The ingest service and the central broker count any messages they have to drop, and a small watcher on each broker looks for gaps in the sequence numbers, so loss shows up somewhere I can see it.
Scale and limits
These are rough figures from the second half of 2026.
- About 300 PSK Reporter spots a second come in, more in busy minutes. That is around 2,000 MQTT messages a second, or 28 GB a day, on the
pskrtrees alone. - About 2,500 clients are connected at once, split roughly evenly between the two fan-out brokers.
- Together the fan-out brokers send about 100,000 messages a second, around 16 MB/s.
- Several thousand receivers are listed on
pskr/rxinfoat any time.
If your client stops reading and about 1 MB of messages queues up for it, new messages are dropped until it catches up. You aren’t disconnected, so a slow client sees a delay and then a gap. Subscribe to only what you need: a filter for one callsign or one band is a few messages a second, while pskr/filter/v2raw/# is the whole firehose.
A gap in PSK Reporter’s own stream, for instance when the ingest service has to reconnect, is not filled in afterwards. The spots from those seconds don’t appear on the feed.