How-To Guides
Common use cases and complete workflows for the Vail ReRBN API.
Monitor a station in real-time
Set up continuous monitoring to be notified whenever a specific callsign is spotted.
Make initial request
Get recent spots and track the highest ID
Poll with after_id
Every 5-10 seconds, request new spots
Process and update
Handle new spots and update your tracked ID
# Not practical with curl - use Python or JavaScript for continuous polling
import requests
import time
last_id = 0
print("Monitoring for W1AW spots (Ctrl+C to stop)...")
while True:
params = {"limit": 500, "call": "W1AW"}
if last_id > 0:
params["after_id"] = last_id
response = requests.get("https://vailrerbn.com/api/v1/spots", params=params)
data = response.json()
for spot in data["spots"]:
grid = spot.get('grid', '????')
print(f"{spot['timestamp']} - {spot['callsign']} ({grid}) on {spot['frequency']} kHz by {spot['spotter']}")
last_id = max(last_id, spot["id"])
time.sleep(5) # Poll every 5 seconds
let lastId = 0;
console.log('Monitoring for W1AW spots (Ctrl+C to stop)...');
async function pollSpots() {
const params = new URLSearchParams({
limit: 500,
call: 'W1AW'
});
if (lastId > 0) {
params.set('after_id', lastId);
}
const response = await fetch(`https://vailrerbn.com/api/v1/spots?${params}`);
const data = await response.json();
for (const spot of data.spots) {
const grid = spot.grid || '????';
console.log(`${spot.timestamp} - ${spot.callsign} (${grid}) on ${spot.frequency} kHz by ${spot.spotter}`);
lastId = Math.max(lastId, spot.id);
}
}
// Poll every 5 seconds
setInterval(pollSpots, 5000);
pollSpots(); // Initial call
<?php
$lastId = 0;
echo "Monitoring for W1AW spots (Ctrl+C to stop)...\n";
while (true) {
$params = ['limit' => 500, 'call' => 'W1AW'];
if ($lastId > 0) {
$params['after_id'] = $lastId;
}
$url = "https://vailrerbn.com/api/v1/spots?" . http_build_query($params);
$response = file_get_contents($url);
$data = json_decode($response, true);
foreach ($data['spots'] as $spot) {
echo "{$spot['timestamp']} - {$spot['callsign']} on {$spot['frequency']} kHz by {$spot['spotter']}\n";
$lastId = max($lastId, $spot['id']);
}
sleep(5); // Poll every 5 seconds
}
require 'net/http'
require 'json'
last_id = 0
puts 'Monitoring for W1AW spots (Ctrl+C to stop)...'
loop do
uri = URI('https://vailrerbn.com/api/v1/spots')
params = { limit: 500, call: 'W1AW' }
params[:after_id] = last_id if last_id > 0
uri.query = URI.encode_www_form(params)
response = Net::HTTP.get(uri)
data = JSON.parse(response)
data['spots'].each do |spot|
puts "#{spot['timestamp']} - #{spot['callsign']} on #{spot['frequency']} kHz by #{spot['spotter']}"
last_id = [last_id, spot['id']].max
end
sleep 5 # Poll every 5 seconds
end
Continuously poll for new spots using cursor pagination
Expected Output
2025-01-09 15:30:00 - W1AW on 14.025 MHz by K1TTT
2025-01-09 15:32:15 - W1AW on 14.027 MHz by N2IC
2025-01-09 15:35:42 - W1AW on 21.025 MHz by W3LPL
Find all DX stations on a specific band
Discover what stations are active on a particular band right now.
# Get all spots on 20m in the last hour
curl "https://vailrerbn.com/api/v1/spots?band=20m&limit=1000"
# Filter by mode as well
curl "https://vailrerbn.com/api/v1/spots?band=20m&mode=FT8&limit=1000"
Tip: Sort by SNR to find the strongest signals
Analyze band conditions over time
Use the /stats endpoint to understand propagation patterns.
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
Use case: Compare mode breakdown at different times of day to identify optimal operating hours for each mode.
Build a skimmer coverage map
See which skimmers are active and how many spots each is reporting.
curl "https://vailrerbn.com/api/v1/skimmers?hours=24"
Returns up to 100 skimmers sorted by spot count. Great for identifying propagation paths and active skimmer regions.
Export spots to CSV for analysis
Transform JSON responses into CSV format for Excel or data analysis tools.
import requests
import csv
# Fetch spots
response = requests.get(
"https://vailrerbn.com/api/v1/spots",
params={"call": "W1AW", "since": 1704739200}
)
data = response.json()
# Write to CSV
with open('spots.csv', 'w', newline='') as f:
writer = csv.DictWriter(f,
fieldnames=['timestamp', 'callsign', 'frequency',
'mode', 'snr', 'spotter'])
writer.writeheader()
for spot in data['spots']:
writer.writerow({
'timestamp': spot['timestamp'],
'callsign': spot['callsign'],
'frequency': spot['frequency'],
'mode': spot['mode'],
'snr': spot['snr'],
'spotter': spot['spotter']
})
Integrate with logging software
Pattern for continuous integration with ham radio logging applications.
Use after_id polling
Poll every 5-10 seconds to avoid rate limits
Deduplicate spots
Track seen IDs to avoid processing duplicates
Filter locally
Apply additional filters client-side (frequency ranges, SNR thresholds)
Best practice: Store the last processed ID persistently so you can resume after restarts.