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).