15 KiB
HAMView API Reference
Version: 1.0
Base URL: /api/v1
Content-Type: application/json
Table of Contents
Authentication
Currently, the API does not require authentication. All endpoints are publicly accessible.
Error Handling
All error responses follow this format:
{
"error": "Human-readable error message"
}
HTTP Status Codes
| Code | Description |
|---|---|
| 200 | Success |
| 400 | Bad Request - Invalid parameters |
| 404 | Not Found - Resource does not exist |
| 500 | Internal Server Error |
Endpoints
Radios
List All Radios
GET /api/v1/radios
Returns a list of all radio receivers/stations.
Response: 200 OK
Radio[]
Example:
curl http://localhost:8073/api/v1/radios
[
{
"id": 1,
"name": "Station-Alpha",
"is_online": true,
"manufacturer": "Heltec",
"protocol": "meshcore",
"frequency": 868.1
}
]
List Radios by Protocol
GET /api/v1/radios/:protocol
Returns radios filtered by protocol.
Parameters:
| Name | Type | In | Description |
|---|---|---|---|
| protocol | string | path | Protocol name (e.g., "meshcore", "aprs") |
Response: 200 OK
Radio[]
Example:
curl http://localhost:8073/api/v1/radios/meshcore
MeshCore
Get MeshCore Statistics
GET /api/v1/meshcore
Returns aggregated network statistics.
Response: 200 OK
{
messages: number;
nodes: number;
receivers: number;
packets: {
timestamps: number[];
packets: number[];
};
}
Example:
curl http://localhost:8073/api/v1/meshcore
{
"messages": 150234,
"nodes": 127,
"receivers": 8,
"packets": {
"timestamps": [1709650800, 1709654400],
"packets": [142, 203]
}
}
List MeshCore Groups
GET /api/v1/meshcore/groups
Returns public MeshCore groups/channels.
Response: 200 OK
{
id: number;
name: string;
secret: string;
}[]
Example:
curl http://localhost:8073/api/v1/meshcore/groups
[
{
"id": 5,
"name": "General Chat",
"secret": "0123456789abcdef0123456789abcdef"
}
]
List MeshCore Nodes
GET /api/v1/meshcore/nodes
Returns MeshCore network nodes.
Query Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
| type | string | No | Node type: "chat", "room", "sensor", "repeater" |
Response: 200 OK
{
id: number;
packet: MeshCorePacket[];
name: string;
type: number;
prefix: string;
public_key: string;
first_heard_at: string;
last_heard_at: string;
last_latitude: number | null;
last_longitude: number | null;
distance?: number;
}[]
Example:
curl http://localhost:8073/api/v1/meshcore/nodes
curl http://localhost:8073/api/v1/meshcore/nodes?type=chat
[
{
"id": 42,
"packet": [],
"name": "NODE-CHARLIE",
"type": 0,
"prefix": "mc",
"public_key": "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"first_heard_at": "2026-01-15T08:00:00Z",
"last_heard_at": "2026-03-05T14:25:00Z",
"last_latitude": 52.3667,
"last_longitude": 4.8945
}
]
Find Nodes Near Location
GET /api/v1/meshcore/nodes/close-to/:publickey
Returns nodes within a specified radius of a reference node.
Path Parameters:
| Name | Type | Description |
|---|---|---|
| publickey | string | 64-character hex-encoded Ed25519 public key |
Query Parameters:
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| radius | number | No | 25000 | Search radius in meters |
Response: 200 OK
{
node: MeshCoreNode;
nodes: MeshCoreNode[]; // Sorted by distance, with distance field populated
}
Example:
curl "http://localhost:8073/api/v1/meshcore/nodes/close-to/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef?radius=50000"
{
"node": {
"id": 42,
"name": "NODE-CHARLIE",
"public_key": "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"last_latitude": 52.3667,
"last_longitude": 4.8945,
"distance": 0
},
"nodes": [
{
"id": 43,
"name": "NODE-DELTA",
"last_latitude": 52.3700,
"last_longitude": 4.9000,
"distance": 450.5
}
]
}
List MeshCore Packets
GET /api/v1/meshcore/packets
Returns MeshCore packets based on filter criteria.
Query Parameters (mutually exclusive):
| Name | Type | Required | Description |
|---|---|---|---|
| hash | string | No | 16-character hex hash |
| type | number | No | Payload type (0-255) |
| channel_hash | string | No | 2-character channel hash (requires type) |
Response: 200 OK
{
id: number;
radio_id: number;
radio: Radio | null;
snr: number;
rssi: number;
version: number;
route_type: number;
payload_type: number;
hash: string;
path: string; // Base64
payload: string; // Base64
raw: string; // Base64
parsed: object | null;
channel_hash: string;
received_at: string;
}[]
Payload Types:
| Value | Description |
|---|---|
| 0 | Ping |
| 1 | Node announcement |
| 2 | Direct text message |
| 3 | Group text message |
| 4 | Position update |
Examples:
# Get 100 most recent packets
curl http://localhost:8073/api/v1/meshcore/packets
# Get packets by hash
curl http://localhost:8073/api/v1/meshcore/packets?hash=a1b2c3d4e5f67890
# Get packets by type
curl http://localhost:8073/api/v1/meshcore/packets?type=3
# Get group messages for a channel
curl http://localhost:8073/api/v1/meshcore/packets?type=3&channel_hash=ab
[
{
"id": 12345,
"radio_id": 1,
"snr": 8.5,
"rssi": -95,
"version": 1,
"route_type": 0,
"payload_type": 3,
"hash": "a1b2c3d4e5f67890",
"path": "AQIDBA==",
"payload": "SGVsbG8gV29ybGQ=",
"raw": "AQIDBAUGBwg=",
"parsed": {"text": "Hello World"},
"channel_hash": "ab",
"received_at": "2026-03-05T14:30:00Z"
}
]
APRS
List APRS Packets
GET /api/v1/aprs/packets
Returns APRS packets based on filter criteria.
Query Parameters (evaluated in order):
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| src | string | No | - | Source callsign (case-insensitive) |
| dst | string | No | - | Destination callsign (case-insensitive) |
| limit | number | No | 100 | Maximum number of packets when no src/dst filter is used |
Response: 200 OK
{
id: number;
radio_id: number;
radio: Radio;
src: string;
dst: string;
path: string;
comment: string;
latitude: number | null;
longitude: number | null;
symbol: string;
raw: string;
received_at: string;
}[]
Examples:
# Get 100 most recent packets
curl http://localhost:8073/api/v1/aprs/packets
# Get packets by source callsign
curl http://localhost:8073/api/v1/aprs/packets?src=OE1ABC
# Get packets by destination callsign
curl http://localhost:8073/api/v1/aprs/packets?dst=APRS
# Get recent packets with explicit limit
curl http://localhost:8073/api/v1/aprs/packets?limit=200
Data Models
Radio
interface Radio {
id: number; // Unique identifier
name: string; // Station name
is_online: boolean; // Online status
manufacturer: string; // Hardware manufacturer
device: string | null; // Device model
firmware_version: string | null; // Firmware version
firmware_date: string | null; // ISO 8601 timestamp
antenna: string | null; // Antenna description
modulation: string; // e.g., "LoRa"
protocol: string; // e.g., "meshcore", "aprs"
latitude: number | null; // Decimal degrees
longitude: number | null; // Decimal degrees
altitude: number | null; // Meters
frequency: number; // Hz
bandwidth: number; // Hz
power: number | null; // dBm
gain: number | null; // dBi
lora_sf: number | null; // LoRa spreading factor (7-12)
lora_cr: number | null; // LoRa coding rate (5-8)
extra: object | null; // Additional metadata
created_at: string; // ISO 8601 timestamp
updated_at: string; // ISO 8601 timestamp
}
MeshCorePacket
interface MeshCorePacket {
id: number;
radio_id: number;
radio: Radio | null;
snr: number; // Signal-to-noise ratio (dB)
rssi: number; // Received signal strength (dBm)
version: number;
route_type: number;
payload_type: number;
hash: string; // 16-char hex
path: string; // Base64-encoded
payload: string; // Base64-encoded
raw: string; // Base64-encoded
parsed: object | null; // Depends on payload_type
channel_hash: string; // 2-char hex
received_at: string; // ISO 8601 timestamp
}
APRSPacket
interface APRSPacket {
id: number;
radio_id: number;
radio: Radio;
src: string; // Source callsign
dst: string; // Destination callsign
path: string; // Digipeater path
comment: string;
latitude: number | null;
longitude: number | null;
symbol: string; // APRS symbol table + code
raw: string; // Raw APRS packet
received_at: string; // ISO 8601 timestamp
}
MeshCoreNode
interface MeshCoreNode {
id: number;
packet: MeshCorePacket[];
name: string;
type: number; // 0=repeater, 1=chat, 2=room, 3=sensor
prefix: string; // 2-char network prefix
public_key: string; // 64-char hex Ed25519 public key
first_heard_at: string; // ISO 8601 timestamp
last_heard_at: string; // ISO 8601 timestamp
last_latitude: number | null;
last_longitude: number | null;
distance?: number; // Meters (proximity queries only)
}
MeshCoreGroup
interface MeshCoreGroup {
id: number;
name: string; // Max 32 characters
secret: string; // 32-char hex
}
MeshCoreStats
interface MeshCoreStats {
messages: number;
nodes: number;
receivers: number;
packets: {
timestamps: number[]; // Unix timestamps
packets: number[]; // Packet counts
};
}
Examples
JavaScript/TypeScript Client
// Using fetch API
const API_BASE = 'http://localhost:8073/api/v1';
// Get all radios
async function getRadios(): Promise<Radio[]> {
const response = await fetch(`${API_BASE}/radios`);
if (!response.ok) throw new Error(await response.text());
return response.json();
}
// Get MeshCore nodes by type
async function getMeshCoreNodes(type?: string): Promise<MeshCoreNode[]> {
const url = type
? `${API_BASE}/meshcore/nodes?type=${type}`
: `${API_BASE}/meshcore/nodes`;
const response = await fetch(url);
if (!response.ok) throw new Error(await response.text());
return response.json();
}
// Find nearby nodes
async function getNodesNearby(publicKey: string, radius: number = 25000) {
const response = await fetch(
`${API_BASE}/meshcore/nodes/close-to/${publicKey}?radius=${radius}`
);
if (!response.ok) throw new Error(await response.text());
return response.json();
}
Python Client
import requests
from typing import List, Optional, Dict, Any
API_BASE = 'http://localhost:8073/api/v1'
def get_radios(protocol: Optional[str] = None) -> List[Dict[str, Any]]:
"""Get all radios or filter by protocol."""
url = f"{API_BASE}/radios/{protocol}" if protocol else f"{API_BASE}/radios"
response = requests.get(url)
response.raise_for_status()
return response.json()
def get_meshcore_packets(
hash: Optional[str] = None,
type: Optional[int] = None,
channel_hash: Optional[str] = None
) -> List[Dict[str, Any]]:
"""Get MeshCore packets with optional filters."""
params = {}
if hash:
params['hash'] = hash
if type is not None:
params['type'] = type
if channel_hash:
params['channel_hash'] = channel_hash
response = requests.get(f"{API_BASE}/meshcore/packets", params=params)
response.raise_for_status()
return response.json()
def get_meshcore_stats() -> Dict[str, Any]:
"""Get MeshCore network statistics."""
response = requests.get(f"{API_BASE}/meshcore")
response.raise_for_status()
return response.json()
Go Client
package hamview
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
)
const APIBase = "http://localhost:8073/api/v1"
type Client struct {
BaseURL string
HTTP *http.Client
}
func NewClient() *Client {
return &Client{
BaseURL: APIBase,
HTTP: &http.Client{},
}
}
func (c *Client) GetRadios() ([]Radio, error) {
var radios []Radio
err := c.get("/radios", &radios)
return radios, err
}
func (c *Client) GetMeshCoreNodes(nodeType string) ([]MeshCoreNode, error) {
path := "/meshcore/nodes"
if nodeType != "" {
path += "?type=" + url.QueryEscape(nodeType)
}
var nodes []MeshCoreNode
err := c.get(path, &nodes)
return nodes, err
}
func (c *Client) get(path string, result interface{}) error {
resp, err := c.HTTP.Get(c.BaseURL + path)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("HTTP %d", resp.StatusCode)
}
return json.NewDecoder(resp.Body).Decode(result)
}
Rate Limiting
Currently no rate limiting is implemented.
Versioning
API version is specified in the URL path (/api/v1). Breaking changes will increment the version number.
Support
For issues or questions, please refer to the project documentation at https://git.maze.io/ham/hamview