daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

transport-tracking.md (32651B)


      1 ---
      2 title: "Aircraft, Vessel & Vehicle Tracking"
      3 description: "Track flights and ships live and historically, and understand the gaps operators use to disappear."
      4 category: osint
      5 subcategory: "Transport"
      6 tags: [osint, aviation, maritime, adsb, ais]
      7 tools: [opensky, pyopensky, adsb-lol, adsbexchange, readsb, flightradar24, marinetraffic, equasis, global-fishing-watch]
      8 difficulty: intermediate
      9 updated: 2026-10-03
     10 references:
     11   - name: "Bellingcat's Online Investigation Toolkit"
     12     url: "https://bellingcat.gitbook.io/toolkit"
     13     author: "Bellingcat"
     14     license: none
     15     relation: derived
     16     note: "Tool catalogue: names, descriptions, cost flags and links for this area."
     17   - name: "OSINT Newsletter Tools Library"
     18     url: "https://tools.osintnewsletter.com"
     19     author: "The OSINT Newsletter"
     20     license: none
     21     relation: derived
     22     note: "Second tool catalogue, cross-checked against the above."
     23 ---
     24 
     25 ## What this covers
     26 
     27 Aircraft and vessels broadcast their own positions, which makes transport one of the few OSINT
     28 domains with a structured, queryable, global feed behind it. The important knowledge is not which
     29 website to open — it is where the data has gaps, because the gaps are where the interesting
     30 behaviour is, and distinguishing a coverage gap from an evasion is the whole skill.
     31 
     32 ## Method
     33 
     34 1. **Pin the durable identifier first.** For aircraft that is the ICAO 24-bit hex address; for
     35    vessels the IMO number. Registrations, callsigns, names and flags all change, frequently on
     36    purpose, and a track assembled on a callsign will silently merge two aircraft.
     37 2. **Look at live data on a map to orient yourself**, then stop using the map. Anything you intend
     38    to assert needs the underlying records, because a screenshot of a map is not reproducible and
     39    the free sites retain only days.
     40 3. **Pull the track from an API and store it.** Positions, timestamps and the receiver metadata if
     41    you can get it. Historical ADS-B and AIS are the parts that cost money, so capture while you can
     42    see it rather than planning to come back.
     43 4. **Resolve the operator separately from the registered owner.** Aircraft sit in trusts and
     44    leasing companies; ships sit under flags of convenience with a manager in a third country. The
     45    tracking feed names neither — that is a
     46    [corporate records](/sheets/osint/companies-and-finance) problem.
     47 5. **Characterise the gap before you call it a gap.** Establish the coverage baseline for that area
     48    and that hour, from a second receiver network if possible, then show the vessel or aircraft was
     49    inside coverage and absent anyway. Without that comparison you have a missing data point, not a
     50    dark period.
     51 6. **Corroborate position with something that is not a transponder.** Satellite imagery over the
     52    claimed location and time, a port-call record, a photograph with a spotter's timestamp. A
     53    transponder reports what it is told to report.
     54 
     55 ## Aircraft
     56 
     57 Aircraft transmit ADS-B: identity, position, altitude, speed, on 1090 MHz. Reception is
     58 crowd-sourced, so coverage follows volunteer receivers, which follow population and wealth.
     59 
     60 | Source | Why use it |
     61 | --- | --- |
     62 | [ADS-B Exchange](https://globe.adsbexchange.com/) | **Does not honour blocking requests.** The only major aggregator that shows aircraft others hide, which is precisely why it matters for research. The map is free; the API is not. |
     63 | [Flightradar24](https://www.flightradar24.com/) | Best coverage and UI; filters blocked aircraft. Historical playback behind a subscription. |
     64 | [OpenSky Network](https://opensky-network.org/) | Research-oriented, documented free API, good historical archive. The right choice for bulk and scripted work. |
     65 | [adsb.lol](https://adsb.lol/) | Community feed with a free, keyless JSON API in the ADS-B Exchange shape. The practical replacement for scripted work now that ADSBx is paid. |
     66 | [Airframes](https://airframes.org/) | Registration-to-airframe history: owners, previous registrations, type, serial. |
     67 | [FAA Registry](https://registry.faa.gov/AircraftInquiry/Search/NNumberInquiry) | Authoritative for US N-numbers: registered owner, address, airworthiness, and the trust structures that obscure them. |
     68 | [GPSJam](https://gpsjam.org/) | Daily maps of GPS interference derived from aircraft navigation-accuracy reports. Reveals jamming zones, which is also why some tracks go wrong. |
     69 | [Live ATC](https://www.liveatc.net/) | Archived air-traffic control audio, which sometimes names aircraft that were never tracked. |
     70 
     71 ### OpenSky Network API
     72 
     73 The feed to automate against. It publishes state vectors (one position per aircraft per second,
     74 aggregated from its receiver network), per-aircraft flight lists, and full tracks, with a documented
     75 rate model rather than a scraping fight.
     76 
     77 **The auth model changed:** HTTP basic authentication with your account password is gone. Current
     78 access is OAuth2 client credentials — create an API client in your OpenSky account, then exchange
     79 the client ID and secret for a 30-minute bearer token.
     80 
     81 ```bash
     82 # one-off: get a token (expires in 30 minutes; a 401 later means refresh it)
     83 TOKEN=$(curl -s -X POST \
     84   'https://auth.opensky-network.org/auth/realms/opensky-network/protocol/openid-connect/token' \
     85   -d 'grant_type=client_credentials' \
     86   -d 'client_id=YOUR_CLIENT_ID' \
     87   --data-urlencode 'client_secret=YOUR_CLIENT_SECRET' | jq -r .access_token)
     88 ```
     89 
     90 ```bash
     91 # current state of one aircraft by ICAO 24-bit address
     92 curl -s -H "Authorization: Bearer $TOKEN" \
     93   'https://opensky-network.org/api/states/all?icao24=4ca7b5' | jq '.states'
     94 
     95 # everything in a bounding box right now — cheaper in credits than a global pull
     96 curl -s -H "Authorization: Bearer $TOKEN" \
     97   'https://opensky-network.org/api/states/all?lamin=45&lomin=5&lamax=48&lomax=10' \
     98   | jq '.states | length'
     99 
    100 # aircraft category as well as position, for sorting military from civil
    101 curl -s -H "Authorization: Bearer $TOKEN" \
    102   'https://opensky-network.org/api/states/all?icao24=4ca7b5&extended=1' | jq .
    103 
    104 # state vector at a past instant — the cheap way to answer "where was it at 14:00Z"
    105 #   1773237600 is 2026-03-11T14:00Z; `date -u -d '2026-03-11 14:00' +%s` on GNU,
    106 #   `date -u -j -f '%Y-%m-%d %H:%M' '2026-03-11 14:00' +%s` on BSD/macOS
    107 curl -s -H "Authorization: Bearer $TOKEN" \
    108   'https://opensky-network.org/api/states/all?icao24=4ca7b5&time=1773237600' | jq .
    109 
    110 # every flight this airframe flew in a two-day window (the maximum interval)
    111 curl -s -H "Authorization: Bearer $TOKEN" \
    112   'https://opensky-network.org/api/flights/aircraft?icao24=4ca7b5&begin=1740614400&end=1740787200' \
    113   | jq '.[] | {firstSeen, estDepartureAirport, lastSeen, estArrivalAirport}'
    114 
    115 # arrivals at an airport, for finding who came in on an unlisted flight
    116 curl -s -H "Authorization: Bearer $TOKEN" \
    117   'https://opensky-network.org/api/flights/arrival?airport=EGLL&begin=1740614400&end=1740700800' \
    118   | jq 'length'
    119 
    120 # the full track of one flight, including the points between airports
    121 curl -s -H "Authorization: Bearer $TOKEN" \
    122   'https://opensky-network.org/api/tracks/all?icao24=4ca7b5&time=0' | jq '.path | length'
    123 ```
    124 
    125 Free-tier limits are credit-based and daily: roughly 400 credits anonymous, 4,000 for a registered
    126 account, 8,000 if you feed data back from your own receiver with at least 30% uptime. A
    127 `/states/all` call costs 1–4 credits depending on how large the bounding box is; `/flights/*` and
    128 `/tracks/*` cost far more because they scan day partitions, so a careless historical loop burns a
    129 day's quota in a few calls. `/flights/aircraft` only serves the previous day and earlier — there is
    130 no same-day history.
    131 
    132 What it does not tell you: the hex address identifies a transponder, not an owner, and the
    133 `callsign` field is whatever the crew typed. An aircraft absent from the results was not
    134 necessarily absent from the sky — it may have been out of receiver range, or not transmitting at
    135 all.
    136 
    137 ### pyopensky
    138 
    139 The maintained Python client, worth using over hand-rolled requests because it handles the token
    140 refresh and because it also wraps OpenSky's Trino historical database, which the REST API does not
    141 expose.
    142 
    143 ```bash
    144 pip install pyopensky
    145 ```
    146 
    147 Credentials go in `settings.conf` (the library prints its location on first run) under a
    148 `[opensky]` section with `client_id` and `client_secret`.
    149 
    150 ```python
    151 from pyopensky.rest import REST
    152 from pyopensky.trino import Trino
    153 import datetime as dt
    154 
    155 rest = REST()
    156 
    157 # live state vectors in a bounding box, as a DataFrame
    158 rest.states(bounds=(45, 5, 48, 10))
    159 
    160 # the route an airline publishes for a callsign — useful when the callsign is the only clue
    161 rest.routes("RYR27LQ")
    162 
    163 # arrivals and departures at an airport over a window
    164 rest.arrival("EGLL", dt.datetime(2026, 3, 11), dt.datetime(2026, 3, 12))
    165 
    166 # historical flight list for one airframe over months, which REST cannot do
    167 db = Trino()
    168 db.flightlist(start="2026-01-01", stop="2026-03-01", icao24="4ca7b5")
    169 
    170 # the raw trajectory, for plotting a loiter pattern or an orbit
    171 db.history(start="2026-03-11 13:00", stop="2026-03-11 16:00", icao24="4ca7b5")
    172 ```
    173 
    174 Trino access is granted separately from the REST credentials and is intended for academic and
    175 research use — you apply for it, and it is not guaranteed. The REST methods work with ordinary
    176 credentials. Treat Trino as the thing that makes a months-long pattern-of-life analysis possible,
    177 and the REST API as what you use day to day.
    178 
    179 ### adsb.lol and ADS-B Exchange
    180 
    181 ADS-B Exchange retired its freemium API tier in March 2025; the current `ADSBexchange.com` RapidAPI
    182 plan starts around $10/month for 10,000 requests, and there is no free key. The map at
    183 [globe.adsbexchange.com](https://globe.adsbexchange.com/) remains free and still shows aircraft
    184 that honour-the-blocklist aggregators hide, so it stays the right place to *look*. For scripted
    185 work, [adsb.lol](https://adsb.lol/) serves the same response shape with no key at all.
    186 
    187 ```bash
    188 # one aircraft by ICAO hex — same JSON shape as the ADSBx v2 API
    189 curl -s 'https://api.adsb.lol/v2/hex/4ca7b5' | jq '.ac[] | {flight, r, t, alt_baro, gs, lat, lon}'
    190 
    191 # by callsign, when that is all a photo or an ATC recording gave you
    192 curl -s 'https://api.adsb.lol/v2/callsign/RYR27LQ' | jq '.ac | length'
    193 
    194 # by registration, to confirm a tail number is airborne now
    195 curl -s 'https://api.adsb.lol/v2/reg/EI-EFZ' | jq '.ac[0].hex'
    196 
    197 # every aircraft of a type currently tracked, for fleet questions
    198 curl -s 'https://api.adsb.lol/v2/type/B738' | jq '.total'
    199 
    200 # everything within 50 nm of a point — the query for "what was over this place"
    201 curl -s 'https://api.adsb.lol/v2/point/51.5/-0.12/50' | jq '.ac[] | {hex, flight, alt_baro}'
    202 
    203 # aircraft squawking an emergency code
    204 curl -s 'https://api.adsb.lol/v2/squawk/7700' | jq '.ac[] | {hex, flight, squawk}'
    205 
    206 # military-tagged aircraft worldwide, right now
    207 curl -s 'https://api.adsb.lol/v2/mil' | jq '.total'
    208 
    209 # LADD and PIA: aircraft using the FAA's blocking and anonymisation programmes
    210 curl -s 'https://api.adsb.lol/v2/ladd' | jq '.total'
    211 curl -s 'https://api.adsb.lol/v2/pia' | jq '.total'
    212 ```
    213 
    214 There is no documented quota, but the service rate-limits aggressively — a burst of requests starts
    215 returning HTTP 429 within seconds, so sleep between calls and cache. Both of these are live-only:
    216 neither gives you history, which is the thing that actually costs money across every ADS-B
    217 provider.
    218 
    219 The `/v2/ladd` and `/v2/pia` endpoints are worth knowing for their own sake. LADD is the FAA
    220 programme that lets an owner suppress their aircraft from public feeds; PIA assigns a temporary
    221 alternative hex address. An aircraft appearing in either list is telling you its owner asked not to
    222 be tracked, which is itself a fact about the aircraft.
    223 
    224 ### readsb and dump1090
    225 
    226 Your own receiver removes the dependency on someone else's coverage, which matters when the area
    227 you care about is a coverage hole. A software-defined radio dongle and an antenna gets you ADS-B
    228 reception out to roughly 200 nautical miles line-of-sight.
    229 
    230 `dump1090` has forked repeatedly; the maintained decoder is
    231 [wiedehopf/readsb](https://github.com/wiedehopf/readsb), with
    232 [tar1090](https://github.com/wiedehopf/tar1090) as the web interface over it. FlightAware's
    233 `dump1090-fa` is still maintained and still fine. Mutability's original `dump1090-mutability` is
    234 abandoned — do not start there.
    235 
    236 ```bash
    237 # readsb, the maintained decoder, via the project's install script
    238 sudo bash -c "$(wget -qO - https://github.com/wiedehopf/adsb-scripts/raw/master/readsb-install.sh)"
    239 
    240 # the web interface over it
    241 sudo bash -c "$(wget -qO - https://github.com/wiedehopf/tar1090/raw/master/install.sh)"
    242 
    243 # what your receiver is seeing right now, as JSON
    244 curl -s http://localhost/tar1090/data/aircraft.json | jq '.aircraft | length'
    245 
    246 # log your own feed to disk once a second — this is how you build private history
    247 while true; do
    248   curl -s http://localhost/tar1090/data/aircraft.json >> "feed-$(date -u +%F).jsonl"
    249   echo >> "feed-$(date -u +%F).jsonl"
    250   sleep 1
    251 done
    252 
    253 # receiver stats, including message rate and position accuracy
    254 curl -s http://localhost/tar1090/data/stats.json | jq '.last1min | {messages, position_rate}'
    255 
    256 # raw Mode S frames on port 30002, for anything the decoder discards
    257 nc localhost 30002 | head -20
    258 
    259 # Beast binary format on 30005, which is what you feed to aggregators
    260 nc localhost 30005 | xxd | head -5
    261 ```
    262 
    263 Running your own receiver gives you one thing no aggregator will: the raw message stream with
    264 reception timestamps, which lets you say "my receiver, which was hearing six other aircraft in that
    265 sector at that minute, heard nothing from this one". That is the comparison that turns a gap into
    266 evidence.
    267 
    268 ## Vessels
    269 
    270 Ships transmit AIS. Terrestrial receivers reach perhaps 40 nautical miles; beyond that, coverage
    271 depends on satellite AIS, which is almost entirely commercial.
    272 
    273 | Source | Why use it |
    274 | --- | --- |
    275 | [MarineTraffic](https://www.marinetraffic.com/) | The standard. Live positions, port calls, vessel particulars, photos. Historical track needs a subscription. |
    276 | [VesselFinder](https://www.vesselfinder.com/) | Good free tier; useful cross-check, and its coverage differs from MarineTraffic's in ways that matter when you are arguing about a gap. |
    277 | [Equasis](https://www.equasis.org/) | **Ownership and management history, free with registration.** The best free source for who actually controls a ship. |
    278 | [IMO GISIS](https://gisis.imo.org/) | Authoritative vessel identity, company and casualty data, free with registration. |
    279 | [Global Fishing Watch](https://globalfishingwatch.org/map) | Fishing effort and vessel-behaviour events inferred from AIS, plus radar-detected vessels that are not broadcasting. |
    280 | [IUU Vessel List](https://iuu-vessels.org/) | Vessels listed by regional fisheries bodies for illegal, unreported and unregulated fishing. |
    281 
    282 ### Equasis
    283 
    284 A free, registration-gated database funded by maritime administrations, holding the ownership and
    285 management chain for essentially every merchant ship, plus inspection history. It is the single
    286 most useful free maritime source and it has no API, so this is a walkthrough.
    287 
    288 Register at [equasis.org](https://www.equasis.org/) with a working email; approval is automatic but
    289 can take a day. Then search by IMO number, never by name.
    290 
    291 ```text
    292 Ship Info          flag, type, tonnage, build year, and the full list of PREVIOUS names
    293                    and previous flags with the dates they changed — this is the panel
    294                    that exposes a vessel renamed three times in two years
    295 Management Detail  registered owner, ISM manager, commercial manager, technical manager,
    296                    each with a company IMO number and a country, and each with the date
    297                    it took over; the registered owner is usually a one-ship shell, and
    298                    the commercial manager is usually who you actually want
    299 Ship History       the same chain backwards in time, which is what you cite when an
    300                    owner claims they sold the vessel before the incident
    301 Inspections        port state control inspections, detentions and the deficiency codes,
    302                    which place the ship in a named port on a named date independently
    303                    of AIS
    304 ```
    305 
    306 Capture for the record: the company IMO numbers, not just names, because company names repeat
    307 across jurisdictions. Then take each company IMO into
    308 [corporate registries](/sheets/osint/companies-and-finance) — the Equasis company record gives you
    309 a country and an address to start from, and nothing about beneficial ownership.
    310 
    311 Equasis is self-reported by the industry and lags reality by weeks on ownership changes. A
    312 detention record is hard fact; a current owner field is a claim.
    313 
    314 ### Global Fishing Watch API
    315 
    316 AIS-derived vessel behaviour as a queryable dataset rather than a map: identity resolution across
    317 MMSI and IMO, and coded events — encounters between two vessels, loitering, port visits, and AIS
    318 gaps. The gap detection is the part that matters outside fisheries work, because it has already
    319 done the coverage reasoning for you.
    320 
    321 Get a token at [globalfishingwatch.org/our-apis/tokens](https://globalfishingwatch.org/our-apis/tokens).
    322 Access is free and non-commercial only.
    323 
    324 ```bash
    325 TOKEN='YOUR_GFW_TOKEN'
    326 
    327 # resolve an identifier to GFW's vessel id, with flag and name history
    328 curl -s -H "Authorization: Bearer $TOKEN" -G \
    329   'https://gateway.api.globalfishingwatch.org/v3/vessels/search' \
    330   --data-urlencode 'query=9876543' \
    331   --data-urlencode 'datasets[0]=public-global-vessel-identity:latest' \
    332   | jq '.entries[] | {id:.selfReportedInfo[0].id, name:.selfReportedInfo[0].shipname, flag:.selfReportedInfo[0].flag}'
    333 
    334 # encounters: two vessels close and slow for long enough to transfer something
    335 curl -s -H "Authorization: Bearer $TOKEN" -G \
    336   'https://gateway.api.globalfishingwatch.org/v3/events' \
    337   --data-urlencode 'datasets[0]=public-global-encounters-events:latest' \
    338   --data-urlencode 'vessels[0]=VESSEL_ID' \
    339   --data-urlencode 'start-date=2026-01-01' --data-urlencode 'end-date=2026-03-01' | jq '.total'
    340 
    341 # AIS gaps — transmission stopping and resuming, with both endpoints
    342 curl -s -H "Authorization: Bearer $TOKEN" -G \
    343   'https://gateway.api.globalfishingwatch.org/v3/events' \
    344   --data-urlencode 'datasets[0]=public-global-gaps-events:latest' \
    345   --data-urlencode 'vessels[0]=VESSEL_ID' \
    346   --data-urlencode 'start-date=2026-01-01' --data-urlencode 'end-date=2026-03-01' \
    347   | jq '.entries[] | {start, end, lat:.position.lat, lon:.position.lon}'
    348 
    349 # loitering: slow movement away from port, often the other half of an encounter
    350 curl -s -H "Authorization: Bearer $TOKEN" -G \
    351   'https://gateway.api.globalfishingwatch.org/v3/events' \
    352   --data-urlencode 'datasets[0]=public-global-loitering-events:latest' \
    353   --data-urlencode 'vessels[0]=VESSEL_ID' \
    354   --data-urlencode 'start-date=2026-01-01' --data-urlencode 'end-date=2026-03-01' | jq '.total'
    355 
    356 # port visits, which date the vessel to a jurisdiction
    357 curl -s -H "Authorization: Bearer $TOKEN" -G \
    358   'https://gateway.api.globalfishingwatch.org/v3/events' \
    359   --data-urlencode 'datasets[0]=public-global-port-visits-events:latest' \
    360   --data-urlencode 'vessels[0]=VESSEL_ID' \
    361   --data-urlencode 'start-date=2026-01-01' --data-urlencode 'end-date=2026-03-01' | jq '.total'
    362 ```
    363 
    364 A GFW gap event is a reasoned inference, not a raw observation: it accounts for expected satellite
    365 coverage and known reception quality in that cell, which is exactly the work you would otherwise
    366 have to do yourself. It is still an inference — a receiver outage or an equipment failure produces
    367 the same signature as a deliberate switch-off.
    368 
    369 ## Evidencing a gap
    370 
    371 The difference between "went dark" as a finding and as a guess is whether you can show the data
    372 should have been there.
    373 
    374 ```text
    375 1. Fix the window        last position with a timestamp, first position after, both with
    376                          the reporting source named
    377 2. Establish coverage    other aircraft or vessels reported in the same cell during the
    378                          same window, from the same feed. If nothing else reported either,
    379                          you have a coverage hole, not a dark period
    380 3. Cross-feed            check a second, independent network: adsb.lol against OpenSky,
    381                          VesselFinder against MarineTraffic. Independent receiver
    382                          populations failing identically is coverage; one seeing traffic
    383                          while the other does not is a reception artefact
    384 4. Check the physics     the distance between last and first position divided by the gap
    385                          duration gives an implied speed. An implied 60 knots for a bulk
    386                          carrier means the track is wrong, not that the ship was fast
    387 5. Look for the reason   GPSJam for the aircraft case, since jamming degrades position
    388                          quality before it removes it; a port call or an AIS equipment
    389                          deficiency in the Equasis inspection record for the vessel case
    390 6. Corroborate           satellite imagery over the implied position during the gap.
    391                          A radar or optical detection of a hull where AIS says nothing
    392                          is the strongest version of this finding
    393 ```
    394 
    395 The three mechanisms to distinguish between: **transponder off**, where transmission simply stops
    396 and resumes, usually near a transfer or a sanctioned port; **MMSI spoofing**, where the identifier
    397 is set to another vessel's or an invalid one, detectable as two vessels with one MMSI in different
    398 oceans, or an MMSI whose country prefix contradicts the claimed flag; and **position spoofing**,
    399 where the track is plausible but false, detectable as an implied speed the hull cannot do, a track
    400 that crosses land, or imagery showing the berth empty.
    401 
    402 ## Rail and road
    403 
    404 - [OpenRailwayMap](https://www.openrailwaymap.org/) — global rail infrastructure, including
    405   electrification, gauge and signalling, which constrains what can run on a line.
    406 - [Chronotrains](https://www.chronotrains.com/) — how far you can travel by train in N hours,
    407   useful for bounding where someone could have been.
    408 - [License Plate Maps](https://www.licenseplatemania.com/) — plate formats by country, for
    409   narrowing a location from a vehicle in a photograph.
    410 - [VIN Decoder (NHTSA)](https://vpic.nhtsa.dot.gov/decoder/) — free US government VIN decode, with
    411   a JSON API: `curl -s 'https://vpic.nhtsa.dot.gov/api/vehicles/decodevin/1HGCM82633A004352?format=json'`
    412 
    413 ## Tool reference
    414 
    415 | Tool | What it does | Cost |
    416 | --- | --- | --- |
    417 | [Aviation Safety Network](https://aviation-safety.net/) | Aircraft incident database; can be searched by country, registration, year, etc. | free |
    418 | [Chronotrains](https://www.chronotrains.com/en) | Chronotrains is a free interactive map designed to explore the reach of Europe’s extensive rail network. Enter a starting point and travel time to see… | free |
    419 | [Federal Aviation Administration](https://registry.faa.gov/AircraftInquiry/Search/NNumberInquiry) | Nationwide Plane Registry. Search by N-Number (a.k.a. callsign). Comprehensive list of privately owned planes in the US. | free |
    420 | [FlightAware](https://www.flightaware.com/live/) | FlightAware is a global flight-tracking platform that provides real-time data on aircraft movements. It offers live tracking, historical data, and… | partly free |
    421 | [Illegal, unreported, unregular fishing Vessels List](https://iuu-vessels.org/Home/Search) | A combined list of known illegal, unreported, unregular fishing vessels | free |
    422 | [OpenRailwayMap](https://wiki.openstreetmap.org/wiki/OpenRailwayMap) | a detailed online map of the world's railway infrastructure | free |
    423 | [OpenSky-Network](http://opensky-network.org/) | Community, open source flight tracking network. | free |
    424 | [ShipFinder](https://shipfinder.co/) | ShipFinder is an application designed to track vessels in near real-time across the globe, available on iPhone and Android platforms (but no longer PC) | partly free |
    425 | License Plate Maps | Collection of tools and maps for discerning license plates by country | free |
    426 
    427 ## Pitfalls
    428 
    429 - **Coverage gaps read as events.** Most disappearances are receiver coverage, not evasion. Check
    430   the coverage map before claiming a vessel went dark.
    431 - **Callsigns and names are not identities.** Track on ICAO hex and IMO number.
    432 - **Historical data usually costs money**, and free sites silently retain only days. Capture what
    433   you need when you see it.
    434 - **Flags of convenience** mean the flag state tells you little about real ownership. Use Equasis.
    435 
    436 ## Worked example
    437 
    438 You have one datum: the tail number `N721AB`, from a photograph of an aircraft on a stand. The
    439 registration, hex and outputs below are illustrative — the sequence of pivots is the point.
    440 
    441 ```bash
    442 # 1. registration to hex, from a live feed — the hex is what you track on
    443 curl -s 'https://api.adsb.lol/v2/reg/N721AB' | jq '.ac[0] | {hex, t, flight, alt_baro}'
    444 # {"hex":"a8a2b5","t":"GLF6","flight":null,"alt_baro":"ground"}
    445 ```
    446 
    447 The airframe is a Gulfstream G650ER and it is on the ground somewhere right now. The hex `a8a2b5`
    448 is the identifier for everything that follows.
    449 
    450 ```bash
    451 # 2. registered owner, from the FAA registry — not the operator
    452 #    registry.faa.gov/AircraftInquiry/Search/NNumberInquiry, N721AB
    453 # -> registered to a Delaware LLC, c/o a Nevada registered agent
    454 ```
    455 
    456 A single-purpose LLC behind a registered agent is the normal arrangement for a private jet, so the
    457 registry gives you a company name, not a person. That name goes to
    458 [corporate records](/sheets/osint/companies-and-finance).
    459 
    460 ```bash
    461 # 3. the movement history, from OpenSky (begin/end are 2025-02-27 to 03-01 —
    462 #    two days is the maximum interval /flights/aircraft accepts)
    463 TOKEN=$(curl -s -X POST \
    464   'https://auth.opensky-network.org/auth/realms/opensky-network/protocol/openid-connect/token' \
    465   -d 'grant_type=client_credentials' -d 'client_id=ID' \
    466   --data-urlencode 'client_secret=SECRET' | jq -r .access_token)
    467 
    468 curl -s -H "Authorization: Bearer $TOKEN" \
    469   'https://opensky-network.org/api/flights/aircraft?icao24=a8a2b5&begin=1740614400&end=1740787200' \
    470   | jq '.[] | {dep:.estDepartureAirport, arr:.estArrivalAirport, off:.firstSeen, on:.lastSeen}'
    471 # three legs across the window; the second shows estDepartureAirport "KVNY"
    472 # and estArrivalAirport null
    473 ```
    474 
    475 A leg with a departure and no arrival is the pivot. Either the aircraft left coverage, or the
    476 transponder stopped.
    477 
    478 ```bash
    479 # 4. the raw track for that leg, to find where the positions stop
    480 curl -s -H "Authorization: Bearer $TOKEN" \
    481   'https://opensky-network.org/api/tracks/all?icao24=a8a2b5&time=1740671000' \
    482   | jq '.path[-3:]'
    483 # last point 31.2N 118.4W at FL410, over open water south-west of San Diego
    484 ```
    485 
    486 ```bash
    487 # 5. was that a coverage hole? ask what else reported in the same cell
    488 curl -s -H "Authorization: Bearer $TOKEN" \
    489   'https://opensky-network.org/api/states/all?time=1740671400&lamin=30&lomin=-120&lamax=33&lomax=-116' \
    490   | jq '[.states[] | {icao:.[0], alt:.[13]}] | length'
    491 # 0 — nothing at all reported in that box at that minute
    492 ```
    493 
    494 Zero other aircraft in a box that size over a busy approach corridor is a coverage answer, not an
    495 evasion answer: the aircraft flew out of the receiver network's reach over the Pacific. Checking
    496 [adsb.lol](https://adsb.lol/) for the same window returns nothing either, from an independent
    497 receiver population — which confirms coverage rather than contradicting it.
    498 
    499 What you can assert: a type, a hex, a registered owner to take into company records, three
    500 dated legs, and a documented reason to *not* report a dark period. What it does not give you is who
    501 was on board, which no transponder feed ever will — that needs a stand photograph with a timestamp,
    502 a flight-plan filing, or a planespotter's log, and the second independent source is what would make
    503 it defensible.
    504 
    505 What would falsify it: another receiver network showing continued positions through the alleged
    506 coverage gap, the registration-to-hex mapping changing before the flight, or the OpenSky track
    507 belonging to a reused callsign rather than hex `a8a2b5`. The no-evasion conclusion rests on the
    508 empty comparison cell across independent receiver populations, not on the missing arrival alone.
    509 
    510 ## Broader catalogues
    511 
    512 - [Transport OSINT](https://tools.osintnewsletter.com/tool-categories/transport-osint)
    513 
    514 
    515 ## More tools
    516 
    517 Further tools for this area from the OSINT Newsletter Tools Library ([Transport OSINT](https://tools.osintnewsletter.com/tool-categories/transport-osint)), excluding those already listed above.
    518 
    519 | Tool | What it does |
    520 | --- | --- |
    521 | [Air Cargo Tracking & News](https://www.utopiax.org/#google_vignette) | A free logistics portal that aggregates air cargo tracking, container tracking, parcel tracking, sailing schedules, logistics… |
    522 | [autoDNA](https://www.autodna.com/) | A vehicle history checking platform that uses a vehicle's VIN (Vehicle Identification Number) to uncover information about its… |
    523 | [Car Detective](https://www.cardetective.com/) | A vehicle intelligence platform providing access to vehicle history, registration information, and provenance data. |
    524 | [CITES Trade Database](https://trade.cites.org/) | An official database of international trade in species protected under the Convention on International Trade in Endangered… |
    525 | [Combat Aircraft](https://www.combataircraft.com/) | Military aviation reference database providing aircraft specifications, operators, imagery, aviation news, and background… |
    526 | [Container Tracking](http://container-tracking.org/) | A web-based tool for tracking shipping containers using container numbers, providing information on vessel movements, shipment… |
    527 | [Global ADS-B Exchange](https://globe.adsbexchange.com/) | Track aircraft movements worldwide in near real time using crowdsourced ADS-B signals. |
    528 | [Global Suppliers Online](https://www.globalsuppliersonline.com/) | A business directory that connects buyers with manufacturers, exporters, wholesalers and suppliers worldwide. |
    529 | [Import Yeti](https://www.importyeti.com/) | A supply chain intelligence tool that helps users discover who imports products into the United States and which overseas… |
    530 | [Live Train Tracker](https://mobility.portal.geops.io/world.geops.transit) | Interactive global public-transport map showing scheduled and, where available, real-time positions of trains and other… |
    531 | [LiveATC](https://www.liveatc.net/) | A live air traffic communications platform that streams air traffic control (ATC) radio transmissions from airports around the… |
    532 | [Maersk Tracking](https://www.maersk.com/tracking/#tracking) | A global cargo tracking platform providing visibility of Maersk ocean and air shipments, including container movements, shipment… |
    533 | [Maritime Database](https://maritime-database.com/) | Global maritime reference and vessel-tracking database providing vessel, port, and maritime-company information. |
    534 | [Open Infrastructure Map](https://openinframap.org/) | An interactive mapping platform that visualises critical infrastructure worldwide using data primarily sourced from OpenStreetMap. |
    535 | [OSINT Tools Map](https://cybdetective.com/osintmap/) | An interactive directory of country-specific OSINT resources, including phonebooks, cadastral maps, business registers, court… |
    536 | [Track-Trace](https://www.track-trace.com/) | An online tracking and lookup service that helps users trace parcels and shipments using tracking numbers. |
    537 | [Vehicle AI](https://vehicle-ai.net/) | AI-powered tool for identifying, analysing, and enriching vehicle-related data. |
    538 | [VIN Decoder from NHTSA](https://vpic.nhtsa.dot.gov/decoder/) | Official U.S. government tool that breaks down a vehicle’s VIN to reveal key details like manufacturer, model, engine type, and… |
    539 
    540 ## Sources
    541 
    542 Both catalogues below are maintained by other people and are considerably larger than
    543 this page. Use them as the canonical index; this sheet is a working route through them.
    544 
    545 - [Bellingcat's Online Investigation Toolkit](https://bellingcat.gitbook.io/toolkit) — ~340 tools, each with its own
    546   review page covering cost, difficulty, requirements and limitations.
    547 - [OSINT Newsletter Tools Library](https://tools.osintnewsletter.com) — ~280 tools, organised by investigative goal.
    548 
    549 Neither publishes a licence, so nothing here is copied from them: tool names, one-line
    550 descriptions, cost flags and links are catalogue facts, and the method and commentary are
    551 this site's own. See [credits](/credits).