No project description provided
Project description
skysnoop
A Python SDK and CLI for querying aircraft data from adsb.lol, a community-driven ADS-B aggregation service.
Features
- 🛩️ Comprehensive API Coverage: Query aircraft by location, identifier, type, and more
- 🎯 Type-Safe: Full type hints and Pydantic models for all data structures
- ⚡ Async-First: Built on httpx for high-performance async operations
- 🖥️ Beautiful CLI: Rich terminal output with tables and JSON formatting
- 🔍 Flexible Filtering: Filter by altitude, type, callsign, squawk, and more
- 🧪 Well-Tested: >90% code coverage with comprehensive test suite
- 🔌 Dual API Support: Both OpenAPI (public) and RE-API (feeder) clients
API Access
This library provides two API clients:
OpenAPI Client (Public API)
The OpenAPI client uses the public https://api.adsb.lol endpoint:
- ✅ Publicly accessible - no feeder requirement
- ✅ Official OpenAPI spec - auto-generated models
- ✅ Type-safe - full Pydantic v2 validation
- 🔑 API keys will be required in the future (currently unavailable)
See OpenAPI Client Documentation for details.
RE-API Client (Feeder API)
The RE-API client uses the https://re-api.adsb.lol/ endpoint:
- 🎯 Feeder-only - requires feeding data to adsb.lol network
- 📡 Legacy API - original implementation
- 🔓 No API key required, but must run from the Feeder IP address
Getting Feeder Access:
- Set up an ADS-B receiver to feed data to adsb.lol
- Visit the adsb.lol documentation for setup instructions
- Join the adsb.lol community
API Documentation:
- OpenAPI Documentation: https://api.adsb.lol/docs
- OpenAPI Spec: https://api.adsb.lol/api/openapi.json
- Main Site: https://adsb.lol
- Community Support: Available through the adsb.lol platform
Installation
From PyPI
pip install skysnoop
From Source
git clone https://github.com/tedivm/skysnoop.git
cd skysnoop
pip install -e .
Quick Start
OpenAPI Client (Recommended)
The OpenAPI client provides access to the public API with type-safe, validated responses.
CLI Usage
# Query military aircraft
skysnoop openapi v2 mil
# Find aircraft by ICAO hex
skysnoop openapi v2 hex 4CA87C
# Find aircraft near a point (within 50nm)
skysnoop openapi v2 point 37.7749 -- -122.4194 50
# Get closest aircraft
skysnoop openapi v2 closest 37.7749 -- -122.4194 100
# Output as JSON
skysnoop openapi v2 mil --json
Python Library Usage
import asyncio
from skysnoop.client import OpenAPIClient
async def main():
async with OpenAPIClient() as client:
# Query by ICAO hex
response = await client.v2.get_by_hex(icao_hex="4CA87C")
# Query military aircraft
response = await client.v2.get_mil()
# Query by location
response = await client.v2.get_by_point(
lat=37.7749,
lon=-122.4194,
radius=50
)
# Access aircraft data
for aircraft in response.ac:
print(f"{aircraft.hex}: {aircraft.flight} at {aircraft.alt_baro}ft")
asyncio.run(main())
See OpenAPI Client Documentation for full details.
RE-API Client (Feeder-Only)
The RE-API client requires feeder access but provides additional functionality.
CLI Usage
Query aircraft within 50 nautical miles of San Francisco:
skysnoop circle -- 37.7749 -122.4194 50
Find all Airbus A321 aircraft:
skysnoop find-type A321
Get JSON output for programmatic use:
skysnoop circle --json -- 37.7749 -122.4194 50
Filter by altitude:
skysnoop circle --above-alt 30000 -- 37.7749 -122.4194 200
Python Library Usage
import asyncio
from skysnoop.client.api import ReAPIClient
async def main():
# Create client
async with ReAPIClient() as client:
# Query aircraft in a circular area
response = await client.circle(
lat=37.7749,
lon=-122.4194,
radius=50 # nautical miles
)
# Iterate through results
print(f"Found {response.resultCount} aircraft")
for aircraft in response:
print(f"{aircraft.hex}: {aircraft.flight} at {aircraft.alt_baro}ft")
asyncio.run(main())
With filters:
from skysnoop.query.filters import QueryFilters
async def main():
async with ReAPIClient() as client:
# Create filters
filters = QueryFilters(
above_alt_baro=30000,
type_code="A321"
)
# Query with filters
response = await client.circle(
lat=37.7749,
lon=-122.4194,
radius=200,
filters=filters
)
for aircraft in response:
print(f"{aircraft.hex}: {aircraft.type} at {aircraft.alt_baro}ft")
asyncio.run(main())
CLI Commands
Geographic Queries
-
circle- Query aircraft within a radius of a pointskysnoop circle -- <lat> <lon> <radius_nm>
-
closest- Find the closest aircraft to a pointskysnoop closest -- <lat> <lon> <max_radius_nm>
-
box- Query aircraft within a bounding boxskysnoop box -- <lat_south> <lat_north> <lon_west> <lon_east>
Identifier Queries
-
find-hex- Find aircraft by ICAO hex codeskysnoop find-hex <hex_code>
-
find-callsign- Find aircraft by callsignskysnoop find-callsign <callsign>
-
find-reg- Find aircraft by registrationskysnoop find-reg <registration>
-
find-type- Find all aircraft of a specific typeskysnoop find-type <type_code>
Bulk Queries
-
all-aircraft- Query all aircraft (with position by default)skysnoop all-aircraft skysnoop all-aircraft --include-no-position # Include aircraft without position
Common Options
All geographic and bulk commands support these filters:
--json- Output as JSON instead of table--callsign <callsign>- Filter by exact callsign--callsign-prefix <prefix>- Filter by callsign prefix--type <type>- Filter by aircraft type (e.g., A321, B738)--squawk <squawk>- Filter by squawk code--above-alt <feet>- Filter for aircraft above altitude--below-alt <feet>- Filter for aircraft below altitude--military- Filter for military aircraft
Note: When using negative coordinates (e.g., longitude), use -- before the coordinates to prevent them from being interpreted as options.
API Reference
ReAPIClient
Main client class for querying the adsb.lol API.
from skysnoop.client.api import ReAPIClient
async with ReAPIClient(
base_url="https://re-api.adsb.lol/", # Default API URL
timeout=30.0 # Request timeout in seconds
) as client:
# Use client methods
pass
Methods
Geographic Queries
-
circle(lat, lon, radius, filters=None)- Query aircraft in circular area- Returns:
APIResponse - Parameters:
lat(float): Latitude in decimal degreeslon(float): Longitude in decimal degreesradius(float): Radius in nautical milesfilters(QueryFilters, optional): Filter criteria
- Returns:
-
closest(lat, lon, radius, filters=None)- Find closest aircraft- Returns:
APIResponse(max 1 aircraft)
- Returns:
-
box(lat_south, lat_north, lon_west, lon_east, filters=None)- Query bounding box- Returns:
APIResponse
- Returns:
Identifier Queries
find_hex(hex_code)- Find by ICAO 24-bit addressfind_callsign(callsign)- Find by callsignfind_reg(registration)- Find by registrationfind_type(type_code)- Find by aircraft type
Bulk Queries
all_with_pos(filters=None)- All aircraft with position dataall(filters=None)- All aircraft including those without position
QueryFilters
Filter criteria for queries.
from skysnoop.query.filters import QueryFilters
filters = QueryFilters(
callsign_exact="UAL123", # Exact callsign match
callsign_prefix="UAL", # Callsign prefix (mutually exclusive with exact)
type_code="A321", # Aircraft type code
squawk="7700", # Squawk code
above_alt_baro=30000, # Minimum altitude in feet
below_alt_baro=40000, # Maximum altitude in feet
mil=True, # Military aircraft only
pia=False, # Privacy/PIA flag
ladd=False # LADD flag
)
Aircraft Model
The Aircraft Pydantic model represents individual aircraft data.
Key Fields:
hex(str, required): ICAO 24-bit addressflight(str | None): Callsign/flight numberregistration(str | None): Aircraft registrationtype(str | None): Aircraft type codelat(float | None): Latitudelon(float | None): Longitudealt_baro(int | Literal["ground"] | None): Barometric altitude or "ground"alt_geom(int | Literal["ground"] | None): Geometric altitude or "ground"gs(float | None): Ground speed in knotstrack(float | None): Track angle in degreessquawk(str | None): Squawk code
Properties:
has_position(bool): True if lat/lon are setposition_age(float | None): Seconds since position updatedcallsign(str | None): Cleaned callsign (stripped whitespace)
APIResponse Model
Response wrapper containing aircraft data.
response = await client.circle(...)
print(response.resultCount) # Number of aircraft
print(response.now) # Query timestamp
print(response.ptime) # Processing time
# Iterate aircraft
for aircraft in response:
print(aircraft.hex)
# Direct access
aircraft_list = response.aircraft
Error Handling
The library defines custom exceptions in skysnoop.exceptions:
from skysnoop.exceptions import SkySnoopError, APIError, TimeoutError, ValidationError
try:
async with ReAPIClient() as client:
response = await client.circle(lat=37.7749, lon=-122.4194, radius=50)
except TimeoutError as e:
print(f"Request timed out: {e}")
except APIError as e:
print(f"API error: {e}")
except ValidationError as e:
print(f"Invalid parameters: {e}")
except SkySnoopError as e:
print(f"General error: {e}")
Exception Hierarchy
SkySnoopError- Base exception for all library errorsAPIError- HTTP/API errors (4xx, 5xx responses)ValidationError- Invalid parameters or dataTimeoutError- Request timeout
Configuration
Configure the library via environment variables or settings:
from skysnoop.settings import settings
# Settings can be overridden
settings.adsb_api_base_url = "https://re-api.adsb.lol/"
settings.adsb_api_timeout = 30.0
settings.cli_output_format = "table" # or "json"
Or via environment variables:
export ADSB_API_BASE_URL="https://re-api.adsb.lol/"
export ADSB_API_TIMEOUT="30.0"
export CLI_OUTPUT_FORMAT="table"
Development
Setup Development Environment
git clone https://github.com/tedivm/skysnoop.git
cd skysnoop
make install
This will create a virtual environment at .venv and install all development dependencies.
Run Tests
# Run all tests with coverage
make pytest
# Run specific test with verbose output
make pytest_loud
# Run live API tests (requires API access from adsb.lol)
make pytest_live
Note: Live API tests require valid API credentials. You must be feeding data to adsb.lol to have API access. See the API Access section above.
Code Quality
# Run all quality checks (tests + linting + type checking)
make tests
# Format code
make black_fixes
# Lint code
make ruff_check
# Type checking
make mypy_check
# Auto-fix linting issues
make ruff_fixes
# Run all formatting fixes
make chores
About adsb.lol
adsb.lol is a community-driven ADS-B aggregation service that collects aircraft position data from volunteers worldwide. API access is free for feeders who contribute data to the network.
Resources:
- Main Site: https://adsb.lol
- API Documentation: https://api.adsb.lol/docs
- Feeding Guide: Check the adsb.lol site for instructions on setting up a feeder
Important Notes:
- Altitude can be an integer or the string "ground" for aircraft on the ground
- All distances are in nautical miles
- All altitudes are in feet
- All speeds are in knots
- API access requires feeding data to the adsb.lol network
License
This project is licensed under the MIT License - see the LICENSE file for details.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file skysnoop-0.2.0.tar.gz.
File metadata
- Download URL: skysnoop-0.2.0.tar.gz
- Upload date:
- Size: 675.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d6d13ebb135b772f97434ecd4c8688bec84f4c668e6c7f14547e1aaf9868b2c9
|
|
| MD5 |
0a64f0fd4946fd32d50996c741eb52eb
|
|
| BLAKE2b-256 |
0f9b3ada3cd83e8b1cd5b4b5683a2a5f0c4269d4dd32a7aae6cf817d0f9a0fa8
|
Provenance
The following attestation bundles were made for skysnoop-0.2.0.tar.gz:
Publisher:
pypi.yaml on tedivm/skysnoop
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
skysnoop-0.2.0.tar.gz -
Subject digest:
d6d13ebb135b772f97434ecd4c8688bec84f4c668e6c7f14547e1aaf9868b2c9 - Sigstore transparency entry: 730044052
- Sigstore integration time:
-
Permalink:
tedivm/skysnoop@2cf83e5775d61abb3f7a71ef5192ef78e197dbfe -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/tedivm
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yaml@2cf83e5775d61abb3f7a71ef5192ef78e197dbfe -
Trigger Event:
push
-
Statement type:
File details
Details for the file skysnoop-0.2.0-py3-none-any.whl.
File metadata
- Download URL: skysnoop-0.2.0-py3-none-any.whl
- Upload date:
- Size: 748.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c754db623712a4b9a23bdd50823f9ce86160e9c78457679f3f3c3d5809a1f144
|
|
| MD5 |
c99ebbba68e681250605cdd8564a3dd2
|
|
| BLAKE2b-256 |
8b81422a0e3c4216a009bd9f38415591c2f4df8b9dc69a402a21ba466083cc8b
|
Provenance
The following attestation bundles were made for skysnoop-0.2.0-py3-none-any.whl:
Publisher:
pypi.yaml on tedivm/skysnoop
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
skysnoop-0.2.0-py3-none-any.whl -
Subject digest:
c754db623712a4b9a23bdd50823f9ce86160e9c78457679f3f3c3d5809a1f144 - Sigstore transparency entry: 730044058
- Sigstore integration time:
-
Permalink:
tedivm/skysnoop@2cf83e5775d61abb3f7a71ef5192ef78e197dbfe -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/tedivm
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yaml@2cf83e5775d61abb3f7a71ef5192ef78e197dbfe -
Trigger Event:
push
-
Statement type: