REST API Documentation
Programmatic access to aircraft and museum data
Authentication
The API uses Bearer token authentication. Include your API key in the Authorization header:
Authorization: Bearer amt_your_api_key_here
There are three permission levels:
Search aircraft, museums, proximity lookups. All public endpoints work without a key too.
Everything in read, plus create and update aircraft, museums, and exhibit links.
Everything in readwrite, plus delete records.
Keys are limited by the owner's current role and museum/country assignments. Disabled accounts cannot use keys. Admin and aircraft_admin have full catalog access; managers can edit assigned museums and their exhibit links. Only admin sessions can manage other users. Session writes require CSRF tokens; adding a Bearer header does not bypass CSRF for session-only endpoints.
Complete route index
Generated from the running application's routes. PUT and PATCH apply partial updates. Session endpoints require login; users may access their own details and keys, while managing other users requires an admin session.
| Method | Route | Access |
|---|---|---|
| POST | /api/v1/aircraft | readwrite |
| GET | /api/v1/aircraft-types | Public |
| POST | /api/v1/aircraft-types | readwrite |
| DELETE | /api/v1/aircraft-types/<int:type_id> | admin |
| GET | /api/v1/aircraft-types/<int:type_id> | Public |
| PATCH, PUT | /api/v1/aircraft-types/<int:type_id> | readwrite |
| GET | /api/v1/aircraft-types/resolve | Public |
| DELETE | /api/v1/aircraft/<int:aircraft_id> | admin |
| GET | /api/v1/aircraft/<int:aircraft_id> | Public |
| PATCH, PUT | /api/v1/aircraft/<int:aircraft_id> | readwrite |
| GET | /api/v1/aircraft/<int:aircraft_id>/history | Public |
| POST | /api/v1/aircraft/<int:aircraft_id>/history | readwrite |
| GET | /api/v1/aircraft/<int:aircraft_id>/history/manage | readwrite |
| POST | /api/v1/aircraft/bulk_import | admin |
| GET | /api/v1/aircraft/search | Public |
| GET | /api/v1/contributors | Public |
| GET | /api/v1/docs | Public |
| GET | /api/v1/exhibits | Public |
| POST | /api/v1/exhibits | readwrite |
| DELETE | /api/v1/exhibits/<int:link_id> | admin |
| PATCH, PUT | /api/v1/exhibits/<int:link_id> | readwrite |
| GET | /api/v1/facts | Public |
| POST | /api/v1/facts | readwrite |
| DELETE | /api/v1/facts/<int:fact_id> | admin |
| PATCH, PUT | /api/v1/facts/<int:fact_id> | readwrite |
| GET | /api/v1/facts/random | Public |
| DELETE | /api/v1/history/<int:event_id> | admin |
| PATCH, PUT | /api/v1/history/<int:event_id> | readwrite |
| GET | /api/v1/keys | Session |
| POST | /api/v1/keys | Session |
| DELETE | /api/v1/keys/<int:key_id> | Session |
| POST | /api/v1/museums | readwrite |
| DELETE | /api/v1/museums/<int:museum_id> | admin |
| GET | /api/v1/museums/<int:museum_id> | Public |
| PATCH, PUT | /api/v1/museums/<int:museum_id> | readwrite |
| POST | /api/v1/museums/bulk_import | admin |
| GET | /api/v1/museums/countries | Public |
| GET | /api/v1/museums/globe | Public |
| GET | /api/v1/museums/map | Public |
| GET | /api/v1/museums/nearby | Public |
| GET | /api/v1/museums/nearest | Public |
| GET | /api/v1/museums/regions | Public |
| GET | /api/v1/museums/search | Public |
| GET | /api/v1/nearest | Public |
| GET | /api/v1/stats | Public |
| GET | /api/v1/templates | Public |
| POST | /api/v1/templates | readwrite |
| DELETE | /api/v1/templates/<int:template_id> | admin |
| GET | /api/v1/templates/<int:template_id> | Public |
| PATCH, PUT | /api/v1/templates/<int:template_id> | readwrite |
| POST | /api/v1/trips/plan | Public |
| POST | /api/v1/trips/route | Public |
| GET | /api/v1/users | Session |
| POST | /api/v1/users | Session |
| DELETE | /api/v1/users/<int:user_id> | Session |
| GET | /api/v1/users/<int:user_id> | Session |
| PATCH, PUT | /api/v1/users/<int:user_id> | Session |
| GET | /api/v1/users/<int:user_id>/keys | Session |
| POST | /api/v1/users/<int:user_id>/keys | Session |
Query and import conventions
Aircraft and museum searches return results, total, page, and pages. per_page defaults to RESULTS_PER_PAGE (20) and is capped at 100; fetch subsequent pages to retrieve the full catalog. sort_by names a supported field and sort_dir is asc or desc.
Detail requests include all exhibits by default. Add visible_only=true to show only on_display exhibits, as the public UI does. Other valid statuses are in_storage and under_restoration. Statistics count only on_display exhibit links.
GET /api/v1/museums/globe returns coordinates and on-display aircraft counts. GET /api/v1/museums/nearby takes location, optional region, and limit (default 10, maximum 50); museums without coordinates are listed separately. GET /api/v1/museums/nearest takes lat and lon or location, optional radius in miles, and limit (default 10, maximum 50). GET /api/v1/contributors lists contributor rankings.
Aircraft and museum bulk_import endpoints require admin-level data access. POST a multipart file or a JSON object with format (csv, json, or auto), data (text), and dry_run (boolean). The limits are 5,000 rows, the configured request-size limit (1 MiB by default), and BULK_IMPORT_RATE_LIMIT (200 per hour by default). Validation includes duplicates already in the database; writes are atomic. Aircraft rows may supply museum_id or museum_name to create an exhibit link.
Templates require name, manufacturer, and model; optional type fields and aliases prefill new aircraft without changing existing airframes. API key creation accepts label and permissions. POST /api/v1/keys also accepts expires_in_days; raw keys are returned once. JSON flags such as is_active and dry_run must be booleans, not strings.
Histories, map, and trip planning
GET /api/v1/aircraft/{id}/history lists published milestones; /history/manage requires readwrite access and includes drafts. POST on /history creates a milestone; PUT/PATCH /api/v1/history/{event_id} edits one; DELETE requires admin data access. Entries require a title and source_name or source_url. Optional integer event_year, event_month, event_day preserve date precision; is_approximate and is_published are booleans. Histories belong to the individual aircraft ID and do not change current exhibit assignments.
GET /api/v1/museums/map accepts q, region, country and returns all matching museums in results and no_coordinates, with total and on-display aircraft_count. The museum map supports clusters and area search.
POST /api/v1/trips/plan accepts origin (location text, or latitude and longitude), targets (1–12 objects), radius_miles (1–5000, default 500), max_stops (1–8, default 5), and round_trip (boolean). Targets use kind=airframe plus aircraft_id, or kind=model plus manufacturer and model. The public endpoint requires a CSRF token and is limited to 30 requests per minute. It returns ordered stops, covered targets, unmatched reasons, and straight-line mileage. Suggestions use on_display exhibits only and do not calculate driving time or opening hours. Try the trip planner.
Endpoints
Aircraft
/api/v1/aircraft/search
public
Search aircraft by tail number, model, variant, name, manufacturer, or alias (e.g. "B29", "Superfortress").
q | Search query (optional) |
page | Page number (default 1) |
per_page | Results per page (default 20, max 100) |
/api/v1/aircraft/{id}
public
Get aircraft details with all museum locations.
/api/v1/aircraft
readwrite
Create a new aircraft. Send JSON body with manufacturer (required), model (required), and optional fields: variant, tail_number, model_name, aircraft_name, role_type, year_built, description, aliases (array of strings), aircraft_type (fixed_wing | rotary_wing | lighter_than_air | spacecraft | missile_rocket), wing_type (monoplane | biplane | triplane), military_civilian (military | civilian), museum_id + display_status to assign to a museum on creation.
/api/v1/aircraft/{id}
readwrite
Update an aircraft record. Send JSON body with any fields to update.
/api/v1/aircraft/{id}
admin
Delete an aircraft and all its exhibit links.
Museums
/api/v1/museums/search
public
Search museums by name, city, state/province, or country. Optionally filter by region or country.
q | Search query (optional) |
region | Filter by region: North America, Europe, Asia, Asia-Pacific, Middle East, South America, Africa, Oceania |
country | Filter by country name |
state | Filter by state/province name |
page | Page number |
per_page | Results per page (max 100) |
/api/v1/museums/{id}
public
Get museum details with its full aircraft collection.
/api/v1/museums/nearest
public
Museums near a point, closest first — no aircraft filter. Answers "what's near me?", where /api/v1/nearest answers "where can I see a B-17?". aircraft_count reflects only aircraft on display.
lat, lon | Decimal degrees. Takes precedence when both are supplied — mobile passes these straight from the browser's geolocation API. |
location | Zip/postal code or city name, used when lat/lon are absent |
radius | Optional cap in miles |
limit | Max results (default 10, max 50) |
/api/v1/museums/regions
public
List all regions with museum counts.
/api/v1/museums/countries
public
List all countries with museum counts.
/api/v1/museums
readwrite
Create a new museum. Required fields: name, city, country, region. Optional: state_province, postal_code, address, website, access_type, latitude, longitude. access_type is one of public (default), appointment or restricted; proximity endpoints hide restricted museums unless called with include_restricted=1.
/api/v1/museums/{id}
readwrite
Update a museum record.
/api/v1/museums/{id}
admin
Delete a museum and all its exhibit links.
Exhibits (Aircraft-Museum Links)
/api/v1/exhibits
public
List every aircraft-museum link as a flat array — one row per exhibit, each carrying the full aircraft and museum objects.
q | Filter by aircraft designation, name, manufacturer, tail number, or museum name/city (optional) |
sort_by | Sort column: id, aircraft, museum, status (optional) |
sort_dir | asc (default) or desc |
/api/v1/exhibits
readwrite
Link an aircraft to a museum. Required: aircraft_id, museum_id. Optional: display_status (on_display, in_storage, under_restoration), notes.
/api/v1/exhibits/{id}
readwrite
Update exhibit status or notes.
/api/v1/exhibits/{id}
admin
Remove an exhibit link.
Aircraft Types (shared type information)
A type is one write-up per designation, inherited at render time by every
airframe whose normalized model + variant matches. Editing one record changes
every matching aircraft page at once. Nothing is stored on the aircraft row and there is no backfill:
a newly imported airframe inherits on its next page load.
Matching. The key is model + variant
uppercased with every non-alphanumeric character dropped, so ("F-4","C") and
("F-4C",null) both resolve to the same record. It is not scoped by manufacturer,
which is deliberate: a Fuji-built UH-1H is still a UH-1H and inherits Bell's write-up.
Resolution prefers an exact model+variant record, then falls back to a
variant-less base record. Most entries should be base records — one
UH-1 serves every UH-1 — and a variant record is worth adding only when that variant
genuinely needs its own text. Below both tiers sits the alias tier described next, so a real record
always wins over an alias of the same specificity.
manufacturer_scope restricts a type to airframes whose
manufacturer matches, and a scoped type never applies to one it does not. Set it only where an
unrelated aircraft shares the designation string (S-2 is both a Grumman Tracker and a
Pitts biplane; 47 and 737 are bare numbers). Matching is normalized prefix
in either direction, so Bell accepts Bell Helicopter. Scoping an ordinary
type is a mistake — it strips the write-up from every licence-built airframe.
aliases are alternate designations that reach the same record.
Aviation gives one aeroplane several names — a T-6 Texan is also an AT-6 and an
SNJ, a Canadair F-104 is a CF-104, a Polish MiG-15 is a Lim-2 —
and the catalog records whichever one is painted on the airframe. An alias is a second match key
pointing at an existing type: no aircraft row changes and no text is duplicated. Send
aliases as a list of strings, or of
{"designation": "204", "manufacturer_scope": "Bell"} objects. The list you send
replaces the current one, so drop an alias by PATCHing the list without it.
A bare-number alias without a scope is refused — unscoped 204 would attach the UH-1
write-up to every airframe spelled 204. An alias may not shadow a real type or another type's alias,
and ranks below a real record of the same specificity, so writing an actual Su-17 type
later supersedes the alias pointing Su-17 at the Su-22 without anyone deleting it.
Uniqueness is (designation, manufacturer_scope). A second record for
the same pair returns 409 with existing_id, so a repeated POST is safe to
retry as a PUT.
/api/v1/aircraft-types
public
List types, ordered by model then variant. Anonymous callers see published records only; any authenticated identity also sees drafts.
q | Fuzzy match on display name, model, type name or manufacturer. A designation typed without punctuation (mig21) also matches (optional) |
/api/v1/aircraft-types/{id}
public
One type, plus inherits_count — how many airframes currently show this write-up. Check it before editing a base record. An unpublished type returns 404 to anonymous callers.
/api/v1/aircraft-types/resolve
public
Ask what a designation would inherit, without writing anything. Returns resolved (the type or null), matched_on (variant, base or null), matched_via_alias (the alias it came in by, or null), match_key and scoped.
model | Designation, e.g. T-33 (required) |
variant | e.g. A (optional) |
manufacturer | Needed to resolve a scoped type; pass it whenever you have it (optional) |
/api/v1/aircraft-types
readwrite
Create a type. Required: model, display_name, description. Omit variant for a base record.
model | Designation exactly as the catalog records it, e.g. T-33 (required) |
variant | Omit for a base type that serves every variant |
display_name | Heading, e.g. Lockheed T-33 Shooting Star (required) |
description | The write-up. Separate paragraphs with a blank line (\n\n) (required) |
manufacturer_scope | Restrict to one builder. Leave unset unless the designation string is genuinely ambiguous |
aliases | Alternate designations reaching this same write-up, e.g. ["AT-6", "SNJ"]. Replaces the current list |
manufacturer | Original designer, shown as provenance only |
model_name, also_built_by, origin_country | Type name (Shooting Star), licence builders, ISO 3166-1 alpha-2 country of design |
aircraft_type | fixed_wing (default), rotary_wing, lighter_than_air, spacecraft, missile_rocket |
role_type, wing_type, military_civilian | Same vocabularies as an aircraft record |
spec_basis | Which variant the figures below describe, e.g. T-33A. Set it whenever you publish any figure — a base type covers every variant but its numbers cannot |
first_flight_year, introduced_year, retired_year, number_built | Integers. Leave retired_year null if still in service anywhere |
crew, engines | Free text, e.g. 2 and 1 x Allison J33-A-35 turbojet |
length_m, wingspan_m, height_m | Metres. For a helicopter put main rotor diameter in wingspan_m |
max_speed_kmh, range_km, ceiling_m | Integers, metric |
source_name, source_url, wikipedia_url | Attribution shown under the card |
is_published | Boolean, default true. False keeps the record editable in admin and off every public page |
Returns 400 for a missing required field or an unparseable number, and 409 with existing_id if this designation and scope already exist.
/api/v1/aircraft-types/{id}
readwrite
Partial update — send only the fields you are changing, which is the normal way to revise a description. Changing model, variant or manufacturer_scope moves the whole inheriting set and rebuilds the slug.
/api/v1/aircraft-types/{id}
admin
Delete a type. No aircraft record is touched; matching airframes simply stop showing the card. To hide a write-up reversibly, PUT is_published: false instead.
Aviation Facts
/api/v1/facts
public
List facts. Hidden facts are excluded unless include_inactive=true.
aircraft_id | Only facts about this aircraft |
include_inactive | Include hidden facts |
/api/v1/facts/random
public
One random active fact. Optional aircraft_id narrows it to a single airframe. Returns 404 when no facts exist.
/api/v1/facts
readwrite
Add a fact. Required: fact (max 5000 chars). Optional: source_url, aircraft_id to attach it to a specific airframe.
/api/v1/facts/{id}
readwrite
Update fact, source_url, aircraft_id or is_active. Set is_active=false to hide a fact without deleting it.
/api/v1/facts/{id}
admin
Delete a fact permanently. Prefer is_active=false to hide one.
Proximity
/api/v1/nearest
public
Find the nearest museum(s) displaying a specific aircraft, measured from a zip/postal code or city. Supports alias search (e.g. "B29", "Superfortress"). Museums without coordinates are returned separately.
aircraft | Search term for aircraft — matches model, name, tail, or alias (required) |
location | Zip/postal code or city name (required) |
museum | Filter by museum name (optional) |
limit | Max results (default 5, max 25) |
Statistics
/api/v1/stats
public
Get counts of aircraft, museums, exhibit links, and countries.