# ARC6.6 API developer reference API v1 developer reference. Reviewed October 29, 2026. This reference describes the current AR-Cluster 6.6 read-only API. For release availability and changes, consult the download page and release notes. Prefix-filter suggestion: Tibi HA8TKS; implementation and checks: NG7M. Reference preparation and testing: NG7M. Original WebCluster: Alan Rovner K7AR. This describes the server API, independently of the WebCluster presentation. ## Start here The API is optional and read-only. It starts disabled with address `127.0.0.1`, port `3994`, HTTP, and no credential. Enable it on your own test instance through Server Setup / Read-only API, generate a token, and save. Give every instance a separate port and token. Existing nodes can apply API settings without restarting the entire node; an API bind failure does not intentionally stop cluster service. See the [public sysop setup guide](https://arcluster.org/sysop-guide/#read-only-api). A non-administrator runtime account may need an administrator to reserve the exact HTTP.sys prefix for that account. Example only; replace host/account/port: ```powershell netsh http add urlacl url=http://127.0.0.1:3994/api/v1/ user=HOST\ArcRuntimeUser listen=yes delegate=no ``` Use an explicit IP literal. Non-loopback requires HTTPS and operator-provisioned certificate/firewall configuration; ARC does not set those up automatically. Token storage uses Windows DPAPI for the ARC runtime account. Copying configuration to a different machine/account is not a credential-transfer mechanism; deliberately configure or rotate the destination credential. No shared credential is in this kit. ## Authentication and first request Send `Authorization: Bearer ` on every GET. Preserve the token's exact case. Credentials belong in a trusted server-side consumer, never a URL, browser JavaScript, cookies, screenshots or logs. Requests containing an Origin header are refused even with a valid token; there is no browser CORS interface. These PowerShell examples assume your credential was supplied securely in the process environment. Do not paste a real token into shared examples or shell history. ```powershell $base = 'http://127.0.0.1:3994/api/v1/' $headers = @{ Authorization = 'Bearer ' + $env:ARC_API_TOKEN } $status = Invoke-RestMethod -Uri ($base + 'status') -Headers $headers $status | Select-Object apiVersion, callsign, instanceId, softwareVersion, scope ``` Check `apiVersion=1`, `scope=read`, and the expected callsign/instanceId before using results. The stable instanceId distinguishes nodes on a multi-instance host. All paths and parameter names below are case-sensitive and relative to `/api/v1/`. ## Endpoints | GET path | Purpose | | --- | --- | | `status` | API version, callsign, stable instanceId, softwareVersion, startedUtc, capturedUtc and fixed read scope. | | `recent-spots` | Live receiver reports from the retained DX cache, including memory-only skimmer duplicates; use this for receiver comparison and recent maps. | | `spots` | Committed SQLite spot history; persistence and duplicate admission mean it is not the raw receiver stream. | | `spotters` | Recently heard reporting identities in the retained cache, with source/geography and optional receiver location. | | `propagation` | Latest four WWV and twelve WCY reports, with original text/numeric fields and attribution. | `status`, `spotters` and `propagation` accept no query parameters. There are no writes, spot injection, administration, configuration or token-export routes. Clients may request `Accept-Encoding: gzip`. Successful responses larger than4096 uncompressed bytes use fast gzip only when the result is smaller. Small responses and errors remain plain JSON. No header or `gzip;q=0` retains plain JSON; explicit refusal overrides wildcard acceptance. Gzip replies include `Content-Encoding: gzip`; `Content-Length` is the transmitted size. Replies carry `Vary: Accept-Encoding`. The1 MiB limit still applies before compression. Enable automatic decompression in your HTTP library before parsing JSON, e.g. `HttpWebRequest.AutomaticDecompression = DecompressionMethods.GZip` in .NET. Existing consumers do not need to change unless they want compression. JSON responses are UTF-8 with no-store caching policy. HTML-encode reported strings when displaying them in a website; radio comments are external input. ## Shared spot query parameters Both spot routes accept the following. Do not send a parameter more than once. | Parameter | Meaning | | --- | --- | | `limit` | 1-200 rows per page; default100. | | `offset` | 0-4800 matching rows; default0. Normally increment by your page size. | | `callsign` | One exact full heard/transmitting callsign, case-insensitive; maximum32 characters. Its existing exact behavior is unchanged. | | `callsignPrefixes` | Up to32 distinct comma-separated literal heard/transmitting callsign prefixes, each1-32 characters, total value at most256 characters. Match any prefix, case-insensitively; no wildcards or regex. Surrounding ASCII spaces are accepted without changing matching or snapshot identity. | | `source` | `human` or `skimmer`. Automated flags and reporting calls ending -# count as skimmer. | | `sinceUtc` | Inclusive reported-time cutoff, UTC ISO timestamp ending Z; seconds and up to7 fractional digits. This is timeUtc, not receivedUtc. | | `minKhz`, `maxKhz` | Inclusive finite positive decimal kHz; maximum100000000. Minimum cannot exceed maximum. | | `storedModes` | Comma-separated exact stored engine mode labels. | | `modes` | CW,RTTY,SSB,FT4,FT8,FT (both FT modes),OTHER. Uses effective/display mode. | | `bands` | 160,80,60,40,30,20,17,15,12,10,6,4,2,1.25,70CM,33CM,23CM; inclusive band windows. | | `spotterContinents` | AF,AN,AS,EU,NA,OC,SA. | | `spotterCountries` | Comma-separated reporting-station DXCC prefix codes, such as K,VE,DL; country names/numeric DXCC IDs are not accepted as substitutes. | | `excludeCountries` | Exclude heard-station DXCC prefix codes, such as K,VE. | | `distinctSkimmers` | true/false, defaultfalse. Collapse skimmers by heard call/effective mode/band, across receivers; human reports are not collapsed. Keep false for receiver comparisons. | Generic lists allow at most32 distinct codes. Values are at most256 characters except recent `spotters` (512); requests allow16 parameters and2048 path/query characters. Unknown, repeated, malformed or oversized parameters are rejected. Lists express alternatives; different filters intersect. An empty list is not a wildcard: omit an unwanted filter. A `#` in a callsign must be URL-encoded as `%23`. For example, `recent-spots?callsignPrefixes=H4,7T,C5,S9&modes=CW` selects CW reports whose heard callsign starts with any of those four strings. Both spot routes accept the new filter. Prefixes are literal callsign text, not DXCC country selections: portable/special calls follow the text as reported. If `callsign` is also supplied, the exact callsign must satisfy the prefix list as well. Other filters intersect normally. Empty entries, internal spaces, wildcards, regex and repeated parameters are rejected; surrounding ASCII spaces are accepted as described above. Allowed characters are letters, digits, `/`, `#` and `-`, as for exact callsigns. Encode `#` as `%23`. Filtering precedes pagination and the recent matching-window limit. The committed `spots` route retains its existing latest5,000-history-row scan bound; prefixes do not enable a full-history scan. Snapshot selection includes the normalized prefix set; changing prefixes requires a new snapshot. Omit optional parameters that the server does not support. ## Recent-only parameters and paging These parameters are accepted on `recent-spots`, not on `spots`: | Parameter | Meaning | | --- | --- | | `spotters` | Up to12 exact comma-separated reporting identities, each at most32 characters; preserve receiver suffixes, including -#. | | `spotterStates` | Up to64 US state/DC and Canadian province/territory two-letter codes; example MT,UT,BC,AB. | | `spotSources` | HUMAN,CW,FT. CW includes CW/RTTY skimmers, FT includes FT4/FT8 skimmers; human mode is still selected by modes. | | `cwSkimValid` | true/false. True requires the node SkimValid flag on CW/RTTY skimmers; false leaves all quality levels eligible. Human/FT eligibility is unchanged. This does not subscribe to a source by itself. | | `snapshot` | The32-character lowercase hex ID returned by the first page. Pin it for subsequent pages using exactly the same selection filters. | ```powershell $since = [DateTime]::UtcNow.AddMinutes(-15).ToString('yyyy-MM-ddTHH:mm:ssZ') $receivers = [uri]::EscapeDataString('NG7M-#,KM7W-#') $selection = 'source=skimmer&spotSources=CW&modes=CW&spotters=' + $receivers + '&sinceUtc=' + [uri]::EscapeDataString($since) $page = Invoke-RestMethod -Uri ($base + 'recent-spots?limit=200&' + $selection) -Headers $headers $page.spots | Select-Object callsign, spotter, frequencyKhz, timeUtc, receivedUtc, skimValid if ($page.hasMore) { $next = Invoke-RestMethod -Uri ($base + 'recent-spots?limit=200&offset=200&snapshot=' + $page.snapshot + '&' + $selection) -Headers $headers } ``` Receiver names are illustrative; a selected receiver may not be active on your node. Continue while hasMore, keeping the same snapshot and selection, within offset bounds. Snapshots can be reused for5 seconds, expire after30 seconds and share an 8-entry capacity. Eviction can expire a cursor early. On a400 expired/evicted or mismatched snapshot, restart from page0; do not mix pages from different snapshots. `recent-spots` filters before selecting at most5000 matching reports. Responses include `history=recent`, `windowScope=matching`, `snapshot`, `capturedUtc`, `windowLimit=5000`, `windowFull`, `hasMore` and `spots`. Results are newest by receivedUtc. The row bound remains even if you request a longer time period. A full window signals a reached bound, not proof of a complete historical interval. Recent-cache scans run outside the shared snapshot-index lock. The receive-table gate and overall scan budgets below apply. Frequency/band prefiltering avoids copying unrelated rows; this adds no history merge. The live cache includes receiver copies suppressed from committed history. It is retained according to the node's CacheMinutes setting and can be shorter after restart/warmup or under normal admission rules. A two-hour query cannot manufacture two hours of available data. This is not a durable RBN packet archive. `spots` instead examines at most the latest5000 committed rows before filtering, sets `history=committed`, and has no stable snapshot cursor. New commits can shift offset pages. Database retention above5000 does not expand that API scan. Do not use this route to estimate every receiver's raw reports. APIs do not force a flush or merge cache/database rows into a new cross-source history. ## Sent, received and comparison semantics For Received by, query `spotters=`. For Sent by, query `callsign=`. For Both, issue the two selections and union results in your consumer. There is no `direction`, `sentBy` or `reportDirection` parameter. The API never silently changes a reporting callsign into its base/transmitting call. The maintained website strips only trailing -# for its transmitting selection; that is website policy, not an API alias rule. Preserve receiver-specific SSIDs. For comparison keep `distinctSkimmers=false` and group latest reports by exact spotter + heard callsign + effective mode + band if desired. A skimValid=false report is not the same thing as skimmerDuplicate=true. Frequent repeated reports can be distinct received observations; the consumer decides how to display them. The maintained WebCluster offers All spots, Validated spots, and Validated, non-duplicate spots for CW/RTTY skimmers. The third requires skimValid=true and skimmerDuplicate=false in consumer selection. There is no skimDupe/cwSkimDupe query parameter; distinctSkimmers is grouping, not duplicate-flag filtering. ## Report and location fields Spot objects expose `callsign`, `spotter`, `spotterNode`, `frequencyKhz`, `timeUtc` (report UTC), `receivedUtc` (node receive UTC), `country` (heard-station DXCC prefix), `spotterCountry`, `spotterContinent`, `spotterState`, `comment`, `storedMode`, `mode`, `source`, `skimmerDuplicate` and `skimValid`. Optional `grid` is the heard-station Maidenhead value when available; it is not automatically the spotter's grid. Optional `spotterLocation` contains `grid`, `latitude`, `longitude`, `source`, `precision`, `approximate` and optional `updatedUtc`. Coordinates use north-positive latitude/east-positive longitude. Directory entries use `location` with the same shape. Missing coordinates/metadata are permitted, including when a bounded metadata lookup times out; do not substitute latitude0/longitude0. A grid centre represents an area, not an exact antenna position. Location order: exact sysop receiver-grid override; RBN receiver grid for skimmers with exact identity preference and safe metadata aliases; exact saved station profile grid; approximate FCC ZIP-area grid. The Canadian importer has no automatic postal geocoder. No licensee mailing addresses or names are exposed by the API. Optional instance Config files `SpotterLocations.csv` and `RbnSpotterLocations.csv` contain two columns CALL,GRID, support4/6/8-character Maidenhead values and # comment lines; they refresh within about2 minutes. File limits are2 MiB/8192 lines. These optional operator-supplied files are not live-directory downloads performed by the API. The installer does not include NC7J-3's receiver snapshot or configuration. `spotters` returns `capturedUtc`, `limit=2048`, `truncated` and `spotters`. Entries contain `callsign`, `lastSeenUtc`, comma-separated `sources` (HUMAN,CW,FT), `country`, `continent`, `state`, and optional location. At most512 entries per response receive optional location enrichment; identities remain available up to the directory cap. Treat unknown JSON fields as additive and optional location fields as optional. ## Errors, budgets and consumer behavior | Status | Meaning | | --- | --- | | 400 | Invalid query/body or expired/evicted/mismatched recent snapshot. | | 401 | Missing/invalid credential; Bearer challenge. | | 403 | Browser Origin denied. | | 404 | Unsupported route. | | 405 | Only GET supported. | | 414 | Oversized request path/query. | | 503 | Temporarily unavailable: scan/gate/storage/serialization/admission bounds can be reached. | Application errors are small JSON objects such as `{"error":"temporarily_unavailable"}`. HTTP.sys can reject malformed/over-capacity/expired requests outside that JSON path. Responses are limited to1 MiB. There are four dedicated API workers, a bounded recent-cache scan (two-second scan/100ms receive-table gate acquisition), and a five-second response-abort timer. Optional location enrichment has a750ms page budget. These are service bounds, not a whole-network latency guarantee. On transient503, back off and retry; do not hammer the node or silently present partial data as complete. Use modest page sizes, short time windows and exact receiver/callsign filters. Cache completed views briefly, bound your own overall time/request/memory budget, stop on hasMore=false, and disclose a full window or other consumer cap. The current website defaults to15 minutes, offers at most2 hours, allows a1000-pair map cap and labels partial coverage; those presentation limits are not extra API parameters. The API exposes neither connected-user/session details nor administrator controls. RBN ingestion remains read-only and -# reports are never forwarded to cluster peers.