gRPCurl + Docker (Recommended)
All sample code, scripts, configuration files, and accompanying documentation (collectively, "Sample Materials"), is provided solely to demonstrate connectivity to the ADS-B Exchange Streaming Platform. Your use of the Sample Materials is subject to your existing Terms of Use with JETNET. In the event of any conflict, the Terms of Use shall control.
The Sample Materials are for evaluation and testing purposes only and are not intended or approved for use in any production or operational environment. Do not deploy these samples or any derivative code in a live environment without independent design, validation, and testing by qualified personnel. JETNET expressly disclaims responsibility for any damages, data loss, or service interruptions arising from production use of the Sample Materials.
The Sample Materials are provided "as is" without warranty of any kind, express or implied, including warranties of merchantability, fitness for a particular purpose, accuracy, or non-infringement. JETNET does not warrant that the Sample Materials are error-free or suitable for your environment.
The Sample Materials reference third-party software, including Docker, grpcurl, and jq, for convenience only. JETNET is not affiliated with, does not endorse, and assumes no responsibility for any third-party software. Use of third-party tools is at your own risk and subject to their respective licenses and terms.
Your Client ID and Subscription ID are confidential. Do not share them with unauthorized parties, embed them in source code repositories, or transmit them unsecured. JETNET is not liable for any unauthorized access or data exposure resulting from your failure to safeguard your credentials.
JETNET reserves the right to modify or withdraw the Sample Materials at any time without prior notice.
Your Credentials
Treat these as secrets — do not share or commit them to version control.
Streams can drop unexpectedly — network blips, planned platform updates, or transient errors. Build your client to auto-reconnect with exponential backoff (e.g. 1s, 2s, 4s, 8s etc) and resume the stream automatically.
| Value | |
|---|---|
| Endpoint | stream.adsbexchange.com:443 |
| ClientId | YOUR_CLIENT_ID |
| SubscriptionId | YOUR_SUBSCRIPTION_ID |
Setup
Step 1: Copy the files
Copy the four files below into a new folder on your machine.
FROM fullstorydev/grpcurl:latest AS grpcurl
FROM python:3.12-alpine
COPY /bin/grpcurl /usr/local/bin/grpcurl
RUN pip install zstandard --no-cache-dir
COPY aircraft.proto /protos/aircraft.proto
COPY entrypoint.sh /app/entrypoint.sh
COPY decode.py /app/decode.py
RUN chmod +x /app/entrypoint.sh
ENV ENDPOINT=stream.adsbexchange.com:443
ENTRYPOINT ["/app/entrypoint.sh"]
#!/bin/sh
set -e
if [ -z "$CLIENT_ID" ] || [ -z "$SUBSCRIPTION_ID" ]; then
echo "Usage: docker run --rm -e CLIENT_ID=<id> -e SUBSCRIPTION_ID=<id> jetnet-stream"
exit 1
fi
echo "Connecting to $ENDPOINT..."
echo "Client: $CLIENT_ID"
echo "Subscription: $SUBSCRIPTION_ID"
echo ""
grpcurl \
-import-path /protos \
-proto /protos/aircraft.proto \
-H "client-id: ${CLIENT_ID}" \
-d "{\"subscription_id\": \"${SUBSCRIPTION_ID}\"}" \
"${ENDPOINT}" \
jetnet.aircraft.AircraftStreamingService/StartStream \
| python3 /app/decode.py
syntax = "proto3";
package jetnet.aircraft;
message StartStreamRequest {
string subscription_id = 1;
}
message JsonAircraftBatch {
bytes json_data = 1;
}
service AircraftStreamingService {
rpc StartStream(StartStreamRequest) returns (stream JsonAircraftBatch);
}
#!/usr/bin/env python3
import sys
import json
import base64
try:
import zstandard as zstd
except ImportError:
print("ERROR: Missing dependency. Run: pip install zstandard", file=sys.stderr)
sys.exit(1)
dctx = zstd.ZstdDecompressor()
batch = 0
buffer = []
for line in sys.stdin:
buffer.append(line)
try:
msg = json.loads("".join(buffer))
except json.JSONDecodeError:
continue
buffer = []
try:
json_data = msg.get("jsonData") or msg.get("json_data")
if not json_data:
continue
data = base64.b64decode(json_data)
try:
raw = dctx.decompress(data)
except zstd.ZstdError:
raw = data
aircraft_list = json.loads(raw)
batch += 1
print(f"\n--- Batch {batch} ({len(aircraft_list)} aircraft) ---")
for ac in aircraft_list[:5]:
hex_id = ac.get("hex", "?")
flight = ac.get("flight", "").strip() or "?"
alt = ac.get("alt_baro", "?")
lat = ac.get("lat", "?")
lon = ac.get("lon", "?")
gs = ac.get("gs", "?")
print(f" {hex_id:8s} {flight:10s} alt={alt}ft lat={lat} lon={lon} gs={gs}kt")
if len(aircraft_list) > 5:
print(f" ... and {len(aircraft_list) - 5} more")
except Exception as e:
print(f"[decode error] {e}", file=sys.stderr)
Step 2: Build the image
docker build -t jetnet-stream .
Step 3: Run
Linux / macOS:
docker run --rm \
-e CLIENT_ID=YOUR_CLIENT_ID \
-e SUBSCRIPTION_ID=YOUR_SUBSCRIPTION_ID \
jetnet-stream
Windows (PowerShell):
docker run --rm `
-e CLIENT_ID=YOUR_CLIENT_ID `
-e SUBSCRIPTION_ID=YOUR_SUBSCRIPTION_ID `
jetnet-stream
What to expect
Connecting to stream.adsbexchange.com:443...
Client: YOUR_CLIENT_ID
Subscription: YOUR_SUBSCRIPTION_ID
--- Batch 1 (42 aircraft) ---
a12345 UAL123 alt=35000ft lat=51.5074 lon=-0.1278 gs=480kt
b67890 BAW456 alt=28000ft lat=48.8566 lon=2.3522 gs=420kt
... and 40 more
Press Ctrl+C to stop.
No output or a connection error means a connectivity issue — check your firewall, credentials, or endpoint URL.