Public API

Everything on the air, as JSON.

Nodes, repeaters, DMR ids, licensing records, what clubs publish and who is on the air right now. Read-only, no key, no sign-up: 120 requests a minute per address, CORS open, answers cacheable for a minute.

Import that file into Postman, Insomnia, Bruno or any OpenAPI 3.1 tool and every endpoint below arrives with its parameters. Base URL https://api.qsoapp.com; staging answers at https://staging-api.qsoapp.com.

Try it

Every box runs the request from this page and shows the answer. Fill in the parameters, press Run, and copy the curl line when it is what you want.

On the air

Nodes, repeaters, DMR, presence and signal reports

GET /v1/nodes · AllStarLink nodes near a point, by place, or by callsign

AllStarLink publishes no coordinate for a node, only the text its owner typed, so place is how most nodes are found.

  • near · lat,lon; nearest first, each row carries distanceKm and distanceMi
  • km · Radius in km
  • mi · Radius in miles, instead of km
  • limit · Rows
  • callsign · The callsign's rows instead of a radius
  • place · Match the location text and description instead of a radius
  • online · 1 or true keeps only what the live feed still lists. A repeater somebody here contributed reports to no network and is never hidden by it.
curl "https://api.qsoapp.com/v1/nodes"
200: nodes, with distanceKm when near is given
GET /v1/nodes/{number} · One node: callsign, frequency, tone, status, location
  • number · AllStarLink node number
curl "https://api.qsoapp.com/v1/nodes/%E2%80%A6"
200: node · 404: Unknown
GET /v1/repeaters · Repeaters near a point, or by callsign

Submitted by trustees and operators, plus BrandMeister's DMR repeaters.

  • near · lat,lon; nearest first, each row carries distanceKm and distanceMi
  • km · Radius in km
  • mi · Radius in miles, instead of km
  • limit · Rows
  • callsign · The callsign's rows instead of a radius
  • online · 1 or true keeps only what the live feed still lists. A repeater somebody here contributed reports to no network and is never hidden by it.
curl "https://api.qsoapp.com/v1/repeaters"
200: repeaters, with distanceKm when near is given
GET /v1/repeaters.chirp.csv · The same search as a CHIRP memory file

Analogue FM only: CTCSS as Tone or TSQL, DCS as DTCS, digital-only machines left out. File then Import in CHIRP.

  • near · lat,lon; nearest first, each row carries distanceKm and distanceMi
  • km · Radius in km
  • mi · Radius in miles, instead of km
  • limit · Rows
curl "https://api.qsoapp.com/v1/repeaters.chirp.csv"
200: text/csv, as an attachment
GET /v1/pota/parks · Parks on the Air near a point

From POTA's public API, cached a day per area. The radius is capped at 200 km.

  • near · lat,lon; nearest first, each row carries distanceKm and distanceMi
  • km · Radius in km
  • mi · Radius in miles, instead of km
  • limit · Rows
curl "https://api.qsoapp.com/v1/pota/parks"
200: parks, nearest first · 502: POTA did not answer
GET /v1/pota/spots · Activators spotted right now

Cached a minute. Without near, every current spot.

  • near · lat,lon; nearest first, each row carries distanceKm and distanceMi
  • km · Radius in km
  • mi · Radius in miles, instead of km
  • limit · Rows
curl "https://api.qsoapp.com/v1/pota/spots"
200: spots: callsign, park, frequency, mode, time · 502: POTA did not answer
GET /v1/satellites/passes · When an amateur satellite crosses your sky

SGP4 over Celestrak's amateur element sets, cached twelve hours, for the FM satellites a handheld can work. Each pass carries where it rises, how high it climbs, where it sets, and the uplink, downlink and tone that go in the radio.

  • near · lat,lon; required
  • hours · How far ahead to look
  • minElevation · Skip passes that stay lower than this, in degrees
curl "https://api.qsoapp.com/v1/satellites/passes"
200: passes, soonest first · 502: Celestrak did not answer
POST /v1/reach · Terrain line of sight from one point to up to 25 sites

2 m at the person and 20 m at a site unless given, over 4/3-earth curvature. Its own rate limit: 30 calls a minute per address, not the 120 the rest of /v1 allows.

curl -X POST "https://api.qsoapp.com/v1/reach" \
  -H "content-type: application/json" -d '{ … }'
Takes a JSON body, so it is documented here rather than run from the page.200: One result per target: clear, marginal (under 30 m in the way), blocked, or unknown when the elevation tiles are missing · 400: from and a non-empty to are both required · 429: Rate limited
GET /v1/repeaters/{id} · One repeater
  • id · Repeater id
curl "https://api.qsoapp.com/v1/repeaters/%E2%80%A6"
200: repeater · 404: Unknown
GET /v1/dmr/{callsign} · A callsign's DMR ids, from RadioID
  • callsign · Callsign
curl "https://api.qsoapp.com/v1/dmr/%E2%80%A6"
200: callsign and ids · 502: RadioID unavailable
GET /v1/presence · Who has said they are around today, by grid square

Opt-in only, four-character Maidenhead grid, cleared nightly.

  • grid · Maidenhead grid, four characters (FN30)
  • near · lat,lon, resolved to a grid square
curl "https://api.qsoapp.com/v1/presence"
200: callsigns present · 400: grid or near required
GET /v1/reports/{kind}/{id} · Signal reports left on a repeater, a node or a callsign
  • kind · repeater, node or callsign
  • id · Repeater id, node number or callsign
curl "https://api.qsoapp.com/v1/reports/%E2%80%A6/%E2%80%A6"
200: reports · 400: Unknown kind

Callsigns

The licensing mirror

GET /v1/lookup/{callsign} · Licensing record (US, Canada) and the callsign's nodes
  • callsign · Callsign
curl "https://api.qsoapp.com/v1/lookup/%E2%80%A6"
200: records and nodes · 404: Unknown

Clubs

What a club publishes

GET /v1/clubs · Every club on QSO App
curl "https://api.qsoapp.com/v1/clubs"
200: clubs
GET /v1/clubs/{slug} · A club's public page: name, callsign, where it meets, nets
  • slug · Club slug or club callsign
curl "https://api.qsoapp.com/v1/clubs/%E2%80%A6"
200: club · 404: Unknown
GET /v1/clubs/{slug}/events · A club's upcoming events
  • slug · Club slug
curl "https://api.qsoapp.com/v1/clubs/%E2%80%A6/events"
200: events · 404: Unknown
GET /v1/clubs/{slug}/events.ics · The same events as an iCalendar feed to subscribe to
  • slug · Club slug
curl "https://api.qsoapp.com/v1/clubs/%E2%80%A6/events.ics"
200: text/calendar · 404: Unknown
GET /v1/clubs/{slug}/posts · A club's public announcements, minutes and agendas
  • slug · Club slug
curl "https://api.qsoapp.com/v1/clubs/%E2%80%A6/posts"
200: posts · 404: Unknown
GET /v1/clubs/{slug}/dues · A club's dues plans
  • slug · Club slug
curl "https://api.qsoapp.com/v1/clubs/%E2%80%A6/dues"
200: plans · 404: Unknown
GET /v1/clubs/{slug}/elections · A club's elections and their results
  • slug · Club slug
curl "https://api.qsoapp.com/v1/clubs/%E2%80%A6/elections"
200: elections · 404: Unknown
GET /v1/nets/open · Every club net on the air right now
curl "https://api.qsoapp.com/v1/nets/open"
200: nets, each with its club, frequency and net control
GET /v1/school-programs · School programmes clubs run, with the club
curl "https://api.qsoapp.com/v1/school-programs"
200: programmes

First contact

The guided path's own content

GET /v1/radios · The handheld profiles behind "pick your radio"

Authored in the repository under content/radios, CC BY-SA 4.0. Each profile carries the keys by their printed names, how this radio changes a setting, and the steps a new operator does in order.

curl "https://api.qsoapp.com/v1/radios"
200: radios, by name
GET /v1/radios/{slug} · One radio profile
  • slug · Radio slug, as listed by /v1/radios
curl "https://api.qsoapp.com/v1/radios/%E2%80%A6"
200: radio · 404: Unknown
GET /v1/path/prompts · The Part 97 prompts a first contact touches

Authored in the repository at content/path/regulatory.md, CC BY-SA 4.0. One source, so the wording a new operator is held to is the same everywhere it is shown.

curl "https://api.qsoapp.com/v1/path/prompts"
200: prompts, in the order they come up

First year

A shared milestone

GET /v1/milestones/{id} · A shared first, as its owner chose to share it
  • id · Share id from a /m/ link
curl "https://api.qsoapp.com/v1/milestones/%E2%80%A6"
200: milestone · 404: Unknown

What is not here

Anything that needs a signed-in operator sits outside /v1 and is not a public API: the requests board, the club's members, dues ledgers, the net room. Where a point comes from, what each source allows and what is deliberately left out is on the Data-API page of the wiki. Assistants that speak the Model Context Protocol get the same data through the MCP gateway.

AllStarLink node data from allstarlink.org · DMR repeater data from BrandMeister · place coordinates from the US Census Bureau gazetteer · licensing records from the FCC and ISED Canada. The API is part of the platform, AGPL-3.0.