A receiver listens for the Remote ID broadcasts drones are required to transmit and reports them to your account. It receives only — it transmits nothing, and needs no permission to run.
| Raspberry Pi A 4 or 5; anything running 64-bit Raspberry Pi OS. |
| Bluetooth adapter Must support Bluetooth 5 extended advertising. A Bluetooth 4 dongle will pair happily and silently miss most drones. |
| Wi-Fi adapter Must support monitor mode, and must be a second adapter — monitor mode takes it off your network. |
| A USB 2.0 port Not optional. See below. |
sudo apt update && sudo apt install -y python3-pip bluez aircrack-ng git sudo git clone https://github.com/thetopnach/cielotrack-receiver.git /opt/cielotrack-receiver cd /opt/cielotrack-receiver sudo pip3 install -r requirements.txt --break-system-packages sudo cp .env.example .env # set BASE_LAT, BASE_LON and WIFI_INTERFACE sudo cp cielotrack-receiver.service /etc/systemd/system/ sudo systemctl enable --now cielotrack-receiver
Full instructions are in the repository README.
On first start the receiver prints a six-digit claim code:
🔑 Central server: unclaimed. Claim code: 481-207 — enter this at https://cielotrack.com/receivers
Sign in, then enter that code on the Receivers page. Your receiver picks up its API key within a minute and starts reporting. The key is handed over exactly once and then erased server-side, so it never sits in a database waiting to be read.
BLE: extended scanning active rather than legacy.You don't have to run our code. Anything that can make HTTPS requests can report to CieloTrack.
curl -X POST https://cielotrack.com/v1/devices/claim -H 'Content-Type: application/json' -d '{"device_id":"<uuid>","bootstrap_secret":"<random>"}'
curl https://cielotrack.com/v1/devices/<uuid>/status -H 'X-Bootstrap-Secret: <random>'
curl -X POST https://cielotrack.com/v1/detections -H 'Authorization: Bearer <api-key>' -H 'Content-Type: application/json' -d '{"detected_at":"2026-01-01T00:00:00Z","uas_id":"…","lat":32.9,"lon":-96.7}'
Batch up to 500 rows at
/v1/detections/batch as {"detections":[…]}. Invalid rows
come back with their index and the reason; valid rows in the same batch are still
stored.
Only detected_at is required — send whatever else your hardware can
decode, and omit or null the rest. Values outside these ranges are rejected rather
than stored, so a bad reading never lands on the map.
| Field | Type | Accepts |
|---|---|---|
detected_atWhen the aircraft was seen. The only required field. |
timestamp | required, ISO-8601 |
uas_idSerial or registration broadcast by the aircraft. |
string | up to 128 characters |
ua_typeAirframe class, e.g. Helicopter, Hybrid Lift, Fixed Wing. |
string | up to 64 characters |
protocolHow it was heard: BLE or Wi-Fi. |
string | up to 64 characters |
macRadio address the broadcast came from. |
string | up to 64 characters |
latAircraft latitude, degrees. |
number | -90 to 90 |
lonAircraft longitude, degrees. |
number | -180 to 180 |
altitude_mAircraft altitude, metres. |
number | -1,000 to 100,000 |
altitude_refWhat altitude_m is measured against: agl (height above ground, comparable to the 400 ft limit) or absolute. |
string | up to 32 characters |
speed_mpsGround speed, metres per second. |
number | -1,000 to 1,000 |
operator_latController latitude, degrees. |
number | -90 to 90 |
operator_lonController longitude, degrees. |
number | -180 to 180 |
operator_altitude_mController altitude, metres. |
number | -1,000 to 100,000 |
operator_location_typeHow the operator position was derived: takeoff, live-gnss or fixed. |
string | up to 32 characters |
identity_sourcedecoded when read from this broadcast; inferred-from-mac when recovered from an earlier sighting of the same radio. |
string | up to 64 characters |
message_countHow many Remote ID messages went into this record. |
integer | 0 to 10,000,000 |
rssi_dbmReceived signal strength, dBm (negative). Send null when the radio reports it as unavailable rather than sending the sentinel. |
integer | -200 to 20 |