The Open Signal API
Three public JSON endpoints carrying what the amateur radio network is doing right now: who is on the air, the pictures they have sent over radio, and the numbers behind both. No key, no account, and open CORS — the same service the desktop software reads.
The base URL
https://opensignal-processor.2e0jwr.co.uk/api/v1Three endpoints, all GET, all returning JSON. Nothing to sign up for and nothing to send but the request.
/api/v1/map— who is on the air now/api/v1/photos— the latest pictures sent over radio/api/v1/stats— aggregate network figures and records
One thing to get straight first
This is the picture service, on its own host. It is not the same thing as the API on this website — that one is a separate Model Context Protocol server over the archive, and it holds different data. If you want live network state, you want this page. If you want an AI agent to query the archive, you want that one.
Times are milliseconds UTC. ageSeconds is computed server-side, so it is immune to your clock being wrong.
GET /api/v1/map
Live presence: every station currently on the air, the contacts in progress between them, and stations heard recently but now quiet. Cached 30 seconds.
{
"generatedUtc": 1787856269752,
"stations": [
{
"call": "G4RHD",
"grid": "io93id",
"lat": 53.146, "lon": -1.375,
"freq": 144550000,
"band": "2m",
"mode": "hybrid",
"with": "M0IZM",
"act": "qso",
"ageSeconds": 12
}
],
"links": [
{ "a": "G4RHD", "b": "M0IZM", "act": "qso", "km": 22 }
],
"recent": [
{
"call": "M8IFY", "grid": "io93if",
"lat": 53.19, "lon": -1.32,
"band": "2m", "mode": "hybrid",
"lastSeenUtc": 1787856100000
}
]
}Fields worth knowing
act—"tx"transmitting,"qso"in contact,""merely present.with— the callsign being worked. Empty when calling or idle.latandloncan benull— a station that has never set a locator has no position. They are also Maidenhead square centres, not exact positions, so treat them as accurate to a few kilometres.
That null is the one that will catch you. List a station without a locator; do not try to map it.
GET /api/v1/photos
The most recent pictures put on the air, newest first. Takes an optional ?limit=: the default is 24 and the maximum is 120 — ask for more and you get 120 rather than an error. Cached 60 seconds.
GET /api/v1/photos?limit=2
{
"generatedUtc": 1787856269863,
"condensed": true,
"pictures": 2,
"items": [
{
"serial": "A62D26",
"url": "https://…/f/g/45b1ba54f38c.jpg",
"thumbUrl": "https://…/f/g/45b1…jpg?w=480",
"pageUrl": "https://…/p/A62D26",
"originalUrl": "https://…?original=1",
"blurDataUrl": "data:image/…",
"name": "de 2E0MBH 07/23/2026",
"sender": "M7GYN",
"receivedBy": ["M0IZM", "M6YJB"],
"copies": 2,
"call": "M7GYN",
"dir": "tx",
"utc": 1787850000000,
"ageSeconds": 6269,
"freq": 144550000,
"band": "2m",
"mode": "hybrid"
}
]
}Condensing, and optional fields
One transmission is stored more than once — the sender’s copy and each receiver’s copy, under different serials. condensed: true means those have been merged, so items is a list of transmissions rather than of stored files. copies tells you how many were folded together.
thumbUrl,pageUrl,originalUrl,blurDataUrl— present on every item at the time of writing.altTextandimageUrlare frequently absent. They are generated at publish time, so older pictures have neither. Check for them; never assume them.- Any picture URL takes
?w=and is resized on demand, snapping to the nearest of 160, 320, 480, 960 or 1440. It returns AVIF where the browser accepts it — a 480px thumbnail is roughly a tenth the bytes of the original.
GET /api/v1/photos/{serial}
One picture, with every station that reported copying it — the reception reports behind the signal figures on the live page.
{
"serial": "1824AD",
"url": "https://…/f/g/….jpg",
"name": "DSC01118",
"utc": 1787840000000,
"band": "2m",
"mode": "hybrid",
"copies": 3,
"sender": {
"call": "M0IZM", "grid": "IO92KX",
"lat": 52.94, "lon": -1.19
},
"receptions": [
{
"call": "M6YJB", "grid": "IO93IC",
"lat": 53.16, "lon": -1.37,
"snrDb": 17.2, "km": 18,
"mode": "hybrid",
"utc": 1787840012000
}
]
}The trap in this endpoint
sender is null for anything copied off the air rather than transmitted through Open Signal — which is every analogue SSTV picture. It is not an error and it is not rare: roughly half the pictures sampled while writing this page returned it.
This site read .lat off that null and a whole modal stopped rendering. Normalise it on the way in rather than discovering it in production.
receptionsmay be an empty array — a picture nobody reported copying.- A reception’s
lat/lonare null when that operator has no locator. List them, do not map them. - An unknown serial returns 400, not 404.
GET /api/v1/stats
Aggregate figures, in blocks: live, today, week, allTime, records, topStations, activityByDay and service. Cached 60 seconds.
{
"live": { "stationsOnAir": 7, "qsosInProgress": 0,
"bands": { "2m": 7 }, "modes": { … } },
"today": { "activeStations": 4, "picturesShared": 26, … },
"week": { "meanSnrDb": 14.5, "newStations": 1, … },
"allTime": { "stationsSeen": 13, "picturesShared": 1365, … },
"records": {
"longestReceptionEver": {
"sender": "M0IZM", "receiver": "G0GOO", "km": 51
}
}
}Two cautions
- Everything under
recordsis optional. Only the records that exist are present, so read each one defensively rather than assuming the shape. service.imagesServedTodayandservice.imagesServedTotalare HTTP download counters, not radio activity. They count image files fetched over the web — the website loading its own thumbnails inflates them. Never present them as a count of pictures sent over the air; next to a figure like “on the air now” they read as exactly that.
Writing a client that survives
- Check array lengths, not status codes. The service does not return 5xx — a backend fault degrades to an empty but valid document. A client watching for errors will see 200 and believe the band is dead.
- Respect the cache headers. 30 seconds on the map, 60 on the others, and the upstream data only changes on that cadence anyway. Polling faster buys no freshness.
- Range-check frequencies. The feed carries the occasional unit slip —
144550000000Hz for 144.550 MHz. Anything outside roughly 100 kHz to 3 GHz is worth discarding rather than guessing at.
- Modes are missing on off-air copies. Analogue SSTV copied off the air arrives with an empty
mode; the mode is often recoverable from the filename, which usually carries it. "partial NN"in a name means an incomplete decode — the picture arrived, but not all of it.- Set a timeout. This site uses six seconds and falls back to an empty document, which is why a slow upstream never becomes a slow page.
Common questions
Do I need an API key?
No. All three endpoints are open GET requests with no authentication, no key and no registration. They also send Access-Control-Allow-Origin: *, so you can call them straight from a browser without a proxy.
Is it free to use?
Yes. It is the same service the Open Signal software and this website read, and there is no charge or quota for reading it. Please respect the cache headers rather than polling every second — the underlying data only changes every 30 to 60 seconds anyway, so faster polling costs you nothing and costs the service everything.
Are there rate limits?
None are published or enforced today. That is not a promise — it is a small service run by one operator, and sustained heavy polling would be dealt with rather than absorbed. Cache what you fetch and you will never notice a limit.
Is the API stable? Can I depend on it?
It is versioned at /api/v1, but it is a product backend rather than a contract-frozen public API, and fields have been added to it as recently as August 2026. Read defensively: check that arrays exist before iterating, and treat any field marked optional here as genuinely optional.
What happens when the service has a problem?
It does not return 5xx. A backend fault degrades to an empty but structurally valid document, so a consumer that checks status codes will see 200 and think all is well. Check array lengths instead — that is how every client on this site is written.
What could I build with it?
A club display showing who is on the air, a desk gadget like the microradar aircraft scope, a Grafana panel of network activity, or an alerting script for when a particular station appears. The map endpoint gives live presence, photos gives the pictures themselves, and stats gives the aggregate figures.
More about Open Signal
See the API rendered
The live network page is built entirely from these three endpoints — the map, the gallery and every figure on it. It is the working example.