Skip to content

United Kingdom ๐Ÿ‡ฌ๐Ÿ‡ง CAA Registry โ€‹

Nameuk-caa-registry
CountryUnited Kingdom
Registration prefixG-
Data sourcehttps://ginfoapi.caa.co.uk (G-INFO REST API)
FormatJSON API (search + details endpoints; no bulk download)
Run frequencyWeekly (Monday, 06:10 UTC)
Depends on Mictronics for ICAO hexNo โ€” the G-INFO details payload includes the aircraft's ICAO 24-bit address directly (AircraftDetails.ICAO24BitAircraftAddress.Hex).

How it works โ€‹

G-INFO has no bulk-export endpoint, so this runner enumerates the entire register itself: it POSTs /api/aircraft/search once for every 2-letter suffix combination from AA to ZZ (676 calls total, via itertools.product), then, for each result with RegistrationStatus == "R", immediately calls GET /api/aircraft/details/{id} and writes the record โ€” there is no intermediate accumulation. Search calls are not rate-limited; details calls sleep for request_interval_seconds between requests to be polite to the API. Any call that returns HTTP 403 is queued and retried once at a fixed 500ms interval after the main enumeration pass finishes. Lookup tables decode the API's free-text AircraftClass into canonical aircraft.type/aircraft.category values and full English country names into ISO 3166-1 alpha-2 codes, falling back to the raw source value for anything unmapped. Every written record explicitly sets military: false โ€” this register is exclusively civil, and the explicit value ensures a stale military: true flag (from Mictronics or a prior record on a reused hex) is corrected on re-registration.

Whenever a record has an aircraft.type_designator, aircraft:type:{type_designator} is looked up in Redis (populated by the mictronics runner) and, if found, its manufacturer_model and description_code (ICAO Doc 8643 description code, e.g. L2J) are set directly on this record โ€” unconditionally, regardless of whether Mictronics also has values for the same hex. This runner's own type_designator is sourced directly from the CAA and is authoritative; merge_aircraft.lua's "registry wins over mictronics" precedence rule guarantees these values take priority at read time either way. The lookup is not a hard dependency โ€” a missing reference table entry, or the table not existing yet, leaves the record exactly as it would have been without this step.

Notable operational characteristic: because it makes 676 prefix search calls plus a details fetch per registered aircraft, a full run takes several hours. It is deliberately scheduled as the last runner of the day by the ofelia service's labels in docker-compose.core.yaml (0 10 6 * * 1, after ourairports at 0 10 5 * * 1) to avoid overlapping with other Monday jobs.

Columns โ€‹

Fields below are grouped by the payload they come from: the POST /api/aircraft/search response (list of match summaries) and the GET /api/aircraft/details/{id} response (full nested record). Field names are otherwise unique except where noted as a duplicate.

Search response (POST /api/aircraft/search) โ€‹

Source fieldImportedNotes
AircraftIDโœ…Used to fetch the details payload; not stored
RegistrationStatusโœ…Filters which AircraftIDs get a details fetch ("R" only); not stored
MarkโŒDuplicate of RegistrationDetails.Mark, which is read from the details payload instead
SerialNoโŒDuplicate of AircraftDetails.SerialNumber, which is read from the details payload instead
MilitarySerialNoโŒPresent in source; never read (no equivalent field exists in the details payload)
AircraftTypeโŒDuplicate of AircraftDetails.Type, which is read from the details payload instead

Details response (GET /api/aircraft/details/{id}) โ€‹

Source fieldImportedNotes
RegistrationDetails.Markโœ…โ†’ registration (prefixed with G-)
RegistrationDetails.Statusโœ…Filtered to "Registered" only; not stored
RegistrationDetails.PreviousIDโŒPresent in source; not read by this runner
RegistrationDetails.CurrentRegistrationDateโŒPresent in source; not read by this runner
RegistrationDetails.DeRegistrationDateโŒPresent in source; not read by this runner
RegistrationDetails.ReasonโŒPresent in source; not read by this runner
RegistrationDetails.ToโŒPresent in source; not read by this runner
AircraftDetails.Manufacturerโœ…โ†’ aircraft.manufacturer
AircraftDetails.Typeโœ…โ†’ aircraft.model
AircraftDetails.SerialNumberโœ…โ†’ aircraft.serial_number
AircraftDetails.ICAO24BitAircraftAddress.Hexโœ…โ†’ icao_hex; records without a 6-character hex are discarded
AircraftDetails.ICAO24BitAircraftAddress.BinaryโŒPresent in source; not read by this runner
AircraftDetails.ICAO24BitAircraftAddress.OctalโŒPresent in source; not read by this runner
AircraftDetails.PopularNameโŒPresent in source; not read by this runner
AircraftDetails.ICAOAircraftTypeDesignatorโœ…โ†’ aircraft.type_designator; also used to look up aircraft:type:{type_designator} in Redis, setting aircraft.manufacturer_model and aircraft.description_code when found
AircraftDetails.AircraftClassโœ…โ†’ aircraft.type / aircraft.category (decoded)
AircraftDetails.BuildCategoryโŒPresent in source; not read by this runner
AircraftDetails.EasaCategoryโŒPresent in source; not read by this runner
AircraftDetails.Engines[0].TotalNumberOfEnginesโœ…โ†’ aircraft.powerplant.count
AircraftDetails.Engines[0].Nameโœ…โ†’ aircraft.powerplant.model; only the first engine entry is used
AircraftDetails.Engines[0].NumberOfEnginesโŒDistinct field from TotalNumberOfEngines (which is used); present in source, not read
AircraftDetails.Engines[0].IsPropellerโŒPresent in source; not read by this runner
AircraftDetails.MtowโŒPresent in source; not read by this runner
AircraftDetails.TotalAirframeHoursโŒPresent in source; not read by this runner
AircraftDetails.YearBuildโœ…โ†’ aircraft.manufactured_date
AircraftDetails.MaintenanceProgrammeโŒPresent in source; not read by this runner
AircraftDetails.ExpiryโŒPresent in source; not read by this runner
AircraftDetails.OwnershipStatusโŒPresent in source; not read by this runner
AircraftDetails.MaximumPassengersโœ…โ†’ aircraft.seats (MaximumPassengers + 1)
AircraftDetails.MaximumTakeOffMassโŒPresent in source; not read by this runner (numeric duplicate of Mtow)
AircraftDetails.CofaDetails.CofaCategoryโŒPresent in source; not read by this runner
AircraftDetails.CofaDetails.CofaValidityExpiryLabelโŒPresent in source; not read by this runner
AircraftDetails.CofaDetails.CofaValidityExpiryโŒPresent in source; not read by this runner
AircraftDetails.CofaDetails.CofaValidityReferenceโŒPresent in source; not read by this runner
AircraftDetails.CofaDetails.CofaOperatingLimitationsโŒPresent in source; not read by this runner
AircraftDetails.CofaDetails.ShowOperatingLimitationsโŒPresent in source; not read by this runner
AircraftDetails.IsPotentialChangeOfOwnershipโŒPresent in source; not read by this runner
AircraftDetails.EmergencyBallisticDeviceFittedโŒPresent in source; not read by this runner
MortgageDetails[].EntryDateAndTimeโŒPresent in source; not read by this runner
MortgageDetails[].EntryTypeโŒPresent in source; not read by this runner
MortgageDetails[].ReferenceTypeโŒPresent in source; not read by this runner
RegisteredAircraftOwners[0].RegisteredOwnerโœ…โ†’ registrant.names[0]; only the first owner is used
RegisteredAircraftOwners[0].AddressIndicatorIDโŒPresent in source; not read by this runner
RegisteredAircraftOwners[0].AddressIndicatorโŒPresent in source; not read by this runner
RegisteredAircraftOwners[0].TradingAsโŒPresent in source; not read by this runner
RegisteredAircraftOwners[0].TrusteeOfโŒPresent in source; not read by this runner
RegisteredAircraftOwners[0].Address1 / Address2โœ…โ†’ registrant.street
RegisteredAircraftOwners[0].Townโœ…โ†’ registrant.city
RegisteredAircraftOwners[0].Countyโœ…โ†’ registrant.administrative_area
RegisteredAircraftOwners[0].PostCodeโœ…โ†’ registrant.postal_code
RegisteredAircraftOwners[0].Countryโœ…โ†’ registrant.country (decoded to ISO 3166-1 alpha-2 where known)
AircraftOperatedByAocHolder.showSectionโŒPresent in source; not read by this runner
AircraftOperatedByAocHolder.OperatorNameโŒPresent in source; not read by this runner (operator enrichment for G-registered aircraft is not sourced from here)
AircraftOperatedByAocHolder.OperatorTrandingAsโŒPresent in source (sic โ€” typo in the live API); not read by this runner
InsuranceSummaryDetails.EvidenceVerifiedDateโŒPresent in source; not read by this runner
InsuranceSummaryDetails.NoFlyDateโŒPresent in source; not read by this runner
InsuranceSummaryDetails.AircraftMarkStatusIDโŒPresent in source; not read by this runner
InsuranceSummaryDetails.AircraftIDโŒPresent in source; not read by this runner (duplicate of the search result's AircraftID)
InsuranceSummaryDetails.MarkโŒPresent in source; not read by this runner (duplicate of RegistrationDetails.Mark)
NoiseSummaryDetails.NoiseCertificateIssueDateโŒPresent in source; not read by this runner
NoiseSummaryDetails.NoiseCertificateNumberโŒPresent in source; not read by this runner
NoiseSummaryDetails.NoiseChapterโŒPresent in source; not read by this runner
RegistrationMarkExemption.IssueDateโŒPresent in source; not read by this runner
RegistrationMarkExemption.OriginโŒPresent in source; not read by this runner
RegistrationMarkExemption.CodeCarriedโŒPresent in source; not read by this runner
RegistrationMarkExemption.SerialCarriedโŒPresent in source; not read by this runner
OpenRegistrationCases[].CaseNameโŒPresent in source; not read by this runner
OpenRegistrationCases[].OpenDateโŒPresent in source; not read by this runner
OpenRegistrationCases[].ReviewDateโŒPresent in source; not read by this runner
OpenRegistrationCases[].PotentialChangeOfOwnershipโŒPresent in source; not read by this runner
OpenApplicationsAndApprovalsCases[].CaseNameโŒPresent in source; not read by this runner
OpenApplicationsAndApprovalsCases[].OpenDateโŒPresent in source; not read by this runner
OpenApplicationsAndApprovalsCases[].ReviewDateโŒPresent in source; not read by this runner
OpenApplicationsAndApprovalsCases[].PotentialChangeOfOwnershipโŒPresent in source; not read by this runner
ExtractDateโŒPresent in source; not read by this runner

See specs/data-dictionary.yaml (uk-caa-registry entry) for full column semantics and cross-source schema notes.

Example Output โ€‹

Read back the merged record for a given ICAO hex (combines this runner's data with Mictronics and any other sources that have written to the same key):

bash
docker run --rm --network host redis:latest redis-cli EVAL "$(cat ./shared/lua/merge_aircraft.lua)" 0 401A48 | python3 -m json.tool --sort-keys --no-ensure-ascii
json
{
    "aircraft": {
        "category": "Land",
        "manufactured_date": "1910-01-01T00:00:00Z",
        "manufacturer": "DEPERDUSSIN CIE",
        "manufacturer_model": "DEPERDUSSIN CIE DEPERDUSSIN MONOPLANE",
        "model": "DEPERDUSSIN MONOPLANE",
        "powerplant": {
            "count": 1,
            "model": "ANZANI Y TYPE"
        },
        "seats": 1,
        "serial_number": "43",
        "type": "Airplane",
        "type_designator": "ULAC"
    },
    "data_sources": [
        "uk-caa-registry"
    ],
    "icao_hex": "401A48",
    "military": false,
    "registrant": {
        "city": "BIGGLESWADE",
        "country": "GB",
        "names": [
            "RICHARD SHUTTLEWORTH TRUSTEES"
        ],
        "postal_code": "SG18 9ER",
        "street": [
            "OLD WARDEN AERODROME",
            "OLD WARDEN"
        ]
    },
    "registration": "G-AANH"
}
bash
docker run --rm --network host redis:latest redis-cli EVAL "$(cat ./shared/lua/merge_aircraft.lua)" 0 407F19 | python3 -m json.tool --sort-keys --no-ensure-ascii
json
{
    "aircraft": {
        "category": "Land",
        "description_code": "L2J",
        "manufactured_date": "2023-01-01T00:00:00Z",
        "manufacturer": "AIRBUS SAS",
        "manufacturer_model": "AIRBUS A-350-1000",
        "model": "AIRBUS A350-1041",
        "powerplant": {
            "count": 2,
            "model": "ROLLS-ROYCE Trent XWB-97"
        },
        "seats": 336,
        "serial_number": "605",
        "type": "Airplane",
        "type_designator": "A35K"
    },
    "data_sources": [
        "mictronics",
        "uk-caa-registry"
    ],
    "icao_hex": "407F19",
    "military": false,
    "registrant": {
        "city": "CRAWLEY",
        "country": "GB",
        "names": [
            "VIRGIN ATLANTIC AIRWAYS LTD"
        ],
        "postal_code": "RH10 9DF",
        "street": [
            "THE VHQ",
            "FLEMING WAY"
        ]
    },
    "registration": "G-VBOB"
}

Configuration โ€‹

See Data Runners for the full list of environment variables every runner reads. This runner writes aircraft:registry:{icao_hex} with a fixed 14-day TTL (ENRICHMENT_TTL_SECONDS in shared/timing.py).

MQTT โ€‹

Published once, at the end of a run, to SkyFollower/runner/uk-caa-registry/statistic/{name} (all retained):

Topic suffixValueFormat
records_importede.g. 271Integer as string
last_run_ate.g. 2026-07-07T14:32:01.123456+00:00ISO 8601 UTC
last_run_statusSuccess or FailureString

Home Assistant autodiscovery configs are also published (retained) to homeassistant/sensor/SkyFollower_runner_uk_caa_registry_{name}/config for each of the three stats above.