API Endpoints Reference
Complete reference for all Vail ReRBN API endpoints with parameters, examples, and response formats.
GET /spots
Search for spots with optional filters. This is the primary endpoint for querying spot data.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
call |
string | — | Callsign to search (partial match, case-insensitive) |
spotter |
string | — | Filter by spotter/skimmer callsign |
mode |
string | — | Mode filter: CW, RTTY, FT8, FT4 (comma-separated) |
band |
string | — | Amateur band: 160m, 80m, 60m, 40m, 30m, 20m, 17m, 15m, 12m, 10m, 6m |
since |
timestamp | 1 hour ago | Start time (Unix timestamp or ISO 8601) |
until |
timestamp | now | End time (Unix timestamp or ISO 8601) |
limit |
integer | 100 | Max results (1-1000) |
offset |
integer | 0 | Pagination offset |
after_id |
integer | — | Return spots with ID > this value (for real-time polling) |
Response Fields
| Field | Type | Description |
|---|---|---|
id |
integer | Unique spot identifier |
timestamp |
string | ISO 8601 timestamp |
spotter |
string | RBN skimmer callsign |
spotter_grid |
string | null | Maidenhead grid of the skimmer (e.g., "FM19"). Null if unknown. |
callsign |
string | Spotted station (DX) callsign |
grid |
string | null | Maidenhead grid of the spotted station (e.g., "FN31pr"). Null if unknown. |
frequency |
number | Frequency in kHz |
mode |
string | Mode: CW, RTTY, FT8, FT4, etc. |
snr |
integer | Signal-to-noise ratio in dB |
wpm |
integer | null | Words per minute (CW only) |
Gridsquares are looked up from HamDB.org. Coverage is best for US callsigns.
International callsigns may return null. New callsigns are queued for
background lookup and will be available on subsequent requests.
Example
curl "https://vailrerbn.com/api/v1/spots?call=W1AW&mode=CW&band=20m"
import requests
response = requests.get(
"https://vailrerbn.com/api/v1/spots",
params={
"call": "W1AW",
"mode": "CW",
"band": "20m"
}
)
data = response.json()
print(f"Found {data['total']} spots")
for spot in data["spots"]:
grid = spot.get('grid', 'Unknown')
print(f"{spot['callsign']} ({grid}) on {spot['frequency']} kHz")
const params = new URLSearchParams({
call: 'W1AW',
mode: 'CW',
band: '20m'
});
const response = await fetch(
`https://vailrerbn.com/api/v1/spots?${params}`
);
const data = await response.json();
console.log(`Found ${data.total} spots`);
data.spots.forEach(spot => {
const grid = spot.grid || 'Unknown';
console.log(`${spot.callsign} (${grid}) on ${spot.frequency} kHz`);
});
<?php
$params = http_build_query([
'call' => 'W1AW',
'mode' => 'CW',
'band' => '20m'
]);
$response = file_get_contents(
"https://vailrerbn.com/api/v1/spots?$params"
);
$data = json_decode($response, true);
echo "Found {$data['total']} spots\n";
foreach ($data['spots'] as $spot) {
echo "{$spot['callsign']} on {$spot['frequency']} kHz\n";
}
require 'net/http'
require 'json'
uri = URI('https://vailrerbn.com/api/v1/spots')
uri.query = URI.encode_www_form({
call: 'W1AW',
mode: 'CW',
band: '20m'
})
response = Net::HTTP.get(uri)
data = JSON.parse(response)
puts "Found #{data['total']} spots"
data['spots'].each do |spot|
puts "#{spot['callsign']} on #{spot['frequency']} kHz"
end
Find recent spots for W1AW on the 20-meter band in CW mode
GET /spots/:callsign
Get all spots for a specific callsign (exact match).
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
hours |
integer | 24 | Time range in hours |
mode |
string | — | Filter by mode |
Example
curl "https://vailrerbn.com/api/v1/spots/W1AW?hours=48"
import requests
callsign = "W1AW"
hours = 48
response = requests.get(
f"https://vailrerbn.com/api/v1/spots/{callsign}",
params={"hours": hours}
)
data = response.json()
print(f"Found {data['total']} spots for {callsign} in last {hours} hours")
for spot in data["spots"]:
spotter_grid = spot.get('spotter_grid', '????')
print(f"{spot['timestamp']} - {spot['spotter']} ({spotter_grid}) - {spot['frequency']} kHz - {spot['mode']} - {spot['snr']} dB")
const callsign = 'W1AW';
const hours = 48;
const response = await fetch(
`https://vailrerbn.com/api/v1/spots/${callsign}?hours=${hours}`
);
const data = await response.json();
console.log(`Found ${data.total} spots for ${callsign} in last ${hours} hours`);
data.spots.forEach(spot => {
const spotterGrid = spot.spotter_grid || '????';
console.log(`${spot.timestamp} - ${spot.spotter} (${spotterGrid}) - ${spot.frequency} kHz - ${spot.mode} - ${spot.snr} dB`);
});
<?php
$callsign = "W1AW";
$hours = 48;
$url = "https://vailrerbn.com/api/v1/spots/$callsign?" . http_build_query(['hours' => $hours]);
$response = file_get_contents($url);
$data = json_decode($response, true);
echo "Found {$data['total']} spots for $callsign in last $hours hours\n";
foreach ($data['spots'] as $spot) {
echo "{$spot['timestamp']} - {$spot['frequency']} kHz - {$spot['mode']} - {$spot['snr']} dB\n";
}
require 'net/http'
require 'json'
callsign = 'W1AW'
hours = 48
uri = URI("https://vailrerbn.com/api/v1/spots/#{callsign}")
uri.query = URI.encode_www_form({ hours: hours })
response = Net::HTTP.get(uri)
data = JSON.parse(response)
puts "Found #{data['total']} spots for #{callsign} in last #{hours} hours"
data['spots'].each do |spot|
puts "#{spot['timestamp']} - #{spot['frequency']} kHz - #{spot['mode']} - #{spot['snr']} dB"
end
Retrieve all spots for W1AW in the past 48 hours
GET /activity
Batch activity check for multiple callsigns in a single request. Returns whether each callsign has been spotted, along with spot count and latest spot details. Designed for monitoring a list of callsigns without making individual API calls.
If you're monitoring 50-100 callsigns, using /spots/:callsign for each one means 50-100 separate HTTP requests. This endpoint handles all of them in a single request with a single efficient database query. Much faster for both you and the server.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
call |
string | required | Comma-separated callsigns (max 100). Example: W1AW,K1TTT,N3LLO |
hours |
integer | 1 | Time window in hours (1-168) |
Response Fields
| Field | Type | Description |
|---|---|---|
callsigns[].callsign |
string | The callsign (uppercase) |
callsigns[].spotted |
boolean | true if at least one spot exists in the time window |
callsigns[].count |
integer | Number of spots in the time window (0 if not spotted) |
callsigns[].last_spotted |
string | null | ISO 8601 timestamp of the most recent spot |
callsigns[].last_frequency |
number | null | Frequency (kHz) of the most recent spot |
callsigns[].last_mode |
string | null | Mode of the most recent spot (CW, FT8, etc.) |
callsigns[].last_snr |
integer | null | SNR (dB) of the most recent spot |
summary.requested |
integer | Total callsigns requested |
summary.spotted |
integer | Number of callsigns that were spotted |
summary.not_spotted |
integer | Number of callsigns with no spots |
Example Response
{
"callsigns": [
{
"callsign": "W1AW",
"spotted": true,
"count": 12,
"last_spotted": "2026-02-17T15:32:00.000Z",
"last_frequency": 14025.3,
"last_mode": "CW",
"last_snr": 25
},
{
"callsign": "K1TTT",
"spotted": false,
"count": 0,
"last_spotted": null,
"last_frequency": null,
"last_mode": null,
"last_snr": null
}
],
"summary": {
"requested": 2,
"spotted": 1,
"not_spotted": 1
},
"hours": 1
}
Example
# Check 5 callsigns in a single request
curl "https://vailrerbn.com/api/v1/activity?call=W1AW,K1TTT,N3LLO,WB9Z,VE3EN&hours=1"
import requests
# List of callsigns to monitor
callsigns = ["W1AW", "K1TTT", "N3LLO", "WB9Z", "VE3EN"]
response = requests.get(
"https://vailrerbn.com/api/v1/activity",
params={
"call": ",".join(callsigns),
"hours": 1
}
)
data = response.json()
print(f"{data['summary']['spotted']}/{data['summary']['requested']} callsigns active")
for entry in data["callsigns"]:
if entry["spotted"]:
print(f" {entry['callsign']}: {entry['count']} spots, "
f"last on {entry['last_frequency']} kHz ({entry['last_mode']})")
else:
print(f" {entry['callsign']}: not spotted")
const callsigns = ['W1AW', 'K1TTT', 'N3LLO', 'WB9Z', 'VE3EN'];
const response = await fetch(
`https://vailrerbn.com/api/v1/activity?call=${callsigns.join(',')}&hours=1`
);
const data = await response.json();
console.log(`${data.summary.spotted}/${data.summary.requested} callsigns active`);
for (const entry of data.callsigns) {
if (entry.spotted) {
console.log(` ${entry.callsign}: ${entry.count} spots, `
+ `last on ${entry.last_frequency} kHz (${entry.last_mode})`);
} else {
console.log(` ${entry.callsign}: not spotted`);
}
}
<?php
$callsigns = ["W1AW", "K1TTT", "N3LLO", "WB9Z", "VE3EN"];
$params = http_build_query([
'call' => implode(',', $callsigns),
'hours' => 1
]);
$response = file_get_contents(
"https://vailrerbn.com/api/v1/activity?$params"
);
$data = json_decode($response, true);
echo "{$data['summary']['spotted']}/{$data['summary']['requested']} callsigns active\n";
foreach ($data['callsigns'] as $entry) {
if ($entry['spotted']) {
echo " {$entry['callsign']}: {$entry['count']} spots, "
. "last on {$entry['last_frequency']} kHz ({$entry['last_mode']})\n";
} else {
echo " {$entry['callsign']}: not spotted\n";
}
}
require 'net/http'
require 'json'
callsigns = %w[W1AW K1TTT N3LLO WB9Z VE3EN]
uri = URI('https://vailrerbn.com/api/v1/activity')
uri.query = URI.encode_www_form({
call: callsigns.join(','),
hours: 1
})
response = Net::HTTP.get(uri)
data = JSON.parse(response)
puts "#{data['summary']['spotted']}/#{data['summary']['requested']} callsigns active"
data['callsigns'].each do |entry|
if entry['spotted']
puts " #{entry['callsign']}: #{entry['count']} spots, " \
"last on #{entry['last_frequency']} kHz (#{entry['last_mode']})"
else
puts " #{entry['callsign']}: not spotted"
end
end
Check if multiple callsigns have been spotted recently - much more efficient than individual calls
Callsign Groups
Create and manage persistent, named groups of callsigns for batch monitoring. Groups are public — anyone can view and query them. Modifications require an edit key returned at creation time.
Instead of passing a long list of callsigns to /activity every time, create a group once and query it by name. Perfect for contest teams, DXpedition monitoring, club rosters, or any recurring set of callsigns.
You can also browse and view groups on the Groups page.
POST /groups — Create a Group
Create a new named callsign group. Returns an edit_key that is shown only once — save it to modify or delete the group later.
Request Body (JSON)
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | 3-50 chars, lowercase alphanumeric + hyphens (e.g., my-contest-team) |
callsigns |
string[] | Yes | Array of callsigns (1-500) |
creator_name |
string | Yes | Your name or callsign (max 100 chars). Visible to others so they can contact you about group changes. |
creator_email |
string | Yes | Your email address. Visible on the group page so others can reach you for adjustment requests. |
description |
string | No | Optional description (max 200 chars) |
Example
curl -X POST https://vailrerbn.com/api/v1/groups \
-H "Content-Type: application/json" \
-d '{
"name": "contest-team-alpha",
"callsigns": ["W1AW", "K1TTT", "N3LLO"],
"creator_name": "Brett KE9BOS",
"creator_email": "ke9bos@pigletradio.org",
"description": "Our ARRL DX contest team"
}'
Response (201 Created)
{
"group": {
"name": "contest-team-alpha",
"callsigns": ["W1AW", "K1TTT", "N3LLO"],
"description": "Our ARRL DX contest team",
"creator_name": "Brett KE9BOS",
"creator_email": "ke9bos@pigletradio.org",
"callsign_count": 3,
"created_at": "2026-02-17T12:00:00.000Z",
"updated_at": "2026-02-17T12:00:00.000Z"
},
"edit_key": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
"notice": "Save this edit_key! It is required to modify or delete this group and will not be shown again."
}
The edit_key is only shown once at creation time. You need it to update or delete the group.
If you lose it, contact ke9bos@pigletradio.org for recovery.
GET /groups — List All Groups
Returns all groups with name, description, and callsign count. No authentication required.
Example
curl https://vailrerbn.com/api/v1/groups
Response
{
"groups": [
{
"name": "contest-team-alpha",
"description": "Our ARRL DX contest team",
"creator_name": "Brett KE9BOS",
"callsign_count": 3,
"created_at": "2026-02-17T12:00:00.000Z"
}
],
"total": 1
}
GET /groups/:name — Get Group Details
Returns the full group details including the callsign list, creator info, and timestamps.
Example
curl https://vailrerbn.com/api/v1/groups/contest-team-alpha
Response
{
"group": {
"name": "contest-team-alpha",
"description": "Our ARRL DX contest team",
"creator_name": "Brett KE9BOS",
"creator_email": "ke9bos@pigletradio.org",
"callsigns": ["K1TTT", "N3LLO", "W1AW"],
"callsign_count": 3,
"created_at": "2026-02-17T12:00:00.000Z",
"updated_at": "2026-02-17T12:00:00.000Z"
}
}
PUT /groups/:name — Update a Group
Update callsigns and/or description. Supports three modes for managing callsigns: full replacement, adding, or removing individual callsigns. Requires edit_key via header or query parameter.
Authentication
Provide the edit key in one of two ways:
- Header:
X-Edit-Key: your_key_here - Query parameter:
?edit_key=your_key_here
Request Body (JSON, all fields optional)
| Field | Type | Description |
|---|---|---|
callsigns |
string[] | Full replacement — replaces the entire callsign list with this array |
add_callsigns |
string[] | Incremental add — adds these callsigns to the existing list (duplicates ignored) |
remove_callsigns |
string[] | Incremental remove — removes these callsigns from the existing list |
description |
string | New description (can be combined with any callsign operation) |
callsigns together with add_callsigns or remove_callsigns in the same request.
Use callsigns for full replacement, or add_callsigns/remove_callsigns for incremental changes.
You may use add_callsigns and remove_callsigns together in one request.
Example: Full Replacement
curl -X PUT https://vailrerbn.com/api/v1/groups/contest-team-alpha \
-H "Content-Type: application/json" \
-H "X-Edit-Key: a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6" \
-d '{
"callsigns": ["W1AW", "K1TTT", "N3LLO", "WB9Z"],
"description": "Updated team roster"
}'
Example: Add Callsigns
curl -X PUT https://vailrerbn.com/api/v1/groups/contest-team-alpha \
-H "Content-Type: application/json" \
-H "X-Edit-Key: a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6" \
-d '{
"add_callsigns": ["N3LLO", "WB9Z"]
}'
Example: Remove Callsigns
curl -X PUT https://vailrerbn.com/api/v1/groups/contest-team-alpha \
-H "Content-Type: application/json" \
-H "X-Edit-Key: a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6" \
-d '{
"remove_callsigns": ["K1TTT"]
}'
DELETE /groups/:name — Delete a Group
Permanently delete a group. Requires edit_key.
Example
curl -X DELETE https://vailrerbn.com/api/v1/groups/contest-team-alpha \
-H "X-Edit-Key: a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"
GET /groups/:name/activity — Group Activity Check
Check which callsigns in the group have been spotted. Same response format as /activity with an added group field.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
hours |
integer | 1 | Time window in hours (1-168) |
Example
curl "https://vailrerbn.com/api/v1/groups/contest-team-alpha/activity?hours=1"
Response
{
"group": "contest-team-alpha",
"callsigns": [
{
"callsign": "W1AW",
"spotted": true,
"count": 12,
"last_spotted": "2026-02-17T15:32:00.000Z",
"last_frequency": 14025.3,
"last_mode": "CW",
"last_snr": 25
},
{
"callsign": "K1TTT",
"spotted": false,
"count": 0,
"last_spotted": null,
"last_frequency": null,
"last_mode": null,
"last_snr": null
}
],
"summary": {
"requested": 2,
"spotted": 1,
"not_spotted": 1
},
"hours": 1
}
GET /groups/:name/spots — Group Spot Data
Get full spot data for all callsigns in the group. Same response format as /spots with an added group field. Includes gridsquare enrichment.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
hours |
integer | 1 | Time window in hours (1-168) |
mode |
string | — | Mode filter (comma-separated: CW, FT8, etc.) |
band |
string | — | Band filter (20m, 40m, etc.) |
limit |
integer | 100 | Max results (1-1000) |
offset |
integer | 0 | Pagination offset |
Example
curl "https://vailrerbn.com/api/v1/groups/contest-team-alpha/spots?hours=24&mode=CW&limit=50"
Error Responses
| Status | Cause |
|---|---|
| 400 | Invalid name, callsigns, or description |
| 401 | Missing or invalid edit key (for PUT/DELETE) |
| 404 | Group not found |
| 409 | Group name already exists (on POST) |
| 429 | Global group limit reached (max 1000) |
Group Name Rules
- 3-50 characters
- Lowercase letters, numbers, and hyphens only
- Must start and end with a letter or number
- No consecutive hyphens
- Reserved names:
new,list,all,admin,api,test,create,delete,update
Automatic Cleanup
Groups that have not been queried or modified in 90 days are automatically deleted. Any read or query resets the timer.
GET /stats
Get aggregate statistics including total spots, spots/min, mode breakdown, and top bands.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
hours |
integer | 1 | Time range from now (use start_hours/end_hours for historical windows) |
start_hours |
integer | 1 | How many hours ago to start the window (older boundary) |
end_hours |
integer | 0 | How many hours ago to end the window (0 = now) |
hours=1— Last 1 hour (ends at now)start_hours=4&end_hours=0— Last 4 hours (same ashours=4)start_hours=4&end_hours=3— Data from 4 hours ago to 3 hours ago (1-hour window)start_hours=24&end_hours=12— Data from 24 hours ago to 12 hours ago
Example
curl "https://vailrerbn.com/api/v1/stats?hours=1"
import requests
response = requests.get(
"https://vailrerbn.com/api/v1/stats",
params={"hours": 1}
)
data = response.json()
print(f"Total spots: {data['totalSpots']}")
print(f"Spots/min: {data['spotsPerMinute']:.1f}")
print(f"Unique callsigns: {data['uniqueCallsigns']}")
print(f"\nMode breakdown:")
for mode, count in data['modeBreakdown'].items():
print(f" {mode}: {count}")
const response = await fetch(
'https://vailrerbn.com/api/v1/stats?hours=1'
);
const data = await response.json();
console.log(`Total spots: ${data.totalSpots}`);
console.log(`Spots/min: ${data.spotsPerMinute.toFixed(1)}`);
console.log(`Unique callsigns: ${data.uniqueCallsigns}`);
console.log(`\nMode breakdown:`);
for (const [mode, count] of Object.entries(data.modeBreakdown)) {
console.log(` ${mode}: ${count}`);
}
<?php
$response = file_get_contents(
"https://vailrerbn.com/api/v1/stats?hours=1"
);
$data = json_decode($response, true);
echo "Total spots: {$data['totalSpots']}\n";
echo "Spots/min: " . number_format($data['spotsPerMinute'], 1) . "\n";
echo "Unique callsigns: {$data['uniqueCallsigns']}\n";
echo "\nMode breakdown:\n";
foreach ($data['modeBreakdown'] as $mode => $count) {
echo " $mode: $count\n";
}
require 'net/http'
require 'json'
uri = URI('https://vailrerbn.com/api/v1/stats')
uri.query = URI.encode_www_form({ hours: 1 })
response = Net::HTTP.get(uri)
data = JSON.parse(response)
puts "Total spots: #{data['totalSpots']}"
puts "Spots/min: #{'%.1f' % data['spotsPerMinute']}"
puts "Unique callsigns: #{data['uniqueCallsigns']}"
puts "\nMode breakdown:"
data['modeBreakdown'].each do |mode, count|
puts " #{mode}: #{count}"
end
Fetch aggregated stats for the past hour
Historical Time Window Example
# Get stats from 4 hours ago to 3 hours ago (1-hour window)
curl "https://vailrerbn.com/api/v1/stats?start_hours=4&end_hours=3"
import requests
# Get stats from 4 hours ago to 3 hours ago
response = requests.get(
"https://vailrerbn.com/api/v1/stats",
params={
"start_hours": 4,
"end_hours": 3
}
)
data = response.json()
print(f"Time range: {data['timeRange']['start']} to {data['timeRange']['end']}")
print(f"Total spots: {data['totalSpots']}")
print(f"Spots/min: {data['spotsPerMinute']:.1f}")
// Get stats from 4 hours ago to 3 hours ago
const response = await fetch(
'https://vailrerbn.com/api/v1/stats?start_hours=4&end_hours=3'
);
const data = await response.json();
console.log(`Time range: ${data.timeRange.start} to ${data.timeRange.end}`);
console.log(`Total spots: ${data.totalSpots}`);
console.log(`Spots/min: ${data.spotsPerMinute.toFixed(1)}`);
<?php
// Get stats from 4 hours ago to 3 hours ago
$response = file_get_contents(
"https://vailrerbn.com/api/v1/stats?start_hours=4&end_hours=3"
);
$data = json_decode($response, true);
echo "Time range: {$data['timeRange']['start']} to {$data['timeRange']['end']}\n";
echo "Total spots: {$data['totalSpots']}\n";
echo "Spots/min: " . number_format($data['spotsPerMinute'], 1) . "\n";
require 'net/http'
require 'json'
# Get stats from 4 hours ago to 3 hours ago
uri = URI('https://vailrerbn.com/api/v1/stats')
uri.query = URI.encode_www_form({
start_hours: 4,
end_hours: 3
})
response = Net::HTTP.get(uri)
data = JSON.parse(response)
puts "Time range: #{data['timeRange']['start']} to #{data['timeRange']['end']}"
puts "Total spots: #{data['totalSpots']}"
puts "Spots/min: #{'%.1f' % data['spotsPerMinute']}"
Fetch stats for a specific time window (e.g., 4 to 3 hours ago)
GET /skimmers
Get list of active skimmers/spotters, sorted by spot count.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
hours |
integer | 1 | Time range in hours |
Note: Returns up to 100 skimmers.
GET /charts
Get chart data for visualizations (hourly activity, mode breakdown, band breakdown, top callsigns).
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
hours |
integer | 12 | Time range from now (use start_hours/end_hours for historical windows) |
start_hours |
integer | 12 | How many hours ago to start the window (older boundary) |
end_hours |
integer | 0 | How many hours ago to end the window (0 = now) |
hours=12— Last 12 hours (ends at now)start_hours=24&end_hours=12— Data from 24 hours ago to 12 hours agostart_hours=48&end_hours=24— Yesterday's full 24-hour window
Note: Hourly activity data excludes the current incomplete hour when end_hours=0.
Historical Time Window Example
# Get chart data from 48 hours ago to 24 hours ago (yesterday)
curl "https://vailrerbn.com/api/v1/charts?start_hours=48&end_hours=24"
import requests
# Get chart data from 48 hours ago to 24 hours ago (yesterday)
response = requests.get(
"https://vailrerbn.com/api/v1/charts",
params={
"start_hours": 48,
"end_hours": 24
}
)
data = response.json()
print(f"Hourly activity ({len(data['hourlyActivity'])} hours):")
for hour in data['hourlyActivity']:
print(f" {hour['hour']}: {hour['count']} spots")
print(f"\nTop modes:")
for mode in data['byMode'][:3]:
print(f" {mode['mode']}: {mode['count']}")
// Get chart data from 48 hours ago to 24 hours ago (yesterday)
const response = await fetch(
'https://vailrerbn.com/api/v1/charts?start_hours=48&end_hours=24'
);
const data = await response.json();
console.log(`Hourly activity (${data.hourlyActivity.length} hours):`);
data.hourlyActivity.forEach(hour => {
console.log(` ${hour.hour}: ${hour.count} spots`);
});
console.log(`\nTop modes:`);
data.byMode.slice(0, 3).forEach(mode => {
console.log(` ${mode.mode}: ${mode.count}`);
});
<?php
// Get chart data from 48 hours ago to 24 hours ago (yesterday)
$response = file_get_contents(
"https://vailrerbn.com/api/v1/charts?start_hours=48&end_hours=24"
);
$data = json_decode($response, true);
echo "Hourly activity (" . count($data['hourlyActivity']) . " hours):\n";
foreach ($data['hourlyActivity'] as $hour) {
echo " {$hour['hour']}: {$hour['count']} spots\n";
}
echo "\nTop modes:\n";
foreach (array_slice($data['byMode'], 0, 3) as $mode) {
echo " {$mode['mode']}: {$mode['count']}\n";
}
require 'net/http'
require 'json'
# Get chart data from 48 hours ago to 24 hours ago (yesterday)
uri = URI('https://vailrerbn.com/api/v1/charts')
uri.query = URI.encode_www_form({
start_hours: 48,
end_hours: 24
})
response = Net::HTTP.get(uri)
data = JSON.parse(response)
puts "Hourly activity (#{data['hourlyActivity'].length} hours):"
data['hourlyActivity'].each do |hour|
puts " #{hour['hour']}: #{hour['count']} spots"
end
puts "\nTop modes:"
data['byMode'].first(3).each do |mode|
puts " #{mode['mode']}: #{mode['count']}"
end
Fetch chart data for a time window in the past (e.g., yesterday)
GET /health
Health check endpoint. Returns server status and database connection info.
Example Response
{
"status": "ok",
"database": "connected",
"databaseSizeMB": 45.2,
"timestamp": "2025-01-09T15:30:00Z"
}
GET /bands
Get list of amateur bands with frequency ranges.
Example Response
{
"bands": [
{
"name": "160m",
"minFreq": 1800,
"maxFreq": 2000
},
{
"name": "80m",
"minFreq": 3500,
"maxFreq": 4000
}
// ... more bands
]
}