API reference v1
Read-only JSON API. No key required. Base URL: http://100.85.157.228/api/v1.
Interactive schema: Swagger UI, OpenAPI JSON.
Try requests in the live tester.
Endpoints
| Method | Path | Description | Status codes |
|---|---|---|---|
| GET | /api/v1/aircraft/{hex} | Aircraft by 24-bit address (6 hex characters, case-insensitive). Header: X-Aircraft-Data. | 200, 404, 422, 429 |
| GET | /api/v1/registration/{registration} | Aircraft by registration. Matching ignores case, spaces, - and .. Registrations are not unique, so the result is a list. | 200, 404, 422, 429 |
| GET | /api/v1/search?q={query} | Hex prefix, exact or prefix registration (≥2 characters) and exact competition number. Empty list if nothing matches. | 200, 422, 429 |
| GET | /api/v1/stats | Record counts and synchronisation times. | 200 |
| GET | /api/v1/health | Overall status (local database, data freshness) plus the status of GliderNet and any enabled photo provider. | 200, 503 |
| GET | /api/v1/openapi.json | OpenAPI 3 specification. | 200 |
Aircraft object
All keys are always present. Values the source does not provide are null; nothing is invented.
The OGN DDB has no manufacturer, operator or callsign fields, so the API does not offer them.
hex | Six uppercase hex characters. |
|---|---|
address_type | ICAO, FLARM or OGN. Only ICAO addresses are Mode-S addresses. |
registration | As entered in the DDB, or null. |
competition_number | Competition number (CN) or null. |
aircraft.model | Model name from the DDB model list, or null. |
aircraft.category, aircraft.category_code | DDB category 1–6: Glider / motor glider, Plane, Ultralight, Helicopter, Drone / UAV, Other. |
tracked, identified | Owner privacy choices. If either is false the DDB publishes no registration, CN or model. |
photos | List of {provider, link, thumbnail_url, thumbnail_large_url, photographer, attribution}. Display attribution and link to link. |
photos_status | disabled, not_applicable, pending (retry later), available or none. |
Live example for GET /api/v1/aircraft/002071 from the current dataset:
{
"hex": "002071",
"address_type": "ICAO",
"registration": "ZS-GCT",
"competition_number": "EX",
"aircraft": {
"model": "JS-1 Revelation",
"category": "Glider / motor glider",
"category_code": 1
},
"tracked": true,
"identified": true,
"photos": [],
"photos_status": "disabled"
}
Errors
Errors share one shape: {"error": {"code": "…", "message": "…"}}.
404 aircraft_not_found | Valid input, no matching aircraft. |
|---|---|
422 invalid_hex | The address is not exactly six hexadecimal characters. |
422 invalid_registration, invalid_query, validation_error | Malformed or missing parameter. |
429 rate_limited | Too many requests; see Retry-After. |
503 database_unavailable | The local database cannot be opened. |
500 internal_error | Unexpected server error (details are logged, never returned). |
X-Aircraft-Data header
Every 200 response from the aircraft, registration and search endpoints carries:
X-Aircraft-Data: Base64URL (RFC 4648 §5, no padding) of compact UTF-8 JSON:{"v":1,"complete":true,"count":N,"included":N,"photos_omitted":false,"aircraft":[{hex, address_type, registration, competition_number, model, category, category_code, tracked, identified, photos:[…]}]}X-Aircraft-Data-Complete:trueorfalse, the same ascomplete.
The encoded value never exceeds 4096 bytes. If the full payload is larger, the server first drops all photos (photos_omitted: true), then keeps only the longest prefix of the aircraft list that fits (included < count). In both cases complete is false, and the JSON body remains the complete, authoritative response. Error responses have no such header.
Rate limits & CORS
Requests to /api/ are limited per client IP (token bucket). Responses include RateLimit-Limit and RateLimit-Remaining; a 429 includes Retry-After. Cross-origin access is available only for the origins the operator configures; X-Aircraft-Data is exposed to those origins via Access-Control-Expose-Headers.
curl examples
# Look up by hex address
curl -s http://100.85.157.228/api/v1/aircraft/002071
# Look up by registration
curl -s http://100.85.157.228/api/v1/registration/ZS-GCT
# Show only the X-Aircraft-Data header and decode it (Base64URL without padding)
curl -sD - -o /dev/null http://100.85.157.228/api/v1/aircraft/002071 \
| awk -F': ' 'tolower($1)=="x-aircraft-data"{print $2}' | tr -d '\r' \
| python3 -c 'import sys,base64,json; v=sys.stdin.read().strip(); print(json.dumps(json.loads(base64.urlsafe_b64decode(v+"="*(-len(v)%4))),indent=2))'
# Invalid address (422) and unknown aircraft (404)
curl -si http://100.85.157.228/api/v1/aircraft/XYZ
curl -si http://100.85.157.228/api/v1/aircraft/FFFFFE