-
Notifications
You must be signed in to change notification settings - Fork 0
API Documentation
Comprehensive documentation for all TrueHour API endpoints, request/response formats, and usage examples.
Local Development: http://localhost:8000
Docker Container: http://localhost:8000 (or your mapped port)
Production: Depends on your deployment
Interactive API Explorer: http://localhost:8000/docs (Swagger UI) Alternative Documentation: http://localhost:8000/redoc (ReDoc)
These endpoints manage your personal aircraft list (owned, club, rental aircraft).
Endpoint: GET /api/user/aircraft
Query Parameters:
-
is_active(optional): Filter by active status (true/false)
Response: 200 OK with array of aircraft objects
Example Request:
curl http://localhost:8000/api/user/aircraft
curl http://localhost:8000/api/user/aircraft?is_active=trueExample Response:
[
{
"id": 1,
"tail_number": "N172SP",
"year": 2001,
"make": "Cessna",
"model": "172S",
"ownership_type": "owned",
"is_active": true,
"created_at": "2025-12-01T10:00:00Z",
"updated_at": "2025-12-01T10:00:00Z"
}
]Endpoint: GET /api/user/aircraft/{id}
Parameters:
-
id(path, required): Aircraft ID
Response: 200 OK with aircraft object or 404 Not Found
Example Request:
curl http://localhost:8000/api/user/aircraft/1Endpoint: POST /api/user/aircraft
Request Body:
{
"tail_number": "N172SP",
"year": 2001,
"make": "Cessna",
"model": "172S",
"ownership_type": "owned",
"is_active": true
}Response: 201 Created with created aircraft object
Example Request:
curl -X POST http://localhost:8000/api/user/aircraft \
-H "Content-Type: application/json" \
-d '{"tail_number":"N172SP","year":2001,"make":"Cessna","model":"172S","ownership_type":"owned","is_active":true}'Endpoint: PUT /api/user/aircraft/{id}
Parameters:
-
id(path, required): Aircraft ID
Request Body: Same as Add Aircraft
Response: 200 OK with updated aircraft or 404 Not Found
Endpoint: DELETE /api/user/aircraft/{id}
Parameters:
-
id(path, required): Aircraft ID
Response: 200 OK with success message or 404 Not Found
Example Request:
curl -X DELETE http://localhost:8000/api/user/aircraft/1These endpoints manage aviation expenses with filtering and aggregation.
Endpoint: GET /api/expenses
Query Parameters:
-
aircraft_id(optional): Filter by aircraft ID -
category(optional): Filter by expense category -
start_date(optional): Filter expenses from this date (YYYY-MM-DD) -
end_date(optional): Filter expenses until this date (YYYY-MM-DD) -
limit(optional, default 100): Maximum number of results -
offset(optional, default 0): Pagination offset
Response: 200 OK with array of expense objects
Example Requests:
# Get all expenses
curl http://localhost:8000/api/expenses
# Get fuel expenses only
curl http://localhost:8000/api/expenses?category=fuel
# Get expenses for specific aircraft
curl http://localhost:8000/api/expenses?aircraft_id=1
# Get expenses in date range
curl "http://localhost:8000/api/expenses?start_date=2025-01-01&end_date=2025-12-31"
# Pagination
curl "http://localhost:8000/api/expenses?limit=20&offset=40"Example Response:
[
{
"id": 1,
"aircraft_id": 1,
"date": "2025-12-01",
"category": "fuel",
"subcategory": "100LL",
"amount": 75.50,
"description": "Fuel at KAPA",
"vendor": "Signature Flight Support",
"created_at": "2025-12-01T15:30:00Z",
"updated_at": "2025-12-01T15:30:00Z"
}
]Endpoint: GET /api/expenses/{id}
Parameters:
-
id(path, required): Expense ID
Response: 200 OK with expense object or 404 Not Found
Endpoint: GET /api/expenses/summary
Query Parameters:
-
start_date(optional): Summary start date (YYYY-MM-DD) -
end_date(optional): Summary end date (YYYY-MM-DD) -
group_by(optional, default "category"): Group by "category" or "subcategory"
Response: 200 OK with aggregated summary
Example Request:
curl "http://localhost:8000/api/expenses/summary?start_date=2025-01-01&end_date=2025-12-31"Example Response:
[
{
"category": "fuel",
"total_amount": 2450.75,
"count": 23,
"avg_amount": 106.55,
"min_amount": 45.00,
"max_amount": 180.25
},
{
"category": "maintenance",
"total_amount": 1200.00,
"count": 4,
"avg_amount": 300.00,
"min_amount": 150.00,
"max_amount": 600.00
}
]Endpoint: POST /api/expenses
Request Body:
{
"aircraft_id": 1,
"date": "2025-12-01",
"category": "fuel",
"subcategory": "100LL",
"amount": 75.50,
"description": "Fuel at KAPA",
"vendor": "Signature Flight Support"
}Response: 201 Created with created expense object
Endpoint: PUT /api/expenses/{id}
Parameters:
-
id(path, required): Expense ID
Request Body: Same as Add Expense
Response: 200 OK with updated expense or 404 Not Found
Endpoint: DELETE /api/expenses/{id}
Parameters:
-
id(path, required): Expense ID
Response: 200 OK with success message or 404 Not Found
These endpoints provide FAA aircraft registry lookups using the embedded SQLite database.
Retrieve information for a single aircraft by tail number.
Endpoint: GET /api/v1/aircraft/{tail}
Parameters:
-
tail(path, required): Aircraft tail number (N-number)- Formats accepted:
N172SP,172SP,N-172SP,n172sp - Case-insensitive
- 'N' prefix optional
- Dashes and spaces stripped
- Formats accepted:
Response: 200 OK with AircraftResponse JSON
Error: 404 Not Found if tail number not in database
Success Response Schema:
{
"tail_number": "string",
"manufacturer": "string",
"model": "string",
"series": "string | null",
"aircraft_type": "string",
"engine_type": "string",
"num_engines": "integer | null",
"num_seats": "integer | null",
"year_mfr": "integer | null"
}Example Request:
curl http://localhost:8080/api/v1/aircraft/N172SPExample Success Response:
{
"tail_number": "172SP",
"manufacturer": "CESSNA",
"model": "172S",
"series": "SKYHAWK SP",
"aircraft_type": "Fixed Wing Single-Engine",
"engine_type": "Reciprocating",
"num_engines": 1,
"num_seats": 4,
"year_mfr": 2001
}Example Error Response:
{
"detail": "Aircraft not found"
}Notes:
- Tail number is normalized before lookup (uppercase, strip N prefix and dashes)
- Optional fields (series, num_engines, num_seats, year_mfr) may be
nullif not in FAA data - Aircraft type and engine type are human-readable strings (mapped from FAA numeric codes)
Retrieve information for multiple aircraft in a single request.
Endpoint: POST /api/v1/aircraft/bulk
Request Body: JSON with array of tail numbers (max 50)
Request Schema:
{
"tail_numbers": ["string", "string", ...]
}Response: 200 OK with BulkResponse JSON
Response Schema:
{
"total": "integer",
"found": "integer",
"results": [
{
"tail_number": "string",
"manufacturer": "string | null",
"model": "string | null",
"series": "string | null",
"aircraft_type": "string | null",
"engine_type": "string | null",
"num_engines": "integer | null",
"num_seats": "integer | null",
"year_mfr": "integer | null",
"error": "string | null"
}
]
}Example Request:
curl -X POST http://localhost:8080/api/v1/aircraft/bulk \
-H "Content-Type: application/json" \
-d '{
"tail_numbers": ["N172SP", "N12345", "N99999"]
}'Example Response:
{
"total": 3,
"found": 2,
"results": [
{
"tail_number": "172SP",
"manufacturer": "CESSNA",
"model": "172S",
"series": "SKYHAWK SP",
"aircraft_type": "Fixed Wing Single-Engine",
"engine_type": "Reciprocating",
"num_engines": 1,
"num_seats": 4,
"year_mfr": 2001,
"error": null
},
{
"tail_number": "12345",
"manufacturer": "BOEING",
"model": "737-800",
"series": null,
"aircraft_type": "Fixed Wing Multi-Engine",
"engine_type": "Turbo-Fan",
"num_engines": 2,
"num_seats": 189,
"year_mfr": 2015,
"error": null
},
{
"tail_number": "99999",
"manufacturer": null,
"model": null,
"series": null,
"aircraft_type": null,
"engine_type": null,
"num_engines": null,
"num_seats": null,
"year_mfr": null,
"error": "Not found"
}
]
}Validation Rules:
- Maximum 50 tail numbers per request
- Each tail number normalized same as single lookup
- Empty array returns
total: 0, found: 0, results: []
Error Handling:
- Invalid tail numbers get
error: "Not found"in results array - Request doesn't fail if some tail numbers are invalid
- All requested tail numbers are returned in results (with or without error)
Example Error Response (422 Validation Error):
{
"detail": [
{
"type": "too_many_items",
"loc": ["body", "tail_numbers"],
"msg": "List should have at most 50 items after validation, not 51",
"input": [...],
"ctx": {"field_type": "List", "max_length": 50}
}
]
}Notes:
- Use bulk endpoint for batch processing, not single lookups
- Results are returned in same order as input
- Performance: ~5ms for 50 lookups (0.1ms per aircraft)
Check API and database health status.
Endpoint: GET /api/v1/health
Response: 200 OK with HealthResponse JSON
Response Schema:
{
"status": "string",
"database_exists": "boolean",
"record_count": "integer",
"last_updated": "string | null"
}Example Request:
curl http://localhost:8080/api/v1/healthExample Response:
{
"status": "healthy",
"database_exists": true,
"record_count": 297431,
"last_updated": "2025-11-28T06:15:23Z"
}Status Values:
-
"healthy": Database exists and has records -
"unhealthy": Database doesn't exist or has 0 records
Notes:
- Used by Docker health check
-
last_updatedshows when database was last built -
record_countvaries slightly as FAA registrations change (~297K-305K typical)
Get database statistics and information.
Endpoint: GET /api/v1/stats
Response: 200 OK with StatsResponse JSON
Response Schema:
{
"record_count": "integer",
"last_updated": "string | null"
}Example Request:
curl http://localhost:8080/api/v1/statsExample Response:
{
"record_count": 297431,
"last_updated": "2025-11-28T06:15:23Z"
}Notes:
- Similar to health check but without status field
- Used by web UI to display database info
Interactive browser-based interface for testing the API.
Endpoint: GET /
Response: HTML page (web interface)
Features:
- Single aircraft lookup with real-time validation
- Bulk lookup (paste multiple tail numbers, one per line)
- Database statistics display
- Dark theme
- Responsive design
Example:
Open http://localhost:8080 in your browser.
Interactive API documentation with request/response examples.
Endpoint: GET /docs
Response: Swagger UI HTML page
Features:
- Interactive API explorer
- Try-it-out functionality
- Request/response schemas
- Auto-generated from Pydantic models
Example:
Open http://localhost:8080/docs in your browser.
Alternative API documentation interface.
Endpoint: GET /redoc
Response: ReDoc HTML page
Features:
- Clean, readable documentation
- Three-panel layout
- Request/response examples
- Auto-generated from Pydantic models
Example:
Open http://localhost:8080/redoc in your browser.
Raw OpenAPI specification in JSON format.
Endpoint: GET /openapi.json
Response: JSON schema
Use Cases:
- Generate client libraries (e.g., with openapi-generator)
- Import into API tools (e.g., Postman, Insomnia)
- Integration with API gateways
Example:
curl http://localhost:8080/openapi.json > openapi.jsonRequests:
- Single lookup: N/A (path parameter)
- Bulk lookup:
application/json
Responses:
- JSON endpoints:
application/json - Web UI:
text/html - OpenAPI:
application/jsonortext/html
All endpoints use UTF-8 encoding.
CORS is enabled for all origins:
Access-Control-Allow-Origin: *Access-Control-Allow-Methods: GET, POST, OPTIONSAccess-Control-Allow-Headers: *
This allows web applications from any domain to call the API.
Currently no rate limiting implemented. Consider adding for production:
- Per-IP rate limiting
- API key-based quotas
- Bulk endpoint specific limits
All errors follow FastAPI's standard error format:
404 Not Found:
{
"detail": "Aircraft not found"
}422 Validation Error:
{
"detail": [
{
"type": "error_type",
"loc": ["location", "field"],
"msg": "Error message",
"input": "user_input"
}
]
}500 Internal Server Error:
{
"detail": "Internal server error"
}import requests
# Single lookup
response = requests.get("http://localhost:8080/api/v1/aircraft/N172SP")
if response.status_code == 200:
aircraft = response.json()
print(f"{aircraft['tail_number']}: {aircraft['manufacturer']} {aircraft['model']}")
else:
print("Not found")
# Bulk lookup
response = requests.post(
"http://localhost:8080/api/v1/aircraft/bulk",
json={"tail_numbers": ["N172SP", "N12345", "N99999"]}
)
data = response.json()
print(f"Found {data['found']} of {data['total']} aircraft")
for result in data['results']:
if result['error']:
print(f"{result['tail_number']}: {result['error']}")
else:
print(f"{result['tail_number']}: {result['manufacturer']} {result['model']}")// Single lookup
fetch('http://localhost:8080/api/v1/aircraft/N172SP')
.then(response => response.json())
.then(data => {
console.log(`${data.tail_number}: ${data.manufacturer} ${data.model}`);
})
.catch(error => console.error('Not found'));
// Bulk lookup
fetch('http://localhost:8080/api/v1/aircraft/bulk', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
tail_numbers: ['N172SP', 'N12345', 'N99999']
})
})
.then(response => response.json())
.then(data => {
console.log(`Found ${data.found} of ${data.total} aircraft`);
data.results.forEach(result => {
if (result.error) {
console.log(`${result.tail_number}: ${result.error}`);
} else {
console.log(`${result.tail_number}: ${result.manufacturer} ${result.model}`);
}
});
});# Single lookup (basic)
curl http://localhost:8080/api/v1/aircraft/N172SP
# Single lookup (pretty print with jq)
curl http://localhost:8080/api/v1/aircraft/N172SP | jq
# Bulk lookup
curl -X POST http://localhost:8080/api/v1/aircraft/bulk \
-H "Content-Type: application/json" \
-d '{"tail_numbers": ["N172SP", "N12345"]}'
# Health check
curl http://localhost:8080/api/v1/health
# Stats
curl http://localhost:8080/api/v1/stats
# Download OpenAPI spec
curl http://localhost:8080/openapi.json > openapi.json#!/bin/bash
# Lookup multiple tail numbers from file (one per line)
while IFS= read -r tail; do
echo -n "$tail: "
curl -s "http://localhost:8080/api/v1/aircraft/$tail" | \
jq -r 'if .detail then "Not found" else "\(.manufacturer) \(.model)" end'
done < tail_numbers.txtFunction LookupAircraft(tailNumber As String) As String
Dim http As Object
Dim url As String
Dim response As String
Set http = CreateObject("MSXML2.XMLHTTP")
url = "http://localhost:8080/api/v1/aircraft/" & tailNumber
http.Open "GET", url, False
http.Send
If http.Status = 200 Then
' Parse JSON response (simplified)
LookupAircraft = http.responseText
Else
LookupAircraft = "Not found"
End If
End FunctionThe API normalizes all tail numbers before lookup:
- Convert to uppercase
- Strip leading 'N' if present
- Remove dashes and spaces
- Trim whitespace
Examples:
-
N172SPβ172SP -
n172spβ172SP -
N-172SPβ172SP -
172SPβ172SP -
N 172 SPβ172SP
Human-readable strings mapped from FAA numeric codes:
- "Glider"
- "Balloon"
- "Blimp/Dirigible"
- "Fixed Wing Single-Engine"
- "Fixed Wing Multi-Engine"
- "Rotorcraft"
- "Weight-Shift-Control"
- "Powered Parachute"
- "Gyroplane"
- "Hybrid Lift"
- "Other"
Human-readable strings mapped from FAA numeric codes:
- "None"
- "Reciprocating" (piston)
- "Turbo-Prop"
- "Turbo-Shaft"
- "Turbo-Jet"
- "Turbo-Fan"
- "Ramjet"
- "2 Cycle"
- "4 Cycle"
- "Unknown"
- "Electric"
- "Rotary"
These fields may be null if not in FAA database:
-
series: Aircraft series/variant name -
num_engines: Number of engines -
num_seats: Number of seats -
year_mfr: Year of manufacture -
last_updated: Database update timestamp (if never built)
Use OpenAPI Generator to create client libraries:
# Download OpenAPI spec
curl http://localhost:8080/openapi.json > openapi.json
# Generate Python client
openapi-generator-cli generate \
-i openapi.json \
-g python \
-o ./client-python
# Generate TypeScript client
openapi-generator-cli generate \
-i openapi.json \
-g typescript-fetch \
-o ./client-typescript
# Generate Go client
openapi-generator-cli generate \
-i openapi.json \
-g go \
-o ./client-goFeel free to create and share client libraries for your language of choice. The OpenAPI spec makes it easy to generate type-safe clients.
Single Lookup: ~1-2ms (local), ~10-50ms (network) Bulk Lookup (50 aircraft): ~5-10ms (local), ~20-100ms (network) Health Check: <1ms Stats: <1ms
Single requests: ~1000 req/sec on modest hardware Bulk requests: ~100 req/sec (50 aircraft each = 5000 lookups/sec)
- Use bulk endpoint for multiple lookups (one request vs many)
- Cache responses client-side (data changes once daily)
- Parallel requests if needed (API is async, handles concurrent well)
- Connection pooling for high-volume usage
- Normalize tail numbers client-side before sending (optional, API does it anyway)
- Handle null fields gracefully (not all aircraft have complete data)
- Check error field in bulk responses (some lookups may fail)
- Cache aggressively (data only updates once daily)
- Use bulk endpoint for batch processing (more efficient)
- Monitor health endpoint to detect stale data or outages
- Handle 404 gracefully (not all N-numbers are registered)
404 Not Found for valid tail number:
- Verify tail number is correct (check FAA registry)
- Aircraft may be deregistered or not yet registered
- Database may be stale (check health endpoint)
422 Validation Error:
- Check request format matches schema
- Ensure max 50 tail numbers for bulk
- Verify Content-Type header is application/json
Connection Refused:
- Verify API is running (
docker psor check process) - Check port mapping (default 8080)
- Verify firewall allows connections
Slow responses:
- Check database size (should be ~25MB)
- Verify SQLite index exists
- Monitor system resources
Stale data:
- Check last_updated in health endpoint
- Verify nightly build workflow is running
- Trigger manual workflow run if needed
π View on GitHub | π³ Docker Hub
π Report Issue | π¬ Discussions
License: MIT License | Copyright (c) 2024-2025 FliteAxis
π Getting Started
π¦ Deployment
π§ Development
π Security
- Security Setup Guide
- Security CI/CD Pipeline
- Code Quality & Linting
- SBOM Management
- Vulnerability Scanning
π Dependencies
π³ Docker
π Reference
π Links